From f44e662dbe19fac35d8cbd9d8fa09d8d3682664a Mon Sep 17 00:00:00 2001 From: Thomas Kerin <thomas.kerin@bitmaintech.com> Date: Sun, 4 Mar 2018 20:48:55 +0100 Subject: [PATCH 001/568] allow custom bip32 prefixes, and varying types of addresses returned get hdnode.getAddress --- src/hdnode.js | 56 ++++++++++++++------ src/keytoscript.js | 124 +++++++++++++++++++++++++++++++++++++++++++++ src/networks.js | 23 +++++++-- test/hdaddress.js | 95 ++++++++++++++++++++++++++++++++++ test/hdnode.js | 13 +++-- 5 files changed, 286 insertions(+), 25 deletions(-) create mode 100644 src/keytoscript.js create mode 100644 test/hdaddress.js diff --git a/src/hdnode.js b/src/hdnode.js index a96b20c..8f98846 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -1,6 +1,7 @@ var Buffer = require('safe-buffer').Buffer var base58check = require('bs58check') var bcrypto = require('./crypto') +var baddress = require('./address') var createHmac = require('create-hmac') var typeforce = require('typeforce') var types = require('./types') @@ -9,12 +10,15 @@ var NETWORKS = require('./networks') var BigInteger = require('bigi') var ECPair = require('./ecpair') +// var KeyToScript = require('./keytoscript') var ecurve = require('ecurve') var curve = ecurve.getCurveByName('secp256k1') -function HDNode (keyPair, chainCode) { +function HDNode (keyPair, chainCode, prefix) { typeforce(types.tuple('ECPair', types.Buffer256bit), arguments) - + if (!prefix) { + prefix = keyPair.network.bip32 + } if (!keyPair.compressed) throw new TypeError('BIP32 only allows compressed keyPairs') this.keyPair = keyPair @@ -22,13 +26,14 @@ function HDNode (keyPair, chainCode) { this.depth = 0 this.index = 0 this.parentFingerprint = 0x00000000 + this.prefix = prefix } HDNode.HIGHEST_BIT = 0x80000000 HDNode.LENGTH = 78 HDNode.MASTER_SECRET = Buffer.from('Bitcoin seed', 'utf8') -HDNode.fromSeedBuffer = function (seed, network) { +HDNode.fromSeedBuffer = function (seed, network, prefix) { typeforce(types.tuple(types.Buffer, types.maybe(types.Network)), arguments) if (seed.length < 16) throw new TypeError('Seed should be at least 128 bits') @@ -45,14 +50,21 @@ HDNode.fromSeedBuffer = function (seed, network) { network: network }) - return new HDNode(keyPair, IR) + return new HDNode(keyPair, IR, prefix) } -HDNode.fromSeedHex = function (hex, network) { - return HDNode.fromSeedBuffer(Buffer.from(hex, 'hex'), network) +HDNode.fromSeedHex = function (hex, network, prefix) { + return HDNode.fromSeedBuffer(Buffer.from(hex, 'hex'), network, prefix) } -HDNode.fromBase58 = function (string, networks) { +/** + * + * @param {string} string + * @param {Array<network>|network} networks + * @param {Array<prefix>} prefixes - only used if networks is object/undefined(so bitcoin). + * @returns {HDNode} + */ +HDNode.fromBase58 = function (string, networks, prefixes) { var buffer = base58check.decode(string) if (buffer.length !== 78) throw new Error('Invalid buffer length') @@ -61,6 +73,7 @@ HDNode.fromBase58 = function (string, networks) { var network // list of networks? + var prefix if (Array.isArray(networks)) { network = networks.filter(function (x) { return version === x.bip32.private || @@ -69,13 +82,26 @@ HDNode.fromBase58 = function (string, networks) { if (!network) throw new Error('Unknown network version') + // we found a network by it's bip32 prefixes, use that + prefix = network.bip32 + // otherwise, assume a network object (or default to bitcoin) } else { network = networks || NETWORKS.bitcoin + if (prefixes) { + prefix = prefixes.filter(function (x) { + return version === x.private || + version === x.public + }).pop() + } else { + // no special prefixes to consider, use networks bip32 prefix + prefix = network.bip32 + } } - if (version !== network.bip32.private && - version !== network.bip32.public) throw new Error('Invalid network version') + // sanity check the version against the prefix + if (version !== prefix.private && + version !== prefix.public) throw new Error('Invalid network version') // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ... var depth = buffer[4] @@ -114,7 +140,7 @@ HDNode.fromBase58 = function (string, networks) { keyPair = new ECPair(null, Q, { network: network }) } - var hd = new HDNode(keyPair, chainCode) + var hd = new HDNode(keyPair, chainCode, prefix) hd.depth = depth hd.index = index hd.parentFingerprint = parentFingerprint @@ -123,7 +149,8 @@ HDNode.fromBase58 = function (string, networks) { } HDNode.prototype.getAddress = function () { - return this.keyPair.getAddress() + var data = this.prefix.scriptFactory.convert(this.keyPair) + return baddress.fromOutputScript(data.scriptPubKey, this.keyPair.network) } HDNode.prototype.getIdentifier = function () { @@ -147,7 +174,7 @@ HDNode.prototype.neutered = function () { network: this.keyPair.network }) - var neutered = new HDNode(neuteredKeyPair, this.chainCode) + var neutered = new HDNode(neuteredKeyPair, this.chainCode, this.prefix) neutered.depth = this.depth neutered.index = this.index neutered.parentFingerprint = this.parentFingerprint @@ -167,8 +194,7 @@ HDNode.prototype.toBase58 = function (__isPrivate) { if (__isPrivate !== undefined) throw new TypeError('Unsupported argument in 2.0.0') // Version - var network = this.keyPair.network - var version = (!this.isNeutered()) ? network.bip32.private : network.bip32.public + var version = (!this.isNeutered()) ? this.prefix.private : this.prefix.public var buffer = Buffer.allocUnsafe(78) // 4 bytes: version bytes @@ -268,7 +294,7 @@ HDNode.prototype.derive = function (index) { }) } - var hd = new HDNode(derivedKeyPair, IR) + var hd = new HDNode(derivedKeyPair, IR, this.prefix) hd.depth = this.depth + 1 hd.index = index hd.parentFingerprint = this.getFingerprint().readUInt32BE(0) diff --git a/src/keytoscript.js b/src/keytoscript.js new file mode 100644 index 0000000..c16272d --- /dev/null +++ b/src/keytoscript.js @@ -0,0 +1,124 @@ +var bcrypto = require('./crypto') +var btemplates = require('./templates') + +function checkAllowedP2sh (keyFactory) { + if (!(keyFactory instanceof P2pkhFactory || + keyFactory instanceof P2wpkhFactory || + keyFactory instanceof P2pkFactory + )) { + throw new Error('Unsupported script factory for P2SH') + } +} + +function checkAllowedP2wsh (keyFactory) { + if (!(keyFactory instanceof P2pkhFactory || + keyFactory instanceof P2pkFactory + )) { + throw new Error('Unsupported script factory for P2SH') + } +} + +var P2pkFactory = function () { + +} + +/** + * @param {bitcoin.ECPair} key + */ +P2pkFactory.prototype.convert = function (key) { + return { + scriptPubKey: btemplates.pubKey.output.encode(key.getPublicKeyBuffer()), + signData: {} + } +} + +var P2pkhFactory = function () { + +} + +/** + * @param {bitcoin.ECPair} key + */ +P2pkhFactory.prototype.convert = function (key) { + var hash160 = bcrypto.hash160(key.getPublicKeyBuffer()) + return { + scriptPubKey: btemplates.pubKeyHash.output.encode(hash160), + signData: {} + } +} + +var P2wpkhFactory = function () { + +} + +/** + * @param {bitcoin.ECPair} key + */ +P2wpkhFactory.prototype.convert = function (key) { + var hash160 = bcrypto.hash160(key.getPublicKeyBuffer()) + return { + scriptPubKey: btemplates.witnessPubKeyHash.output.encode(hash160), + signData: {} + } +} + +var P2shFactory = function (keyFactory) { + checkAllowedP2sh(keyFactory) + this.factory = keyFactory +} + +P2shFactory.prototype.convert = function (key) { + var detail = this.factory.convert(key) + var hash160 = bcrypto.hash160(detail.scriptPubKey) + return { + scriptPubKey: btemplates.scriptHash.output.encode(hash160), + signData: { + redeemScript: detail.scriptPubKey + } + } +} + +var P2wshFactory = function (keyFactory) { + checkAllowedP2wsh(keyFactory) + this.factory = keyFactory +} + +P2wshFactory.prototype.convert = function (key) { + var detail = this.factory.convert(key) + var hash160 = bcrypto.hash160(detail.scriptPubKey) + return { + scriptPubKey: btemplates.scriptHash.output.encode(hash160), + signData: { + redeemScript: detail.scriptPubKey + } + } +} + +var P2shP2wshFactory = function (keyFactory) { + checkAllowedP2wsh(keyFactory) + this.factory = keyFactory +} + +P2shP2wshFactory.prototype.convert = function (key) { + var detail = this.factory.convert(key) + var sha256 = bcrypto.sha256(detail.scriptPubKey) + var wp = btemplates.witnessScriptHash.output.encode(sha256) + var hash160 = bcrypto.hash160(wp) + var spk = btemplates.scriptHash.output.encode(hash160) + return { + scriptPubKey: spk, + signData: { + redeemScript: wp, + witnessScript: detail.scriptPubKey + } + } +} + +module.exports = { + P2pkhFactory: P2pkhFactory, + P2wpkhFactory: P2wpkhFactory, + P2pkFactory: P2pkFactory, + P2shFactory: P2shFactory, + P2wshFactory: P2wshFactory, + P2shP2wshFactory: P2shP2wshFactory +} diff --git a/src/networks.js b/src/networks.js index 43cd5fc..bfef034 100644 --- a/src/networks.js +++ b/src/networks.js @@ -1,13 +1,28 @@ +var KeyToScript = require('./keytoscript') // https://en.bitcoin.it/wiki/List_of_address_prefixes // Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731 +var p2pkh = new KeyToScript.P2pkhFactory() +var p2wpkh = new KeyToScript.P2wpkhFactory() + module.exports = { bitcoin: { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'bc', bip32: { public: 0x0488b21e, - private: 0x0488ade4 + private: 0x0488ade4, + scriptFactory: p2pkh + }, + bip49: { + private: 0x049d7878, + public: 0x049d7cb2, + scriptFactory: new KeyToScript.P2shFactory(p2wpkh) + }, + bip84: { + private: 0x04b2430c, + public: 0x04b24746, + scriptFactory: p2wpkh }, pubKeyHash: 0x00, scriptHash: 0x05, @@ -18,7 +33,8 @@ module.exports = { bech32: 'tb', bip32: { public: 0x043587cf, - private: 0x04358394 + private: 0x04358394, + scriptFactory: p2pkh }, pubKeyHash: 0x6f, scriptHash: 0xc4, @@ -28,7 +44,8 @@ module.exports = { messagePrefix: '\x19Litecoin Signed Message:\n', bip32: { public: 0x019da462, - private: 0x019d9cfe + private: 0x019d9cfe, + scriptFactory: p2pkh }, pubKeyHash: 0x30, scriptHash: 0x32, diff --git a/test/hdaddress.js b/test/hdaddress.js new file mode 100644 index 0000000..59b84ab --- /dev/null +++ b/test/hdaddress.js @@ -0,0 +1,95 @@ +/* global describe, it */ +/* eslint-disable no-new */ + +var assert = require('assert') +var HDNode = require('../src/hdnode') +var bnetwork = require('../src/networks') + +describe('bip44', function () { + it('works without a ScriptFactory', function () { + // mnemonic: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about + var seed = '5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4' + var network = bnetwork.bitcoin + + var root = HDNode.fromSeedHex(seed, network) + assert.equal( + root.toBase58(), + 'xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu' + ) + + var account = root.derivePath('44\'/0\'/0\'') + assert.equal( + account.toBase58(), + 'xprv9xpXFhFpqdQK3TmytPBqXtGSwS3DLjojFhTGht8gwAAii8py5X6pxeBnQ6ehJiyJ6nDjWGJfZ95WxByFXVkDxHXrqu53WCRGypk2ttuqncb' + ) + assert.equal( + 'xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj', + account.neutered().toBase58() + ) + + var key = account.derivePath('0/0') + assert.equal('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', key.getAddress()) + }) + + it('works with a prefix', function () { + // mnemonic: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about + var seed = '5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4' + var network = bnetwork.bitcoin + + var root = HDNode.fromSeedHex(seed, network, network.bip32) + var account = root.derivePath('44\'/0\'/0\'') + assert.equal( + account.toBase58(), + 'xprv9xpXFhFpqdQK3TmytPBqXtGSwS3DLjojFhTGht8gwAAii8py5X6pxeBnQ6ehJiyJ6nDjWGJfZ95WxByFXVkDxHXrqu53WCRGypk2ttuqncb' + ) + assert.equal( + 'xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj', + account.neutered().toBase58() + ) + + var key = account.derivePath('0/0') + assert.equal('1LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA', key.getAddress()) + }) +}) + +describe('bip49', function () { + it('serialization and address', function () { + // mnemonic: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about + var seed = '5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4' + var network = bnetwork.bitcoin + var root = HDNode.fromSeedHex(seed, network, network.bip49) + var account = root.derivePath('49\'/0\'/0\'') + assert.equal( + account.toBase58(), + 'yprvAHwhK6RbpuS3dgCYHM5jc2ZvEKd7Bi61u9FVhYMpgMSuZS613T1xxQeKTffhrHY79hZ5PsskBjcc6C2V7DrnsMsNaGDaWev3GLRQRgV7hxF' + ) + assert.equal( + 'ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP', + account.neutered().toBase58() + ) + + var key = account.derivePath('0/0') + assert.equal('37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf', key.getAddress()) + }) +}) + +describe('bip84', function () { + it('serialization and address', function () { + // mnemonic: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about + var seed = '5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4' + var network = bnetwork.bitcoin + var root = HDNode.fromSeedHex(seed, network, network.bip84) + var account = root.derivePath('84\'/0\'/0\'') + assert.equal( + account.toBase58(), + 'zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE' + ) + assert.equal( + 'zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs', + account.neutered().toBase58() + ) + + var key = account.derivePath('0/0') + assert.equal('bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu', key.getAddress()) + }) +}) diff --git a/test/hdnode.js b/test/hdnode.js index 944dded..23cd042 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -50,7 +50,6 @@ describe('HDNode', function () { it('has a default depth/index of 0', function () { var hd = new HDNode(keyPair, chainCode) - assert.strictEqual(hd.depth, 0) assert.strictEqual(hd.index, 0) }) @@ -124,12 +123,12 @@ describe('HDNode', function () { }) describe('getAddress', function () { - it('wraps keyPair.getAddress', setupTest(function () { - this.mock(keyPair).expects('getAddress') - .once().withArgs().returns('foobar') - - assert.strictEqual(hd.getAddress(), 'foobar') - })) + // it('wraps keyPair.getAddress', setupTest(function () { + // this.mock(keyPair).expects('getAddress') + // .once().withArgs().returns('foobar') + // + // assert.strictEqual(hd.getAddress(), 'foobar') + // })) }) describe('getNetwork', function () { From 84e330df3dd153cb0a4d8626b5e65b382af37be4 Mon Sep 17 00:00:00 2001 From: Thomas Kerin <thomas.kerin@bitmaintech.com> Date: Sun, 4 Mar 2018 21:08:23 +0100 Subject: [PATCH 002/568] hdnode: expose getScriptData --- src/hdnode.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/hdnode.js b/src/hdnode.js index 8f98846..5ab8ffe 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -148,9 +148,13 @@ HDNode.fromBase58 = function (string, networks, prefixes) { return hd } +HDNode.prototype.getScriptData = function () { + return this.prefix.scriptFactory.convert(this.keyPair) +} + HDNode.prototype.getAddress = function () { - var data = this.prefix.scriptFactory.convert(this.keyPair) - return baddress.fromOutputScript(data.scriptPubKey, this.keyPair.network) + var scriptData = this.getScriptData() + return baddress.fromOutputScript(scriptData.scriptPubKey, this.keyPair.network) } HDNode.prototype.getIdentifier = function () { From 7c9c6ed20454e55ec49d0b6e4a43bf5ee394c951 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 20 Mar 2018 13:19:39 +1100 Subject: [PATCH 003/568] rm sinon, sinon-test --- package.json | 2 -- test/ecdsa.js | 40 ++++++++++++------------------ test/ecpair.js | 60 +++++++++++++++++++++++++++------------------ test/hdnode.js | 66 ++++++++++++++++++++++++++++---------------------- test/mockme.js | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 78 deletions(-) create mode 100644 test/mockme.js diff --git a/package.json b/package.json index 5594b35..7250f63 100644 --- a/package.json +++ b/package.json @@ -55,8 +55,6 @@ "mocha": "^5.0.1", "nyc": "^11.4.1", "proxyquire": "^1.4.0", - "sinon": "^4.3.0", - "sinon-test": "^2.1.3", "standard": "^9.0.2" }, "license": "MIT" diff --git a/test/ecdsa.js b/test/ecdsa.js index 8d8b17c..f93beb9 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -3,9 +3,7 @@ var assert = require('assert') var bcrypto = require('../src/crypto') var ecdsa = require('../src/ecdsa') -var sinon = require('sinon') -var sinonTest = require('sinon-test') -var setupTest = sinonTest(sinon) +var mockme = require('./mockme') var BigInteger = require('bigi') var ECSignature = require('../src/ecsignature') @@ -30,12 +28,13 @@ describe('ecdsa', function () { }) }) - it('loops until an appropriate k value is found', setupTest(function () { - this.mock(BigInteger).expects('fromBuffer') - .exactly(3) - .onCall(0).returns(new BigInteger('0')) // < 1 - .onCall(1).returns(curve.n) // > n-1 - .onCall(2).returns(new BigInteger('42')) // valid + it('loops until an appropriate k value is found', mockme(function () { + this.mock(BigInteger, 'fromBuffer', function f (b) { + assert.strictEqual(b.length, 32) + if (f.calls === 0) return BigInteger.ZERO // < 1 + if (f.calls === 1) return curve.n // > n - 1 + if (f.calls === 2) return new BigInteger('42') // valid + }, 3) var x = new BigInteger('1').toBuffer(32) var h1 = Buffer.alloc(32) @@ -44,24 +43,17 @@ describe('ecdsa', function () { assert.strictEqual(k.toString(), '42') })) - it('loops until a suitable signature is found', setupTest(function () { - this.mock(BigInteger).expects('fromBuffer') - .exactly(4) - .onCall(0).returns(new BigInteger('0')) // < 1 - .onCall(1).returns(curve.n) // > n-1 - .onCall(2).returns(new BigInteger('42')) // valid, but 'bad' signature - .onCall(3).returns(new BigInteger('53')) // valid, good signature + it('loops until a suitable signature is found', mockme(function () { + var checkSigStub = this.stub(function f () { + if (f.calls === 0) return false // bad signature + if (f.calls === 1) return true // good signature + }, 2) - var mockCheckSig = this.mock() - mockCheckSig.exactly(2) - mockCheckSig.onCall(0).returns(false) // bad signature - mockCheckSig.onCall(1).returns(true) // good signature - - var x = new BigInteger('1').toBuffer(32) + var x = BigInteger.ONE.toBuffer(32) var h1 = Buffer.alloc(32) - var k = ecdsa.deterministicGenerateK(h1, x, mockCheckSig) + var k = ecdsa.deterministicGenerateK(h1, x, checkSigStub) - assert.strictEqual(k.toString(), '53') + assert.strictEqual(k.toHex(), 'a9b1a1a84a4c2f96b6158ed7a81404c50cb74373c22e8d9e02d0411d719acae2') })) fixtures.valid.rfc6979.forEach(function (f) { diff --git a/test/ecpair.js b/test/ecpair.js index 7972585..342b166 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -5,9 +5,7 @@ var assert = require('assert') var ecdsa = require('../src/ecdsa') var ecurve = require('ecurve') var proxyquire = require('proxyquire') -var sinon = require('sinon') -var sinonTest = require('sinon-test') -var setupTest = sinonTest(sinon) +var mockme = require('./mockme') var BigInteger = require('bigi') var ECPair = require('../src/ecpair') @@ -76,9 +74,10 @@ describe('ECPair', function () { keyPair = new ECPair(BigInteger.ONE) }) - it('wraps Q.getEncoded', setupTest(function () { - this.mock(keyPair.Q).expects('getEncoded') - .once().withArgs(keyPair.compressed) + it('wraps Q.getEncoded', mockme(function () { + this.mock(keyPair.Q, 'getEncoded', function (compressed) { + assert.strictEqual(compressed, keyPair.compressed) + }, 1) keyPair.getPublicKeyBuffer() })) @@ -167,21 +166,31 @@ describe('ECPair', function () { assert.strictEqual(keyPair.network, NETWORKS.testnet) }) - it('loops until d is within interval [1, n - 1] : 1', setupTest(function () { - var rng = this.mock() - rng.exactly(2) - rng.onCall(0).returns(BigInteger.ZERO.toBuffer(32)) // invalid length - rng.onCall(1).returns(BigInteger.ONE.toBuffer(32)) // === 1 + it('throws if d is bad length', function () { + function rng () { + return BigInteger.ZERO.toBuffer(28) + } + + assert.throws(function () { + ECPair.makeRandom({ rng: rng }) + }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/) + }) + + it('loops until d is within interval [1, n - 1] : 1', mockme(function () { + var rng = this.stub(function f () { + if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // 0 + return BigInteger.ONE.toBuffer(32) // >0 + }, 2) ECPair.makeRandom({ rng: rng }) })) - it('loops until d is within interval [1, n - 1] : n - 1', setupTest(function () { - var rng = this.mock() - rng.exactly(3) - rng.onCall(0).returns(BigInteger.ZERO.toBuffer(32)) // < 1 - rng.onCall(1).returns(curve.n.toBuffer(32)) // > n-1 - rng.onCall(2).returns(curve.n.subtract(BigInteger.ONE).toBuffer(32)) // === n-1 + it('loops until d is within interval [1, n - 1] : n - 1', mockme(function () { + var rng = this.stub(function f () { + if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // <1 + if (f.calls === 1) return curve.n.toBuffer(32) // >n-1 + return curve.n.subtract(BigInteger.ONE).toBuffer(32) // n-1 + }, 3) ECPair.makeRandom({ rng: rng }) })) @@ -217,9 +226,11 @@ describe('ECPair', function () { }) describe('signing', function () { - it('wraps ecdsa.sign', setupTest(function () { - this.mock(ecdsa).expects('sign') - .once().withArgs(hash, keyPair.d) + it('wraps ecdsa.sign', mockme(function () { + this.mock(ecdsa, 'sign', function (h, d) { + assert.strictEqual(h, hash) + assert.strictEqual(d, keyPair.d) + }, 1) keyPair.sign(hash) })) @@ -240,9 +251,12 @@ describe('ECPair', function () { signature = keyPair.sign(hash) }) - it('wraps ecdsa.verify', setupTest(function () { - this.mock(ecdsa).expects('verify') - .once().withArgs(hash, signature, keyPair.Q) + it('wraps ecdsa.verify', mockme(function () { + this.mock(ecdsa, 'verify', function (h, s, q) { + assert.strictEqual(h, hash) + assert.strictEqual(s, signature) + assert.strictEqual(q, keyPair.Q) + }, 1) keyPair.verify(hash, signature) })) diff --git a/test/hdnode.js b/test/hdnode.js index 944dded..b5321a5 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -3,9 +3,7 @@ var assert = require('assert') var ecdsa = require('../src/ecdsa') -var sinon = require('sinon') -var sinonTest = require('sinon-test') -var setupTest = sinonTest(sinon) +var mockme = require('./mockme') var BigInteger = require('bigi') var ECPair = require('../src/ecpair') @@ -81,18 +79,20 @@ describe('HDNode', function () { }) }) - it('throws if IL is not within interval [1, n - 1] | IL === 0', setupTest(function () { - this.mock(BigInteger).expects('fromBuffer') - .once().returns(BigInteger.ZERO) + it('throws if IL is not within interval [1, n - 1] | IL === 0', mockme(function () { + this.mock(BigInteger, 'fromBuffer', function () { + return BigInteger.ZERO + }, 1) assert.throws(function () { HDNode.fromSeedHex('ffffffffffffffffffffffffffffffff') }, /Private key must be greater than 0/) })) - it('throws if IL is not within interval [1, n - 1] | IL === n', setupTest(function () { - this.mock(BigInteger).expects('fromBuffer') - .once().returns(curve.n) + it('throws if IL is not within interval [1, n - 1] | IL === n', mockme(function () { + this.mock(BigInteger, 'fromBuffer', function () { + return curve.n + }, 1) assert.throws(function () { HDNode.fromSeedHex('ffffffffffffffffffffffffffffffff') @@ -124,38 +124,43 @@ describe('HDNode', function () { }) describe('getAddress', function () { - it('wraps keyPair.getAddress', setupTest(function () { - this.mock(keyPair).expects('getAddress') - .once().withArgs().returns('foobar') + it('wraps keyPair.getAddress', mockme(function () { + this.mock(hd.keyPair, 'getAddress', function () { + return 'foo' + }, 1) - assert.strictEqual(hd.getAddress(), 'foobar') + assert.strictEqual(hd.getAddress(), 'foo') })) }) describe('getNetwork', function () { - it('wraps keyPair.getNetwork', setupTest(function () { - this.mock(keyPair).expects('getNetwork') - .once().withArgs().returns('network') + it('wraps keyPair.getNetwork', mockme(function () { + this.mock(hd.keyPair, 'getNetwork', function () { + return 'foo' + }, 1) - assert.strictEqual(hd.getNetwork(), 'network') + assert.strictEqual(hd.getNetwork(), 'foo') })) }) describe('getPublicKeyBuffer', function () { - it('wraps keyPair.getPublicKeyBuffer', setupTest(function () { - this.mock(keyPair).expects('getPublicKeyBuffer') - .once().withArgs().returns('pubKeyBuffer') + it('wraps keyPair.getPublicKeyBuffer', mockme(function () { + this.mock(hd.keyPair, 'getPublicKeyBuffer', function () { + return 'foo' + }, 1) - assert.strictEqual(hd.getPublicKeyBuffer(), 'pubKeyBuffer') + assert.strictEqual(hd.getPublicKeyBuffer(), 'foo') })) }) describe('sign', function () { - it('wraps keyPair.sign', setupTest(function () { - this.mock(keyPair).expects('sign') - .once().withArgs(hash).returns('signed') + it('wraps keyPair.sign', mockme(function () { + this.mock(hd.keyPair, 'sign', function (h) { + assert.strictEqual(hash, h) + return 'foo' + }, 1) - assert.strictEqual(hd.sign(hash), 'signed') + assert.strictEqual(hd.sign(hash), 'foo') })) }) @@ -166,11 +171,14 @@ describe('HDNode', function () { signature = hd.sign(hash) }) - it('wraps keyPair.verify', setupTest(function () { - this.mock(keyPair).expects('verify') - .once().withArgs(hash, signature).returns('verified') + it('wraps keyPair.verify', mockme(function () { + this.mock(hd.keyPair, 'verify', function (h, s) { + assert.strictEqual(hash, h) + assert.strictEqual(signature, s) + return 'foo' + }, 1) - assert.strictEqual(hd.verify(hash, signature), 'verified') + assert.strictEqual(hd.verify(hash, signature), 'foo') })) }) }) diff --git a/test/mockme.js b/test/mockme.js new file mode 100644 index 0000000..2507ef6 --- /dev/null +++ b/test/mockme.js @@ -0,0 +1,66 @@ +// TODO: move to own dependency +function mockme (f) { + var mocks = [] + + function mock (constructor, functionName, func, n) { + n = n || Infinity + + var initial = constructor[functionName] + var context = constructor.constructor.name !== 'Function' ? constructor : null + function __mock () { + if (func.calls > n) throw new RangeError('Exceeded expected number of calls') + var r = func.apply(context, arguments) + ++func.calls + return r + } + func.calls = 0 + func.expected = n + func.reset = function reset () { + constructor[functionName] = initial + } + constructor[functionName] = __mock + mocks.push(func) + } + + function stub (func, n) { + n = n || Infinity + + function __stub () { + if (func.calls > n) throw new RangeError('Exceeded expected number of calls') + var r = func.apply(null, arguments) + ++func.calls + return r + } + func.calls = 0 + func.expected = n + + mocks.push(func) + return __stub + } + + return function run () { + var err + try { + f.apply({ + mock: mock, + stub: stub + }, arguments) + } catch (e) { + err = e + } + + mocks.forEach(function (x) { + if (!err) { + if (x.expected !== Infinity && x.calls !== x.expected) { + err = new RangeError('Too few calls') + } + } + + if (x.reset) x.reset() + }) + + if (err) throw err + } +} + +module.exports = mockme From 04c628e9f108fb26f13f10c64d2ac2590ba83cab Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 20 Mar 2018 14:49:10 +1100 Subject: [PATCH 004/568] add hoodwink dependency --- package.json | 1 + test/ecdsa.js | 6 ++--- test/ecpair.js | 12 ++++----- test/hdnode.js | 16 ++++++------ test/mockme.js | 66 -------------------------------------------------- 5 files changed, 18 insertions(+), 83 deletions(-) delete mode 100644 test/mockme.js diff --git a/package.json b/package.json index 7250f63..f785872 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "bip65": "^1.0.1", "bs58": "^4.0.0", "dhttp": "^2.4.2", + "hoodwink": "^1.0.0", "minimaldata": "^1.0.2", "mocha": "^5.0.1", "nyc": "^11.4.1", diff --git a/test/ecdsa.js b/test/ecdsa.js index f93beb9..f6c6004 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -3,7 +3,7 @@ var assert = require('assert') var bcrypto = require('../src/crypto') var ecdsa = require('../src/ecdsa') -var mockme = require('./mockme') +var hoodwink = require('hoodwink') var BigInteger = require('bigi') var ECSignature = require('../src/ecsignature') @@ -28,7 +28,7 @@ describe('ecdsa', function () { }) }) - it('loops until an appropriate k value is found', mockme(function () { + it('loops until an appropriate k value is found', hoodwink(function () { this.mock(BigInteger, 'fromBuffer', function f (b) { assert.strictEqual(b.length, 32) if (f.calls === 0) return BigInteger.ZERO // < 1 @@ -43,7 +43,7 @@ describe('ecdsa', function () { assert.strictEqual(k.toString(), '42') })) - it('loops until a suitable signature is found', mockme(function () { + it('loops until a suitable signature is found', hoodwink(function () { var checkSigStub = this.stub(function f () { if (f.calls === 0) return false // bad signature if (f.calls === 1) return true // good signature diff --git a/test/ecpair.js b/test/ecpair.js index 342b166..2530e32 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -5,7 +5,7 @@ var assert = require('assert') var ecdsa = require('../src/ecdsa') var ecurve = require('ecurve') var proxyquire = require('proxyquire') -var mockme = require('./mockme') +var hoodwink = require('hoodwink') var BigInteger = require('bigi') var ECPair = require('../src/ecpair') @@ -74,7 +74,7 @@ describe('ECPair', function () { keyPair = new ECPair(BigInteger.ONE) }) - it('wraps Q.getEncoded', mockme(function () { + it('wraps Q.getEncoded', hoodwink(function () { this.mock(keyPair.Q, 'getEncoded', function (compressed) { assert.strictEqual(compressed, keyPair.compressed) }, 1) @@ -176,7 +176,7 @@ describe('ECPair', function () { }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/) }) - it('loops until d is within interval [1, n - 1] : 1', mockme(function () { + it('loops until d is within interval [1, n - 1] : 1', hoodwink(function () { var rng = this.stub(function f () { if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // 0 return BigInteger.ONE.toBuffer(32) // >0 @@ -185,7 +185,7 @@ describe('ECPair', function () { ECPair.makeRandom({ rng: rng }) })) - it('loops until d is within interval [1, n - 1] : n - 1', mockme(function () { + it('loops until d is within interval [1, n - 1] : n - 1', hoodwink(function () { var rng = this.stub(function f () { if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // <1 if (f.calls === 1) return curve.n.toBuffer(32) // >n-1 @@ -226,7 +226,7 @@ describe('ECPair', function () { }) describe('signing', function () { - it('wraps ecdsa.sign', mockme(function () { + it('wraps ecdsa.sign', hoodwink(function () { this.mock(ecdsa, 'sign', function (h, d) { assert.strictEqual(h, hash) assert.strictEqual(d, keyPair.d) @@ -251,7 +251,7 @@ describe('ECPair', function () { signature = keyPair.sign(hash) }) - it('wraps ecdsa.verify', mockme(function () { + it('wraps ecdsa.verify', hoodwink(function () { this.mock(ecdsa, 'verify', function (h, s, q) { assert.strictEqual(h, hash) assert.strictEqual(s, signature) diff --git a/test/hdnode.js b/test/hdnode.js index b5321a5..42a2d14 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -3,7 +3,7 @@ var assert = require('assert') var ecdsa = require('../src/ecdsa') -var mockme = require('./mockme') +var hoodwink = require('hoodwink') var BigInteger = require('bigi') var ECPair = require('../src/ecpair') @@ -79,7 +79,7 @@ describe('HDNode', function () { }) }) - it('throws if IL is not within interval [1, n - 1] | IL === 0', mockme(function () { + it('throws if IL is not within interval [1, n - 1] | IL === 0', hoodwink(function () { this.mock(BigInteger, 'fromBuffer', function () { return BigInteger.ZERO }, 1) @@ -89,7 +89,7 @@ describe('HDNode', function () { }, /Private key must be greater than 0/) })) - it('throws if IL is not within interval [1, n - 1] | IL === n', mockme(function () { + it('throws if IL is not within interval [1, n - 1] | IL === n', hoodwink(function () { this.mock(BigInteger, 'fromBuffer', function () { return curve.n }, 1) @@ -124,7 +124,7 @@ describe('HDNode', function () { }) describe('getAddress', function () { - it('wraps keyPair.getAddress', mockme(function () { + it('wraps keyPair.getAddress', hoodwink(function () { this.mock(hd.keyPair, 'getAddress', function () { return 'foo' }, 1) @@ -134,7 +134,7 @@ describe('HDNode', function () { }) describe('getNetwork', function () { - it('wraps keyPair.getNetwork', mockme(function () { + it('wraps keyPair.getNetwork', hoodwink(function () { this.mock(hd.keyPair, 'getNetwork', function () { return 'foo' }, 1) @@ -144,7 +144,7 @@ describe('HDNode', function () { }) describe('getPublicKeyBuffer', function () { - it('wraps keyPair.getPublicKeyBuffer', mockme(function () { + it('wraps keyPair.getPublicKeyBuffer', hoodwink(function () { this.mock(hd.keyPair, 'getPublicKeyBuffer', function () { return 'foo' }, 1) @@ -154,7 +154,7 @@ describe('HDNode', function () { }) describe('sign', function () { - it('wraps keyPair.sign', mockme(function () { + it('wraps keyPair.sign', hoodwink(function () { this.mock(hd.keyPair, 'sign', function (h) { assert.strictEqual(hash, h) return 'foo' @@ -171,7 +171,7 @@ describe('HDNode', function () { signature = hd.sign(hash) }) - it('wraps keyPair.verify', mockme(function () { + it('wraps keyPair.verify', hoodwink(function () { this.mock(hd.keyPair, 'verify', function (h, s) { assert.strictEqual(hash, h) assert.strictEqual(signature, s) diff --git a/test/mockme.js b/test/mockme.js deleted file mode 100644 index 2507ef6..0000000 --- a/test/mockme.js +++ /dev/null @@ -1,66 +0,0 @@ -// TODO: move to own dependency -function mockme (f) { - var mocks = [] - - function mock (constructor, functionName, func, n) { - n = n || Infinity - - var initial = constructor[functionName] - var context = constructor.constructor.name !== 'Function' ? constructor : null - function __mock () { - if (func.calls > n) throw new RangeError('Exceeded expected number of calls') - var r = func.apply(context, arguments) - ++func.calls - return r - } - func.calls = 0 - func.expected = n - func.reset = function reset () { - constructor[functionName] = initial - } - constructor[functionName] = __mock - mocks.push(func) - } - - function stub (func, n) { - n = n || Infinity - - function __stub () { - if (func.calls > n) throw new RangeError('Exceeded expected number of calls') - var r = func.apply(null, arguments) - ++func.calls - return r - } - func.calls = 0 - func.expected = n - - mocks.push(func) - return __stub - } - - return function run () { - var err - try { - f.apply({ - mock: mock, - stub: stub - }, arguments) - } catch (e) { - err = e - } - - mocks.forEach(function (x) { - if (!err) { - if (x.expected !== Infinity && x.calls !== x.expected) { - err = new RangeError('Too few calls') - } - } - - if (x.reset) x.reset() - }) - - if (err) throw err - } -} - -module.exports = mockme From f165345451a121de6a3a63a4446185395f3572b3 Mon Sep 17 00:00:00 2001 From: Prahalad Belavadi <prahaladbelavadi@gmail.com> Date: Fri, 6 Apr 2018 17:26:51 +0530 Subject: [PATCH 005/568] add coinbase multisig tool to list of utilizing projects --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4105a60..dfe8b7f 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ If you have a use case that you feel could be listed here, please [ask for it](h - [Melis Wallet](https://melis.io) - [Robocoin](https://wallet.robocoin.com) - [Skyhook ATM](http://projectskyhook.com) +- [Coinbase Multisig tool](https://github.com/coinbase/multisig-tool) ## Contributing From 549b36bf1a85fbf9ca3f7aaa6812443c5d2967ae Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sat, 14 Apr 2018 01:07:10 +1000 Subject: [PATCH 006/568] rm deprecated bufferutils --- src/bufferutils.js | 29 +------- src/index.js | 2 - test/bufferutils.js | 125 ++------------------------------- test/fixtures/bufferutils.json | 73 +++++-------------- 4 files changed, 22 insertions(+), 207 deletions(-) diff --git a/src/bufferutils.js b/src/bufferutils.js index 179e84e..b173b3d 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -1,6 +1,3 @@ -var pushdata = require('pushdata-bitcoin') -var varuint = require('varuint-bitcoin') - // https://github.com/feross/buffer/blob/master/index.js#L1127 function verifuint (value, max) { if (typeof value !== 'number') throw new Error('cannot write a non-number as a number') @@ -15,7 +12,6 @@ function readUInt64LE (buffer, offset) { b *= 0x100000000 verifuint(b + a, 0x001fffffffffffff) - return b + a } @@ -27,30 +23,7 @@ function writeUInt64LE (buffer, value, offset) { return offset + 8 } -// TODO: remove in 4.0.0? -function readVarInt (buffer, offset) { - var result = varuint.decode(buffer, offset) - - return { - number: result, - size: varuint.decode.bytes - } -} - -// TODO: remove in 4.0.0? -function writeVarInt (buffer, number, offset) { - varuint.encode(number, buffer, offset) - return varuint.encode.bytes -} - module.exports = { - pushDataSize: pushdata.encodingLength, - readPushDataInt: pushdata.decode, readUInt64LE: readUInt64LE, - readVarInt: readVarInt, - varIntBuffer: varuint.encode, - varIntSize: varuint.encodingLength, - writePushDataInt: pushdata.encode, - writeUInt64LE: writeUInt64LE, - writeVarInt: writeVarInt + writeUInt64LE: writeUInt64LE } diff --git a/src/index.js b/src/index.js index 1ad7099..a809b3c 100644 --- a/src/index.js +++ b/src/index.js @@ -6,8 +6,6 @@ for (var key in templates) { } module.exports = { - bufferutils: require('./bufferutils'), // TODO: remove in 4.0.0 - Block: require('./block'), ECPair: require('./ecpair'), ECSignature: require('./ecsignature'), diff --git a/test/bufferutils.js b/test/bufferutils.js index 6efbb9f..e172ba1 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -6,49 +6,10 @@ var bufferutils = require('../src/bufferutils') var fixtures = require('./fixtures/bufferutils.json') describe('bufferutils', function () { - describe('pushDataSize', function () { - fixtures.valid.forEach(function (f) { - it('determines the pushDataSize of ' + f.dec + ' correctly', function () { - if (!f.hexPD) return - - var size = bufferutils.pushDataSize(f.dec) - - assert.strictEqual(size, f.hexPD.length / 2) - }) - }) - }) - - describe('readPushDataInt', function () { - fixtures.valid.forEach(function (f) { - if (!f.hexPD) return - - it('decodes ' + f.hexPD + ' correctly', function () { - var buffer = Buffer.from(f.hexPD, 'hex') - var d = bufferutils.readPushDataInt(buffer, 0) - var fopcode = parseInt(f.hexPD.substr(0, 2), 16) - - assert.strictEqual(d.opcode, fopcode) - assert.strictEqual(d.number, f.dec) - assert.strictEqual(d.size, buffer.length) - }) - }) - - fixtures.invalid.readPushDataInt.forEach(function (f) { - if (!f.hexPD) return - - it('decodes ' + f.hexPD + ' as null', function () { - var buffer = Buffer.from(f.hexPD, 'hex') - - var n = bufferutils.readPushDataInt(buffer, 0) - assert.strictEqual(n, null) - }) - }) - }) - describe('readUInt64LE', function () { fixtures.valid.forEach(function (f) { - it('decodes ' + f.hex64 + ' correctly', function () { - var buffer = Buffer.from(f.hex64, 'hex') + it('decodes ' + f.hex, function () { + var buffer = Buffer.from(f.hex, 'hex') var number = bufferutils.readUInt64LE(buffer, 0) assert.strictEqual(number, f.dec) @@ -57,7 +18,7 @@ describe('bufferutils', function () { fixtures.invalid.readUInt64LE.forEach(function (f) { it('throws on ' + f.description, function () { - var buffer = Buffer.from(f.hex64, 'hex') + var buffer = Buffer.from(f.hex, 'hex') assert.throws(function () { bufferutils.readUInt64LE(buffer, 0) @@ -66,68 +27,13 @@ describe('bufferutils', function () { }) }) - describe('readVarInt', function () { - fixtures.valid.forEach(function (f) { - it('decodes ' + f.hexVI + ' correctly', function () { - var buffer = Buffer.from(f.hexVI, 'hex') - var d = bufferutils.readVarInt(buffer, 0) - - assert.strictEqual(d.number, f.dec) - assert.strictEqual(d.size, buffer.length) - }) - }) - - fixtures.invalid.readUInt64LE.forEach(function (f) { - it('throws on ' + f.description, function () { - var buffer = Buffer.from(f.hexVI, 'hex') - - assert.throws(function () { - bufferutils.readVarInt(buffer, 0) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('varIntBuffer', function () { - fixtures.valid.forEach(function (f) { - it('encodes ' + f.dec + ' correctly', function () { - var buffer = bufferutils.varIntBuffer(f.dec) - - assert.strictEqual(buffer.toString('hex'), f.hexVI) - }) - }) - }) - - describe('varIntSize', function () { - fixtures.valid.forEach(function (f) { - it('determines the varIntSize of ' + f.dec + ' correctly', function () { - var size = bufferutils.varIntSize(f.dec) - - assert.strictEqual(size, f.hexVI.length / 2) - }) - }) - }) - - describe('writePushDataInt', function () { - fixtures.valid.forEach(function (f) { - if (!f.hexPD) return - - it('encodes ' + f.dec + ' correctly', function () { - var buffer = Buffer.alloc(5, 0) - - var n = bufferutils.writePushDataInt(buffer, f.dec, 0) - assert.strictEqual(buffer.slice(0, n).toString('hex'), f.hexPD) - }) - }) - }) - describe('writeUInt64LE', function () { fixtures.valid.forEach(function (f) { - it('encodes ' + f.dec + ' correctly', function () { + it('encodes ' + f.dec, function () { var buffer = Buffer.alloc(8, 0) bufferutils.writeUInt64LE(buffer, f.dec, 0) - assert.strictEqual(buffer.toString('hex'), f.hex64) + assert.strictEqual(buffer.toString('hex'), f.hex) }) }) @@ -141,25 +47,4 @@ describe('bufferutils', function () { }) }) }) - - describe('writeVarInt', function () { - fixtures.valid.forEach(function (f) { - it('encodes ' + f.dec + ' correctly', function () { - var buffer = Buffer.alloc(9, 0) - - var n = bufferutils.writeVarInt(buffer, f.dec, 0) - assert.strictEqual(buffer.slice(0, n).toString('hex'), f.hexVI) - }) - }) - - fixtures.invalid.readUInt64LE.forEach(function (f) { - it('throws on ' + f.description, function () { - var buffer = Buffer.alloc(9, 0) - - assert.throws(function () { - bufferutils.writeVarInt(buffer, f.dec, 0) - }, new RegExp(f.exception)) - }) - }) - }) }) diff --git a/test/fixtures/bufferutils.json b/test/fixtures/bufferutils.json index 2b884cf..42c145d 100644 --- a/test/fixtures/bufferutils.json +++ b/test/fixtures/bufferutils.json @@ -2,84 +2,59 @@ "valid": [ { "dec": 0, - "hex64": "0000000000000000", - "hexVI": "00", - "hexPD": "00" + "hex": "0000000000000000" }, { "dec": 1, - "hex64": "0100000000000000", - "hexVI": "01", - "hexPD": "01" + "hex": "0100000000000000" }, { "dec": 252, - "hex64": "fc00000000000000", - "hexVI": "fc", - "hexPD": "4cfc" + "hex": "fc00000000000000" }, { "dec": 253, - "hex64": "fd00000000000000", - "hexVI": "fdfd00", - "hexPD": "4cfd" + "hex": "fd00000000000000" }, { "dec": 254, - "hex64": "fe00000000000000", - "hexVI": "fdfe00", - "hexPD": "4cfe" + "hex": "fe00000000000000" }, { "dec": 255, - "hex64": "ff00000000000000", - "hexVI": "fdff00", - "hexPD": "4cff" + "hex": "ff00000000000000" }, { "dec": 65534, - "hex64": "feff000000000000", - "hexVI": "fdfeff", - "hexPD": "4dfeff" + "hex": "feff000000000000" }, { "dec": 65535, - "hex64": "ffff000000000000", - "hexVI": "fdffff", - "hexPD": "4dffff" + "hex": "ffff000000000000" }, { "dec": 65536, - "hex64": "0000010000000000", - "hexVI": "fe00000100", - "hexPD": "4e00000100" + "hex": "0000010000000000" }, { "dec": 65537, - "hex64": "0100010000000000", - "hexVI": "fe01000100", - "hexPD": "4e01000100" + "hex": "0100010000000000" }, { "dec": 4294967295, - "hex64": "ffffffff00000000", - "hexVI": "feffffffff", - "hexPD": "4effffffff" + "hex": "ffffffff00000000" }, { "dec": 4294967296, - "hex64": "0000000001000000", - "hexVI": "ff0000000001000000" + "hex": "0000000001000000" }, { "dec": 4294967297, - "hex64": "0100000001000000", - "hexVI": "ff0100000001000000" + "hex": "0100000001000000" }, { "dec": 9007199254740991, - "hex64": "ffffffffffff1f00", - "hexVI": "ffffffffffffff1f00" + "hex": "ffffffffffff1f00" } ], "invalid": { @@ -87,31 +62,15 @@ { "description": "n === 2^53", "exception": "RangeError: value out of range", - "hex64": "0000000000002000", - "hexVI": "ff0000000000000020", + "hex": "0000000000002000", "dec": 9007199254740992 }, { "description": "n > 2^53", "exception": "RangeError: value out of range", - "hex64": "0100000000002000", - "hexVI": "ff0100000000000020", + "hex": "0100000000002000", "dec": 9007199254740993 } - ], - "readPushDataInt": [ - { - "description": "OP_PUSHDATA1, no size", - "hexPD": "4c" - }, - { - "description": "OP_PUSHDATA2, no size", - "hexPD": "4d" - }, - { - "description": "OP_PUSHDATA4, no size", - "hexPD": "4e" - } ] } } From 3bfdacafa239121e8d8219491e9aa53098d1f83d Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sat, 14 Apr 2018 01:18:31 +1000 Subject: [PATCH 007/568] change TransactionBuilder to default to version 2 --- src/transaction_builder.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index d1e8587..ca59d88 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -476,6 +476,7 @@ function TransactionBuilder (network, maximumFeeRate) { this.inputs = [] this.tx = new Transaction() + this.tx.version = 2 } TransactionBuilder.prototype.setLockTime = function (locktime) { From 5af87c0c496cf0425b60a3d15b5c836be10960c6 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sat, 14 Apr 2018 01:27:57 +1000 Subject: [PATCH 008/568] privatise internals for TxBuilder --- src/transaction_builder.js | 52 ++++++++++++++++++------------------- test/transaction_builder.js | 31 +++++++++++----------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index d1e8587..c83c94c 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -468,21 +468,21 @@ function buildInput (input, allowIncomplete) { } function TransactionBuilder (network, maximumFeeRate) { - this.prevTxMap = {} + this.__prevTxSet = {} this.network = network || networks.bitcoin // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) this.maximumFeeRate = maximumFeeRate || 2500 - this.inputs = [] - this.tx = new Transaction() + this.__inputs = [] + this.__tx = new Transaction() } TransactionBuilder.prototype.setLockTime = function (locktime) { typeforce(types.UInt32, locktime) // if any signatures exist, throw - if (this.inputs.some(function (input) { + if (this.__inputs.some(function (input) { if (!input.signatures) return false return input.signatures.some(function (s) { return s }) @@ -490,14 +490,14 @@ TransactionBuilder.prototype.setLockTime = function (locktime) { throw new Error('No, this would invalidate signatures') } - this.tx.locktime = locktime + this.__tx.locktime = locktime } TransactionBuilder.prototype.setVersion = function (version) { typeforce(types.UInt32, version) // XXX: this might eventually become more complex depending on what the versions represent - this.tx.version = version + this.__tx.version = version } TransactionBuilder.fromTransaction = function (transaction, network) { @@ -522,7 +522,7 @@ TransactionBuilder.fromTransaction = function (transaction, network) { }) // fix some things not possible through the public API - txb.inputs.forEach(function (input, i) { + txb.__inputs.forEach(function (input, i) { fixMultisigOrder(input, transaction, i) }) @@ -563,7 +563,7 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) } var prevTxOut = txHash.toString('hex') + ':' + vout - if (this.prevTxMap[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut) + if (this.__prevTxSet[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut) var input = {} @@ -596,9 +596,9 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) input.prevOutType = prevOutType || btemplates.classifyOutput(options.prevOutScript) } - var vin = this.tx.addInput(txHash, vout, options.sequence, options.scriptSig) - this.inputs[vin] = input - this.prevTxMap[prevTxOut] = vin + var vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig) + this.__inputs[vin] = input + this.__prevTxSet[prevTxOut] = true return vin } @@ -612,7 +612,7 @@ TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) { scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network) } - return this.tx.addOutput(scriptPubKey, value) + return this.__tx.addOutput(scriptPubKey, value) } TransactionBuilder.prototype.build = function () { @@ -624,13 +624,13 @@ TransactionBuilder.prototype.buildIncomplete = function () { TransactionBuilder.prototype.__build = function (allowIncomplete) { if (!allowIncomplete) { - if (!this.tx.ins.length) throw new Error('Transaction has no inputs') - if (!this.tx.outs.length) throw new Error('Transaction has no outputs') + if (!this.__tx.ins.length) throw new Error('Transaction has no inputs') + if (!this.__tx.outs.length) throw new Error('Transaction has no outputs') } - var tx = this.tx.clone() + var tx = this.__tx.clone() // Create script signatures from inputs - this.inputs.forEach(function (input, i) { + this.__inputs.forEach(function (input, i) { var scriptType = input.witnessScriptType || input.redeemScriptType || input.prevOutType if (!scriptType && !allowIncomplete) throw new Error('Transaction is not complete') var result = buildInput(input, allowIncomplete) @@ -672,10 +672,10 @@ function canSign (input) { TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) { // TODO: remove keyPair.network matching in 4.0.0 if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network') - if (!this.inputs[vin]) throw new Error('No input at index: ' + vin) + if (!this.__inputs[vin]) throw new Error('No input at index: ' + vin) hashType = hashType || Transaction.SIGHASH_ALL - var input = this.inputs[vin] + var input = this.__inputs[vin] // if redeemScript was previously provided, enforce consistency if (input.redeemScript !== undefined && @@ -699,9 +699,9 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy // ready to sign var signatureHash if (input.witness) { - signatureHash = this.tx.hashForWitnessV0(vin, input.signScript, input.value, hashType) + signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType) } else { - signatureHash = this.tx.hashForSignature(vin, input.signScript, hashType) + signatureHash = this.__tx.hashForSignature(vin, input.signScript, hashType) } // enforce in order signing of public keys @@ -730,7 +730,7 @@ function signatureHashType (buffer) { } TransactionBuilder.prototype.__canModifyInputs = function () { - return this.inputs.every(function (input) { + return this.__inputs.every(function (input) { // any signatures? if (input.signatures === undefined) return true @@ -746,10 +746,10 @@ TransactionBuilder.prototype.__canModifyInputs = function () { } TransactionBuilder.prototype.__canModifyOutputs = function () { - var nInputs = this.tx.ins.length - var nOutputs = this.tx.outs.length + var nInputs = this.__tx.ins.length + var nOutputs = this.__tx.outs.length - return this.inputs.every(function (input) { + return this.__inputs.every(function (input) { if (input.signatures === undefined) return true return input.signatures.every(function (signature) { @@ -770,11 +770,11 @@ TransactionBuilder.prototype.__canModifyOutputs = function () { TransactionBuilder.prototype.__overMaximumFees = function (bytes) { // not all inputs will have .value defined - var incoming = this.inputs.reduce(function (a, x) { return a + (x.value >>> 0) }, 0) + var incoming = this.__inputs.reduce(function (a, x) { return a + (x.value >>> 0) }, 0) // but all outputs do, and if we have any input value // we can immediately determine if the outputs are too small - var outgoing = this.tx.outs.reduce(function (a, x) { return a + x.value }, 0) + var outgoing = this.__tx.outs.reduce(function (a, x) { return a + x.value }, 0) var fee = incoming - outgoing var feeRate = fee / bytes diff --git a/test/transaction_builder.js b/test/transaction_builder.js index f2325a4..757426d 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -133,10 +133,11 @@ describe('TransactionBuilder', function () { }) }) - it('correctly classifies transaction inputs', function () { + it('classifies transaction inputs', function () { var tx = Transaction.fromHex(fixtures.valid.classification.hex) var txb = TransactionBuilder.fromTransaction(tx) - txb.inputs.forEach(function (i) { + + txb.__inputs.forEach(function (i) { assert.strictEqual(i.prevOutType, 'scripthash') assert.strictEqual(i.redeemScriptType, 'multisig') assert.strictEqual(i.signType, 'multisig') @@ -164,22 +165,22 @@ describe('TransactionBuilder', function () { var vin = txb.addInput(txHash, 1, 54) assert.strictEqual(vin, 0) - var txIn = txb.tx.ins[0] + var txIn = txb.__tx.ins[0] assert.strictEqual(txIn.hash, txHash) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.inputs[0].prevOutScript, undefined) + assert.strictEqual(txb.__inputs[0].prevOutScript, undefined) }) it('accepts a txHash, index [, sequence number and scriptPubKey]', function () { var vin = txb.addInput(txHash, 1, 54, scripts[1]) assert.strictEqual(vin, 0) - var txIn = txb.tx.ins[0] + var txIn = txb.__tx.ins[0] assert.strictEqual(txIn.hash, txHash) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.inputs[0].prevOutScript, scripts[1]) + assert.strictEqual(txb.__inputs[0].prevOutScript, scripts[1]) }) it('accepts a prevTx, index [and sequence number]', function () { @@ -190,11 +191,11 @@ describe('TransactionBuilder', function () { var vin = txb.addInput(prevTx, 1, 54) assert.strictEqual(vin, 0) - var txIn = txb.tx.ins[0] + var txIn = txb.__tx.ins[0] assert.deepEqual(txIn.hash, prevTx.getHash()) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.inputs[0].prevOutScript, scripts[1]) + assert.strictEqual(txb.__inputs[0].prevOutScript, scripts[1]) }) it('returns the input index', function () { @@ -222,7 +223,7 @@ describe('TransactionBuilder', function () { var vout = txb.addOutput(keyPair.getAddress(), 1000) assert.strictEqual(vout, 0) - var txout = txb.tx.outs[0] + var txout = txb.__tx.outs[0] assert.deepEqual(txout.script, scripts[0]) assert.strictEqual(txout.value, 1000) }) @@ -231,7 +232,7 @@ describe('TransactionBuilder', function () { var vout = txb.addOutput(scripts[0], 1000) assert.strictEqual(vout, 0) - var txout = txb.tx.outs[0] + var txout = txb.__tx.outs[0] assert.deepEqual(txout.script, scripts[0]) assert.strictEqual(txout.value, 1000) }) @@ -504,10 +505,10 @@ describe('TransactionBuilder', function () { '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000' var txb = TransactionBuilder.fromTransaction(Transaction.fromHex(rawtx)) - txb.inputs[0].value = 241530 - txb.inputs[1].value = 241530 - txb.inputs[2].value = 248920 - txb.inputs[3].value = 248920 + txb.__inputs[0].value = 241530 + txb.__inputs[1].value = 241530 + txb.__inputs[2].value = 248920 + txb.__inputs[3].value = 248920 assert.throws(function () { txb.build() @@ -530,7 +531,7 @@ describe('TransactionBuilder', function () { var txHex = tx.toHex() var newTxb = TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) // input should have the key 'witness' set to true - assert.equal(newTxb.inputs[0].witness, true) + assert.equal(newTxb.__inputs[0].witness, true) }) it('should handle badly pre-filled OP_0s', function () { From 41885bfab271806450816da6af2699ba297f33d0 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sat, 14 Apr 2018 01:59:56 +1000 Subject: [PATCH 009/568] nulldata: change data to array --- src/templates/nulldata.js | 6 +++--- test/fixtures/script.json | 4 ++++ test/fixtures/templates.json | 17 +++++++++++++++-- test/templates.js | 2 +- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js index 107aaeb..b7d53f8 100644 --- a/src/templates/nulldata.js +++ b/src/templates/nulldata.js @@ -14,15 +14,15 @@ function check (script) { check.toJSON = function () { return 'null data output' } function encode (data) { - typeforce(types.Buffer, data) + typeforce([types.Buffer], data) - return bscript.compile([OPS.OP_RETURN, data]) + return bscript.compile([OPS.OP_RETURN].concat(data)) } function decode (buffer) { typeforce(check, buffer) - return buffer.slice(2) + return bscript.decompile(buffer).slice(1) } module.exports = { diff --git a/test/fixtures/script.json b/test/fixtures/script.json index 3ba8bc3..5046757 100644 --- a/test/fixtures/script.json +++ b/test/fixtures/script.json @@ -95,6 +95,10 @@ "asm": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef", "script": "6a14deadffffffffffffffffffffffffffffffffbeef" }, + { + "asm": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef deadffffffffffffffffffffffffffffffffbeef", + "script": "6a14deadffffffffffffffffffffffffffffffffbeef14deadffffffffffffffffffffffffffffffffbeef" + }, { "asm": "OP_0 OP_0 3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901 3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", "script": "0000473044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901483045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", diff --git a/test/fixtures/templates.json b/test/fixtures/templates.json index 0cba6c6..61728c0 100644 --- a/test/fixtures/templates.json +++ b/test/fixtures/templates.json @@ -113,13 +113,26 @@ }, { "type": "nulldata", - "data": "06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", + "data": [ + "06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474" + ], "output": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", "outputHex": "6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474" }, { "type": "nulldata", - "data": "deadffffffffffffffffffffffffffffffffbeef", + "data": [ + "deadffffffffffffffffffffffffffffffffbeef", + "deadffffffffffffffffffffffffffffffffbeef" + ], + "output": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef deadffffffffffffffffffffffffffffffffbeef", + "outputHex": "6a14deadffffffffffffffffffffffffffffffffbeef14deadffffffffffffffffffffffffffffffffbeef" + }, + { + "type": "nulldata", + "data": [ + "deadffffffffffffffffffffffffffffffffbeef" + ], "output": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef", "outputHex": "6a14deadffffffffffffffffffffffffffffffffbeef" }, diff --git a/test/templates.js b/test/templates.js index 104b326..c89bd52 100644 --- a/test/templates.js +++ b/test/templates.js @@ -499,7 +499,7 @@ describe('script-templates', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'nulldata') return - var data = Buffer.from(f.data, 'hex') + var data = f.data.map(function (x) { return Buffer.from(x, 'hex') }) var output = btemplates.nullData.output.encode(data) it('encodes to ' + f.output, function () { From 647f06e629a0f98d171defac000c8d385d73bf37 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 16 Apr 2018 10:28:54 +1000 Subject: [PATCH 010/568] amend test fixtures and add default test case --- test/fixtures/transaction_builder.json | 111 +++++++++++++++++++++---- test/integration/transactions.js | 2 + test/transaction_builder.js | 25 +++--- 3 files changed, 109 insertions(+), 29 deletions(-) diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 1475c97..29642d1 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -4,6 +4,7 @@ { "description": "Transaction w/ P2PKH -> P2PKH", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006b483045022100a3b254e1c10b5d039f36c05f323995d6e5a367d98dd78a13d5bbc3991b35720e022022fccea3897d594de0689601fbd486588d5bfa6915be2386db0397ee9a6e80b601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -25,6 +26,7 @@ { "description": "Transaction w/ P2PK -> P2PKH", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000494830450221009833abb3ab49d7004c06bcc79eafd6905ada3eee91f3376ad388548034acd9a702202e84dda6ef2678c82256afcfc459aaa68e179b2bb0e6b2dc3f1410e132c5e6c301ffffffff0100f90295000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -47,6 +49,7 @@ { "description": "Transaction w/ P2SH(P2PKH) -> P2PKH", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000085483045022100a3b254e1c10b5d039f36c05f323995d6e5a367d98dd78a13d5bbc3991b35720e022022fccea3897d594de0689601fbd486588d5bfa6915be2386db0397ee9a6e80b601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817981976a914751e76e8199196d454941c45d1b3a323f1433bd688acffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -70,6 +73,7 @@ "description": "Transaction w/ P2SH(P2MS 2/2) -> P2PKH", "network": "testnet", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd1b0100483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a4014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -96,6 +100,7 @@ "description": "Transaction w/ P2MS 2/2 -> P2PKH", "network": "testnet", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009200483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a401ffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -122,6 +127,7 @@ "description": "Transaction w/ P2MS 2/3 -> P2PKH", "network": "testnet", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000910047304402206b2fc7d3182e2853cab5bcffb85c3ef5470d2d05c496295538c9947af3bfd0ec0220300aa705a985c74f76c26c6d68da9b61b5c4cd5432e8c6a54623f376c8bf8cde01473044022031059c4dd6a97d84e3a4eb1ca21a9870bd1762fbd5db7c1932d75e56da78794502200f22d85be3c5f7035e89a147ee2619a066df19aff14a62e6bb3f649b6da19edf01ffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -148,6 +154,7 @@ "description": "Transaction w/ P2MS 2/2 (reverse order) -> P2PKH", "network": "testnet", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009200483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a401ffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -174,6 +181,7 @@ "description": "Transaction w/ P2SH(P2MS 2/3)", "network": "testnet", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd5e0100483045022100eec19e061cad41610f9b42d2b06638b6b0fec3da0de9c6858e7f8c06053979900220622936dd47e202b2ad17639cda680e52334d407149252959936bb1f38e4acc52014830450221009aac215157a74a18234fd06be27448dccee809986bbf93be457a9262f0c69a9402203ff41d7c757f0e8951e4471f205087ecff499f986400ab18210eaad9a628e33c014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -200,6 +208,7 @@ "description": "Transaction w/ P2SH(P2MS 2/3), different hash types", "network": "testnet", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd5e0100483045022100eec19e061cad41610f9b42d2b06638b6b0fec3da0de9c6858e7f8c06053979900220622936dd47e202b2ad17639cda680e52334d407149252959936bb1f38e4acc5201483045022100d404fb6de6cf42efb9d7948d2e8fb6618f8eba55ecd25907d18d576d9aa6f39d02205ec2e7fa7c5f8a9793732ca9d2f9aba3b2bb04ca6d467ba36940e0f695e48de5024cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -227,6 +236,7 @@ { "description": "Transaction w/ P2SH(P2PK) -> P2PKH", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006c47304402201115644b134932c8a7a8e925769d130a801288d477130e2bf6fadda20b33754d02202ecefbf63844d7cb2d5868539c39f973fe019f72e5c31a707836c0d61ef317db012321033e29aea1168a835d5e386c292082db7b7807172a10ec634ad34226f36d79e70facffffffff0100f90295000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -250,6 +260,7 @@ { "description": "Transaction w/ non-zero vin inputs", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205c80bbb5125b35d5e5a8324b1336832d29a6fc004859c8a9ff6bef47ba7fc348022018612216e57a521b2c4543f1f4fd738a76814c37c074e88adfe12464fff31cf901210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -295,6 +306,7 @@ { "description": "Transaction w/ nLockTime, P2PKH -> P2PKH", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006b483045022100e31ef1bcc6f060cb6a53675a11606b9180f4f8b1ec823113fb4c0bf1c5b99b8e02204234690c19cd89e544002d26dbcbd49bf9d1b4cfc5a617fd8ab2607acfd869b001210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588acffff0000", + "version": 1, "locktime": 65535, "inputs": [ { @@ -318,6 +330,7 @@ "description": "Transaction w/ 1 P2PKH transaction input (Issue #644)", "network": "testnet", "txHex": "010000000132595835c74fccf097db4ccae9dc2de621e58e0d3f697a27b469b61c7a223b39000000006a47304402202fd41f18f1d7915bc811610236e1d708f9cd3515734abd5db7ac607f030728af02206bee7d163f04c470ce55561523824eb8b3abce80c214aabf7dfb78a073ea4a70012103f29374a4c2c218a4077db9ba0b9d674cde3719560460af4eb3190d512dd5de92ffffffff01808d5b00000000001976a914ff99e06c1a4ac394b4e1cb3d3a4b2b47749e339a88ac00000000", + "version": 1, "inputs": [ { "txHex": "0100000001f7e6430096cd2790bac115aaab22c0a50fb0a1794305302e1a399e81d8d354f4020000006a47304402205793a862d193264afc32713e2e14541e1ff9ebb647dd7e7e6a0051d0faa87de302205216653741ecbbed573ea2fc053209dd6980616701c27be5b958a159fc97f45a012103e877e7deb32d19250dcfe534ea82c99ad739800295cd5429a7f69e2896c36fcdfeffffff0340420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac8bda2702000000001976a9145a0ef60784137d03e7868d063b05424f2f43799f88ac40420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac2fcc0e00", @@ -339,6 +352,7 @@ { "description": "Transaction w/ P2PKH -> P2WPKH", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006b4830450221009a524a3257b8bd0c77ba4cba65fc00954f2030243f2eb16571838a3b951c8c07022008f5af9de672b365fd257377db1cf6da4da1b49b9637ceb651ac0eb4181dc3ca01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff011027000000000000160014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c500000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -360,6 +374,7 @@ { "description": "Transaction w/ P2PKH -> P2WSH", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006a473044022056c99ba23eb15b3e666b188f87b04d5ef23eeda5298939cdaec35a3bddf3835602205887a5a460f299819b0c93948fafab8b2d64d8c051934431e3bb9acebef5d1b001210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01102700000000000022002032447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b8000000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -381,6 +396,7 @@ { "description": "Transaction w/ P2WPKH -> P2WPKH", "txHex": "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff011027000000000000160014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c502483045022100a8fc5e4c6d7073474eff2af5d756966e75be0cdfbba299518526080ce8b584be02200f26d41082764df89e3c815b8eaf51034a3b68a25f1be51208f54222c1bb6c1601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", + "version": 1, "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -404,6 +420,7 @@ { "description": "Transaction w/ P2WSH(P2PK) -> P2PKH", "txHex": "010000000001014533a3bc1e039bd787656068e135aaee10aee95a64776bfc047ee6a7c1ebdd2f0000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac02473044022039725bb7291a14dd182dafdeaf3ea0d5c05c34f4617ccbaa46522ca913995c4e02203b170d072ed2e489e7424ad96d8fa888deb530be2d4c5d9aaddf111a7efdb2d3012321038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bac00000000", + "version": 1, "inputs": [ { "txId": "2fddebc1a7e67e04fc6b77645ae9ae10eeaa35e168606587d79b031ebca33345", @@ -428,6 +445,7 @@ { "description": "Sighash: SINGLE (random)", "txHex": "01000000012ffb29d53528ad30c37c267fbbeda3c6fce08f5f6f5d3b1eab22193599a3612a010000006b483045022100f963f1d9564075a934d7c3cfa333bd1378859b84cba947e149926fc9ec89b5ae02202b5b912e507bae65002aff972f9752e2aeb2e22c5fdbaaad672090378184df37032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff0260a62f01000000001976a9140de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b888ac80969800000000001976a91454d0e925d5ee0ee26768a237067dee793d01a70688ac00000000", + "version": 1, "inputs": [ { "txId": "2a61a399351922ab1e3b5d6f5f8fe0fcc6a3edbb7f267cc330ad2835d529fb2f", @@ -457,6 +475,7 @@ { "description": "Sighash: ALL", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006a47304402206abb0622b8b6ca83f1f4de84830cf38bf4615dc9e47a7dcdcc489905f26aa9cb02201d2d8a7815242b88e4cd66390ca46da802238f9b1395e0d118213d30dad38184012102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006b483045022100de13b42804f87a09bb46def12ab4608108d8c2db41db4bc09064f9c46fcf493102205e5c759ab7b2895c9b0447e56029f6895ff7bb20e0847c564a88a3cfcf080c4f012102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b4830450221009100a3f5b30182d1cb0172792af6947b6d8d42badb0539f2c209aece5a0628f002200ae91702ca63347e344c85fcb536f30ee97b75cdf4900de534ed5e040e71a548012102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", + "version": 1, "inputs": [ { "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", @@ -516,6 +535,7 @@ { "description": "Sighash: ALL | ANYONECANPAY", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100bd2829550e9b3a081747281029b5f5a96bbd83bb6a92fa2f8310f1bd0d53abc90220071b469417c55cdb3b04171fd7900d2768981b7ab011553d84d24ea85d277079812102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a47304402206295e17c45c6356ffb20365b696bcbb869db7e8697f4b8a684098ee2bff85feb02202905c441abe39ec9c480749236b84fdd3ebd91ecd25b559136370aacfcf2815c812102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b483045022100f58e7c98ac8412944d575bcdece0e5966d4018f05988b5b60b6f46b8cb7a543102201c5854d3361e29b58123f34218cec2c722f5ec7a08235ebd007ec637b07c193a812102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", + "version": 1, "inputs": [ { "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", @@ -575,6 +595,7 @@ { "description": "Sighash: SINGLE", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100e822f152bb15a1d623b91913cd0fb915e9f85a8dc6c26d51948208bbc0218e800220255f78549d9614c88eac9551429bc00224f22cdcb41a3af70d52138f7e98d333032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a47304402206f37f79adeb86e0e2da679f79ff5c3ba206c6d35cd9a21433f0de34ee83ddbc00220118cabbac5d83b3aa4c2dc01b061e4b2fe83750d85a72ae6a1752300ee5d9aff032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006a473044022042ac843d220a56b3de05f24c85a63e71efa7e5fc7c2ec766a2ffae82a88572b0022051a816b317313ea8d90010a77c3e02d41da4a500e67e6a5347674f836f528d82032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", + "version": 1, "inputs": [ { "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", @@ -634,6 +655,7 @@ { "description": "Sighash: SINGLE|ANYONECANPAY", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100d05a3b6cf2f0301000b0e45c09054f2c61570ce8798ebf571eef72da3b1c94a1022016d7ef3c133fa703bae2c75158ea08d335ac698506f99b3c369c37a9e8fc4beb832102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006b483045022100ee6bf07b051001dcbfa062692a40adddd070303286b714825b3fb4693dd8fcdb022056610885e5053e5d47f2be3433051305abe7978ead8f7cf2d0368947aff6b307832102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b483045022100cfc930d5b5272d0220d9da98fabec97b9e66306f735efa837f43f6adc675cad902202f9dff76b8b9ec8f613d46094f17f64d875804292d8804aa59fd295b6fc1416b832102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", + "version": 1, "inputs": [ { "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", @@ -693,6 +715,7 @@ { "description": "Sighash: NONE", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100e7f0a1ddd2c0b81e093e029b8a503afa27fe43549b0668d2141abf35eb3a63be022037f12d12cd50fc94a135f933406a8937557de9b9566a8841ff1548c1b6984531022102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a473044022008451123ec2535dab545ade9d697519e63b28df5e311ea05e0ce28d39877a7c8022061ce5dbfb7ab478dd9e05b0acfd959ac3eb2641f61958f5d352f37621073d7c0022102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006a47304402205c001bcdfb35c70d8aa3bdbc75399afb72eb7cf1926ca7c1dfcddcb4d4d3e0f8022028992fffdcd4e9f34ab726f97c24157917641c2ef99361f588e3d4147d46eea5022102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", + "version": 1, "inputs": [ { "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", @@ -752,6 +775,7 @@ { "description": "Sighash: NONE | ANYONECANPAY", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006a47304402204ed272952177aaa5a1b171c2ca5a7a3d300ffcd7e04b040c0baaa4e3561862a502207e65a5b8f99c8a632b186c8a60496a12bf3116f51909b7497413aefdc3be7bf6822102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a47304402203ec365300cc67602f4cc5be027959d3667b48db34c6c87d267c94a7e210d5c1f02204843350311c0a9711cad1960b17ce9e323a1ce6f37deefc3ffe63082d480be92822102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b48304502210084f86f905c36372eff9c54ccd509a519a3325bcace8abfeed7ed3f0d579979e902201ff330dd2402e5ca9989a8a294fa36d6cf3a093edb18d29c9d9644186a3efeb4822102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", + "version": 1, "inputs": [ { "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", @@ -811,6 +835,7 @@ { "description": "Sighash V1: ALL", "txHex": "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000484730440220691a19d365c8d75f921346c70271506bde136f13a4b566dd796902c262e2ec6d02202b00c4aa030eedf294552bdfc163936d2f4e91c59e7798c4471250cf07cb859501eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff0230f45e13000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac00e9a435000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac0002483045022100fddd014889f18d489b5400bfa8cb0a32301a768d934b1a0e2b55398119f26cab02207676c64c16ffa7ffaaf8e16b3b74e916687eebdfdb36b9b7997e838384d464640121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000", + "version": 1, "inputs": [ { "txId": "9f96ade4b41d5433f4eda31e1738ec2b36f6e7d1420d94a6af99801a88f7f7ff", @@ -854,6 +879,7 @@ { "description": "Sighash V1: ALL 2", "txHex": "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000", + "version": 1, "inputs": [ { "txId": "77541aeb3c4dac9260b68f74f44c973081a9d4cb2ebe8038b2d70faa201b6bdb", @@ -885,6 +911,7 @@ { "description": "Sighash V1: P2MS 6/6", "txHex": "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff02e6312761010000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac583e0f00000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac0800483045022100f902f491c4df15199e584790ae8c7202569a977accac0a09fa3f4f3b6ec3517602205961a951c4a12fa966da67b6fd75975b9de156b9895f8ab5f289ecaee12b9b3501473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502483045022100bd5294e145d729e9593f49079b74e6e4b8aeba63440408595ce0949d5c6450a702207f9c9fb45907fe0180d3f4bee499006007bb90894b5f824a26dfa5d3afec543303483045022100febf9409d7f3c091ddc4d296a483aae7b3d2a91d38f6ea2a153f7ff085fe7766022078d11972c74cd78f816152463a5e1a5d986dfb94b55cf5f7242e4f6d5df000ff81483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a088247304402201a0e125aed6a700e45d6c86017d5a9d2264c8079319d868f3f163f5d63cb5bfe02200887608f2322ca0d82df67316275371028b0b21750417d594117963fe23b67ec83cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000", + "version": 1, "inputs": [ { "txId": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", @@ -952,6 +979,7 @@ { "description": "P2PK", "txHex": "010000000193aef40ae141694895e99e18e49d0181b086dd7c011c0241175c6eaf320099970000000049483045022100e57eba5380dcc8a7bdb5370b423dadd43070e1ca268f94bc97b2ded55ca45e9502206a43151c8af03a00f0ac86526d07981e303fc0daea8c6ed435abe8961533046d01ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", + "version": 1, "inputs": [ { "txId": "97990032af6e5c1741021c017cdd86b081019de4189ee995486941e10af4ae93", @@ -978,6 +1006,7 @@ { "description": "P2SH(P2PK)", "txHex": "0100000001a30e865fa60f6c25a8b218bb5a6b9acc7cf3f1db2f2e3a7114b51af5d6ae811f000000006c473044022026d2b56b6cb0269bf4e80dd655b9e917019e2ccef57f4b858d03bb45a2da59d9022010519a7f327f03e7c9613e0694f929544af29d3682e7ec8f19147e7a86651ecd012321038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bacffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", + "version": 1, "inputs": [ { "txId": "1f81aed6f51ab514713a2e2fdbf1f37ccc9a6b5abb18b2a8256c0fa65f860ea3", @@ -1005,6 +1034,7 @@ { "description": "P2WSH(P2PK)", "txHex": "010000000001014533a3bc1e039bd787656068e135aaee10aee95a64776bfc047ee6a7c1ebdd2f0000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac02473044022039725bb7291a14dd182dafdeaf3ea0d5c05c34f4617ccbaa46522ca913995c4e02203b170d072ed2e489e7424ad96d8fa888deb530be2d4c5d9aaddf111a7efdb2d3012321038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bac00000000", + "version": 1, "inputs": [ { "txId": "2fddebc1a7e67e04fc6b77645ae9ae10eeaa35e168606587d79b031ebca33345", @@ -1032,6 +1062,7 @@ { "description": "P2SH(P2WSH(P2PK))", "txHex": "01000000000101e0779d448aaa203a96b3de14d0482e26dd75a4278ae5bb6d7cc18e6874f3866000000000232200200f9ea7bae7166c980169059e39443ed13324495b0d6678ce716262e879591210ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac024730440220014207a5f0601ed7b3c3f9d82309b32e8f76dd6776a55cb5f8684b9ff029e0850220693afd7b69471b51d9354cc1a956b68b8d48e32f6b0ad7a19bb5dd3e4499179a012321038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bac00000000", + "version": 1, "inputs": [ { "txId": "6086f374688ec17c6dbbe58a27a475dd262e48d014deb3963a20aa8a449d77e0", @@ -1060,6 +1091,7 @@ { "description": "P2PKH", "txHex": "010000000176d7b05b96e69d9760bacf14e496ea01085eff32be8f4e08b299eb92057826e5000000006b4830450221009bd6ff2561437155913c289923175d3f114cca1c0e2bc5989315d5261502c2c902201b71ad90dce076a5eb872330ed729e7c2c4bc2d0513efff099dbefb3b62eab4f0121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", + "version": 1, "inputs": [ { "txId": "e526780592eb99b2084e8fbe32ff5e0801ea96e414cfba60979de6965bb0d776", @@ -1086,6 +1118,7 @@ { "description": "P2SH(P2PKH)", "txHex": "01000000014b9ffc17c3cce03ee66980bf32d36aaa13462980c3af9d9d29ec6b97ab1c91650000000084473044022003d738d855d0c54a419ac62ebe1a1c0bf2dc6993c9585adb9a8666736658107002204d57ff62ee7efae6df73430bba62494faeba8c125a4abcf2488757a4f8877dd50121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b1976a914851a33a5ef0d4279bd5854949174e2c65b1d450088acffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", + "version": 1, "inputs": [ { "txId": "65911cab976bec299d9dafc380294613aa6ad332bf8069e63ee0ccc317fc9f4b", @@ -1113,6 +1146,7 @@ { "description": "P2WSH(P2PKH)", "txHex": "0100000000010123539877e39a273819006de1c433e09f9e9af201fc178dd0f2cf2eaa5ad53b480000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac03483045022100f02a82b0a94a5d5dc4d2127ac34be62cb066713d71d56bdf5ef7810ab57a157302205f24abdde1dab554a02edcf378e98828024e57272e5e474a5b04accdca080a030121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b1976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", + "version": 1, "inputs": [ { "txId": "483bd55aaa2ecff2d08d17fc01f29a9e9fe033c4e16d001938279ae377985323", @@ -1140,6 +1174,7 @@ { "description": "P2SH(P2WSH(P2PKH))", "txHex": "01000000000101363dfbfe2566db77e3b1195bedf1d0daeb9ce526cd7611ba81759b2654ce415c0000000023220020578db4b54a6961060b71385c17d3280379a557224c52b11b19a3a1c1eef606a0ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac03483045022100c8bd5ebb26ba6719158650c3e7c5e80be4c886ba025c44cc41f5149b3114705a02203ac6e1f38f6c081d506f28f1b5e38ebec9e0f0fa911d0e3f68d48d8b0e77b34b0121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b1976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", + "version": 1, "inputs": [ { "txId": "5c41ce54269b7581ba1176cd26e59cebdad0f1ed5b19b1e377db6625fefb3d36", @@ -1168,6 +1203,7 @@ { "description": "P2MS 1/1", "txHex": "010000000179310ec46e734b3490ee839c5ae4a09d28561ee9fff2d051f733d201f958d6d2000000004a00483045022100d269531f120f377ed2f94f42bef893ff2fe6544ac97fb477fa291bc6cfb7647e02200983f6a5bbd4ce6cf97f571995634805a7324cc5d8353ed954fa62477b0fcd0901ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", + "version": 1, "inputs": [ { "txId": "d2d658f901d233f751d0f2ffe91e56289da0e45a9c83ee90344b736ec40e3179", @@ -1194,6 +1230,7 @@ { "description": "P2SH(P2MS)", "txHex": "010000000152882c661c49dd2f53bd9ced7e9f44b184888ad2fe7d86737f0efaa7aecdced1000000006f00473044022025f2e161f0a97888df948f4dcc7c04fe502510b8d8260ca9920f38d55e4d17720220271b6843224b3a34542a4df31781d048da56ee46b8c5fb99043e30abd527b2d801255121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b51aeffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", + "version": 1, "inputs": [ { "txId": "d1cecdaea7fa0e7f73867dfed28a8884b1449f7eed9cbd532fdd491c662c8852", @@ -1221,6 +1258,7 @@ { "description": "P2WSH(P2MS)", "txHex": "01000000000101c1eced6216de0889d4629ff64a8af8e8ec6d0b414de0c57b46c02cc303d321fe0000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac0300483045022100d4c0cbdb45915b8a3162362fa5f74556de919aeda5337fc44a7fb000e833460d022017742c37d7a061e2ae3a086c7c585c9c85e5d31af468d3e00045c0f35b8f8eb601255121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b51ae00000000", + "version": 1, "inputs": [ { "txId": "fe21d303c32cc0467bc5e04d410b6dece8f88a4af69f62d48908de1662edecc1", @@ -1248,6 +1286,7 @@ { "description": "P2SH(P2WSH(P2MS))", "txHex": "010000000001013a5a2ab0223d3b504b52af76d650329750666fbf1be13d4cb08d0d9fc550a47d00000000232200201b8c0c2878c5634c3ce738cdc568c592e99783dbd28ff4c6cb5b7b4675d9ee99ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac0300483045022100c97a5e205ce0023d3d44f846abf1f0e21b6f2646bd2496bbe92e4333fe4401be02201247e047d669f713582713e35d2eba430abc3d75a924bb500362bf47d6234ed501255121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b51ae00000000", + "version": 1, "inputs": [ { "txId": "7da450c59f0d8db04c3de11bbf6f6650973250d676af524b503b3d22b02a5a3a", @@ -1274,8 +1313,9 @@ "locktime": 0 }, { - "description": "P2WKH", + "description": "P2WPKH", "txHex": "0100000000010133defbe3e28860007ff3e21222774c220cb35d554fa3e3796d25bf8ee983e1080000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac0248304502210097c3006f0b390982eb47f762b2853773c6cedf83668a22d710f4c13c4fd6b15502205e26ef16a81fc818a37f3a34fc6d0700e61100ea6c6773907c9c046042c440340121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b00000000", + "version": 1, "inputs": [ { "txId": "08e183e98ebf256d79e3a34f555db30c224c772212e2f37f006088e2e3fbde33", @@ -1300,8 +1340,9 @@ "locktime": 0 }, { - "description": "P2SH(P2WKH)", + "description": "P2SH(P2WPKH)", "txHex": "010000000001015df9a0b9ade2d835881704e0f53b51a4b19ecfc794ea1f3555783dd7f68659ce0000000017160014851a33a5ef0d4279bd5854949174e2c65b1d4500ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac02483045022100cb3929c128fec5108071b662e5af58e39ac8708882753a421455ca80462956f6022030c0f4738dd1a13fc7a34393002d25c6e8a6399f29c7db4b98f53a9475d94ca20121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b00000000", + "version": 1, "inputs": [ { "txId": "ce5986f6d73d7855351fea94c7cf9eb1a4513bf5e004178835d8e2adb9a0f95d", @@ -1326,10 +1367,34 @@ ], "locktime": 0 }, + { + "description": "Defaults to version 2", + "txHex": "0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006b483045022100d090736fce4387d25f9d7ba85270a3859947dbd0a19f347467c3a596e06c132402204b14c29d128f824bee953bdaf2ed4e16c7cff0b24bddb4add94b4b6c42773d8801210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": null, + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "signs": [ + { + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + } + ] + } + ], + "outputs": [ + { + "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", + "value": 10000 + } + ], + "locktime": 0 + }, { "description": "P2SH(P2WSH(P2MS 2/2)), incomplete", "network": "testnet", "txHex": "010000000001012915794541ffa77ca795ec7c23ee989a63ccd1a71fab73e1c27ed20c4b6c69a4010000002322002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5afffffffff01b8820100000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac040047304402203b334650f1f13574a1c2edc76421867f7252950968bf0293c8b3ed086ab89e3d0220565cafab0a5044617e94756b948241525b2483a52504e1064d29f641fb18129e010047522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae00000000", + "version": 1, "incomplete": true, "inputs": [ { @@ -1357,6 +1422,7 @@ "description": "P2SH(P2WSH(P2MS 2/2))", "network": "testnet", "txHex": "010000000001012915794541ffa77ca795ec7c23ee989a63ccd1a71fab73e1c27ed20c4b6c69a4010000002322002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5afffffffff01b8820100000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac040047304402203b334650f1f13574a1c2edc76421867f7252950968bf0293c8b3ed086ab89e3d0220565cafab0a5044617e94756b948241525b2483a52504e1064d29f641fb18129e0148304502210096e859827fb629b6547658c613f7c8298de151513d74b224560aa8608d521d600220736fb5564322237716ec940de44c67c428198adf5dedfda183c17aa77cd28d640147522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae00000000", + "version": 1, "incomplete": true, "inputs": [ { @@ -1390,6 +1456,7 @@ "description": "P2WSH(P2MS 2/3) -> P2PKH", "network": "testnet", "txHex": "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01000000232200201b48bf145648b9492ecd6d76754ea3def4b90e22e4ef7aee9ca291b2de455701ffffffff01f07e0e00000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac0400473044022036c9ecb03cb04c09be1f52766725dcfe9a815973bd2f34ce19a345f2d925a45502207b90737852d2508db104ad17612de473687e67928c045555a1ed8d495c0570d901483045022100aec0e58e4e597b35ca5a727702a0da3d4f2ef4759914da7fc80aecb3c479a6d902201ec27ea8dcca4b73ee81e4b627f52f9e627c3497f61e4beeb98f86e02979640a0169522103c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc5787721020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b02102d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc914953ae00000000", + "version": 1, "stages": [ "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01000000232200201b48bf145648b9492ecd6d76754ea3def4b90e22e4ef7aee9ca291b2de455701ffffffff01f07e0e00000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac0500473044022036c9ecb03cb04c09be1f52766725dcfe9a815973bd2f34ce19a345f2d925a45502207b90737852d2508db104ad17612de473687e67928c045555a1ed8d495c0570d901000069522103c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc5787721020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b02102d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc914953ae00000000" ], @@ -1487,6 +1554,8 @@ { "description": "P2SH(P2MS 2/2), signed in correct order", "network": "testnet", + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -1511,12 +1580,13 @@ "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "value": 1000 } - ], - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + ] }, { "description": "P2SH(P2MS 2/2), signed in shuffled order", "network": "testnet", + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -1541,12 +1611,13 @@ "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "value": 1000 } - ], - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + ] }, { "description": "P2SH(P2MS 2/2), manually messed up order of signatures", "network": "testnet", + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -1571,12 +1642,13 @@ "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "value": 1000 } - ], - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + ] }, { "description": "P2SH(P2MS 2/3), signed by key 1 and 2", "network": "testnet", + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100ae06d8269b79b5cfa0297d1d88261b0061e52fc2814948c3aa05fa78ee76894302206e0c79a5c90569d8c72a542ef9a06471cbbcd2c651b312339983dfba4f8ff463014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -1601,12 +1673,13 @@ "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "value": 1000 } - ], - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100ae06d8269b79b5cfa0297d1d88261b0061e52fc2814948c3aa05fa78ee76894302206e0c79a5c90569d8c72a542ef9a06471cbbcd2c651b312339983dfba4f8ff463014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + ] }, { "description": "P2SH(P2MS 2/3), signed by key 1 and 3", "network": "testnet", + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -1631,12 +1704,13 @@ "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "value": 1000 } - ], - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + ] }, { "description": "P2SH(P2MS 2/3), signed by key 3 and 1", "network": "testnet", + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -1661,12 +1735,13 @@ "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "value": 1000 } - ], - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + ] }, { "description": "P2SH(P2MS 2/3), signed by key 1 and 3, manually messed up order of signatures", "network": "testnet", + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -1691,12 +1766,13 @@ "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "value": 1000 } - ], - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + ] }, { "description": "P2SH(P2MS 2/3), signed by key 3 and 1, manually removing OP_0s", "network": "testnet", + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "version": 1, "inputs": [ { "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", @@ -1723,8 +1799,7 @@ "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "value": 1000 } - ], - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + ] } ] }, diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 56ef033..4c1cc26 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -14,6 +14,7 @@ describe('bitcoinjs-lib (transactions)', function () { var alice = bitcoin.ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') var txb = new bitcoin.TransactionBuilder() + txb.setVersion(1) txb.addInput('61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', 0) // Alice's previous transaction output, has 15000 satoshis txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000) // (in)15000 - (out)12000 = (fee)3000, this is the miner fee @@ -29,6 +30,7 @@ describe('bitcoinjs-lib (transactions)', function () { var bob = bitcoin.ECPair.fromWIF('KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z') var txb = new bitcoin.TransactionBuilder() + txb.setVersion(1) txb.addInput('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c', 6) // Alice's previous transaction output, has 200000 satoshis txb.addInput('7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730', 0) // Bob's previous transaction output, has 300000 satoshis txb.addOutput('1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb', 180000) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index f2325a4..35da882 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -18,7 +18,7 @@ function construct (f, dontSign) { var network = NETWORKS[f.network] var txb = new TransactionBuilder(network) - if (f.version !== undefined) txb.setVersion(f.version) + if (Number.isFinite(f.version)) txb.setVersion(f.version) if (f.locktime !== undefined) txb.setLockTime(f.locktime) f.inputs.forEach(function (input) { @@ -301,6 +301,7 @@ describe('TransactionBuilder', function () { } var txb = new TransactionBuilder() + txb.setVersion(1) txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) txb.addOutput('1111111111111111111114oLvT2', 100000) txb.sign(0, keyPair) @@ -520,6 +521,7 @@ describe('TransactionBuilder', function () { var redeemScript = Buffer.from('002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', 'hex') var scriptPubKey = Buffer.from('a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', 'hex') var txb = new TransactionBuilder(network) + txb.setVersion(1) txb.addInput('a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', 1, 0xffffffff, scriptPubKey) txb.addOutput(scriptPubKey, 99000) txb.sign(0, keyPair, redeemScript, null, 100000, witnessScript) @@ -554,23 +556,24 @@ describe('TransactionBuilder', function () { }) it('should not classify blank scripts as nonstandard', function () { - var tx = new TransactionBuilder() - tx.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) + var txb = new TransactionBuilder() + txb.setVersion(1) + txb.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) - var incomplete = tx.buildIncomplete().toHex() + var incomplete = txb.buildIncomplete().toHex() var keyPair = ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') // sign, as expected - tx.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) - tx.sign(0, keyPair) - var txId = tx.build().getId() + txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) + txb.sign(0, keyPair) + var txId = txb.build().getId() assert.equal(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') // and, repeat - tx = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) - tx.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) - tx.sign(0, keyPair) - var txId2 = tx.build().getId() + txb = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) + txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) + txb.sign(0, keyPair) + var txId2 = txb.build().getId() assert.equal(txId, txId2) }) }) From b17705006913efdfed39274d94665908ea4bea79 Mon Sep 17 00:00:00 2001 From: Prahalad Belavadi <prahaladbelavadi@gmail.com> Date: Sat, 21 Apr 2018 16:52:27 +0530 Subject: [PATCH 011/568] add warning for generating addresses from sha256 hash example --- package-lock.json | 4608 +++++++++++++++++++++++++++++++++ test/integration/addresses.js | 3 +- 2 files changed, 4610 insertions(+), 1 deletion(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..49be3bf --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4608 @@ +{ + "name": "bitcoinjs-lib", + "version": "3.3.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array.prototype.find": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz", + "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.11.0" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base-x": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.4.tgz", + "integrity": "sha512-UYOadoSIkEI/VrRGSG6qp93rp2WdokiAiNYDfGW5qURAY8GiAQkvMbwNNSDYiVJopqv4gCna7xqf4rrNGp+5AA==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "bech32": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.3.tgz", + "integrity": "sha512-yuVFUvrNcoJi0sv5phmqc6P+Fl1HjRDRNOOkHY2X/3LBy2bIGNSFx4fZ95HMaXHupuS7cZR15AsvtmCIF4UEyg==" + }, + "bigi": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz", + "integrity": "sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU=" + }, + "bip39": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-2.5.0.tgz", + "integrity": "sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA==", + "dev": true, + "requires": { + "create-hash": "1.2.0", + "pbkdf2": "3.0.16", + "randombytes": "2.0.6", + "safe-buffer": "5.1.1", + "unorm": "1.4.1" + } + }, + "bip65": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bip65/-/bip65-1.0.3.tgz", + "integrity": "sha512-RQ1nc7xtnLa5XltnCqkoR2zmhuz498RjMJwrLKQzOE049D1HUqnYfon7cVSbwS5UGm0/EQlC2CH+NY3MyITA4Q==", + "dev": true + }, + "bip66": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "bitcoin-ops": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz", + "integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "3.0.4" + } + }, + "bs58check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.1.tgz", + "integrity": "sha512-okRQiWc5FJuA2VOwQ1hB7Sf0MyEFg/EwRN12h4b8HrJoGkZ3xq1CGjkaAfYloLcZyqixQnO5mhPpN6IcHSplVg==", + "requires": { + "bs58": "4.0.1", + "create-hash": "1.2.0" + } + }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "dev": true + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "1.0.1" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "1.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "typedarray": "0.0.6" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "1.0.4", + "inherits": "2.0.3", + "md5.js": "1.3.4", + "ripemd160": "2.0.2", + "sha.js": "2.4.11" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "inherits": "2.0.3", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.1", + "sha.js": "2.4.11" + } + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "dev": true, + "requires": { + "es5-ext": "0.10.42" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + } + }, + "deglob": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.0.tgz", + "integrity": "sha1-TUSr4W7zLHebSXK9FBqAMlApoUo=", + "dev": true, + "requires": { + "find-root": "1.1.0", + "glob": "7.1.2", + "ignore": "3.3.7", + "pkg-config": "1.1.1", + "run-parallel": "1.1.8", + "uniq": "1.0.1" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" + } + }, + "dhttp": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/dhttp/-/dhttp-2.4.2.tgz", + "integrity": "sha512-tuip8Xn15O5adhKh3XCCUbGq8+hZPATbsgdg8lw32fPwk/wkzCBu61H26VQBfOQnrHqcO0YWdprDXMRLks0vzQ==", + "dev": true, + "requires": { + "http-status-codes": "1.3.0", + "parse-headers": "2.0.1" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "2.0.2" + } + }, + "ecurve": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/ecurve/-/ecurve-1.0.6.tgz", + "integrity": "sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w==", + "requires": { + "bigi": "1.4.2", + "safe-buffer": "5.1.1" + } + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es-abstract": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", + "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } + }, + "es5-ext": { + "version": "0.10.42", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", + "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", + "dev": true, + "requires": { + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1", + "next-tick": "1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-symbol": "3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", + "es6-set": "0.1.5", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42", + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "dev": true, + "requires": { + "es6-map": "0.1.5", + "es6-weak-map": "2.0.2", + "esrecurse": "4.2.1", + "estraverse": "4.2.0" + } + }, + "eslint": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.18.0.tgz", + "integrity": "sha1-ZH6YXErnFQLSCsYsEJ9m1RBMiks=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "chalk": "1.1.3", + "concat-stream": "1.6.2", + "debug": "2.6.9", + "doctrine": "2.1.0", + "escope": "3.6.0", + "espree": "3.5.4", + "esquery": "1.0.1", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "glob": "7.1.2", + "globals": "9.18.0", + "ignore": "3.3.7", + "imurmurhash": "0.1.4", + "inquirer": "0.12.0", + "is-my-json-valid": "2.17.2", + "is-resolvable": "1.1.0", + "js-yaml": "3.11.0", + "json-stable-stringify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.5", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "1.2.1", + "progress": "1.1.8", + "require-uncached": "1.0.3", + "shelljs": "0.7.8", + "strip-bom": "3.0.0", + "strip-json-comments": "2.0.1", + "table": "3.8.3", + "text-table": "0.2.0", + "user-home": "2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "eslint-config-standard": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-7.1.0.tgz", + "integrity": "sha1-R+dp6gc59bLVaTsaUBwhyWUPr88=", + "dev": true + }, + "eslint-config-standard-jsx": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-3.3.0.tgz", + "integrity": "sha1-yrCAGhWjYL9j+suXqyL73YjYpeA=", + "dev": true + }, + "eslint-plugin-promise": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.4.2.tgz", + "integrity": "sha1-G+J5Pq/i0YtbEjuBNsJp+AT+cSI=", + "dev": true + }, + "eslint-plugin-react": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.9.0.tgz", + "integrity": "sha1-VMLpkGt2+dEBQgML3DTp1oQKC7I=", + "dev": true, + "requires": { + "array.prototype.find": "2.0.4", + "doctrine": "1.5.0", + "jsx-ast-utils": "1.4.1" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + } + } + }, + "eslint-plugin-standard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-2.0.1.tgz", + "integrity": "sha1-NYlpn/nJF/LCX3apFmh/ZBw2n/M=", + "dev": true + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "5.5.3", + "acorn-jsx": "3.0.1" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.42" + } + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "1.3.0", + "object-assign": "4.1.1" + } + }, + "fill-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", + "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", + "dev": true, + "requires": { + "is-object": "1.0.1", + "merge-descriptors": "1.0.1" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, + "for-each": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", + "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=", + "dev": true, + "requires": { + "is-function": "1.0.1" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "1.0.2" + } + }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "hoodwink": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hoodwink/-/hoodwink-1.0.0.tgz", + "integrity": "sha512-BcnI8v1qTuvQ9ngAdgkdaaC9b6IrAsPloMGEkatPuryG1GF57VH1EacnrhDZ8QL2+8JqK8VOR97STS5JdWWNew==", + "dev": true + }, + "http-status-codes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.3.0.tgz", + "integrity": "sha1-nNDnE5F3PQZxtInUHLxQlKpBY7Y=", + "dev": true + }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "inquirer": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true, + "requires": { + "ansi-escapes": "1.4.0", + "ansi-regex": "2.1.1", + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-width": "2.2.0", + "figures": "1.7.0", + "lodash": "4.17.5", + "readline2": "1.0.1", + "run-async": "0.1.0", + "rx-lite": "3.1.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "through": "2.3.8" + } + }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", + "dev": true + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", + "dev": true + }, + "is-my-json-valid": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", + "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", + "dev": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "is-my-ip-valid": "1.0.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "1.0.1" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "1.0.1" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", + "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "jsx-ast-utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", + "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "4.0.0", + "pify": "3.0.0", + "strip-bom": "3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + } + }, + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", + "dev": true + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merkle-lib": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", + "integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY=" + }, + "minimaldata": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minimaldata/-/minimaldata-1.0.2.tgz", + "integrity": "sha1-AfOywB2LJzmEP9hT1AY2xaLrdms=", + "dev": true, + "requires": { + "bitcoin-ops": "1.4.1", + "pushdata-bitcoin": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz", + "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + } + }, + "module-not-found-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", + "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nyc": { + "version": "11.7.1", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.7.1.tgz", + "integrity": "sha512-EGePURSKUEpS1jWnEKAMhY+GWZzi7JC+f8iBDOATaOsLZW5hM/9eYx2dHGaEXa1ITvMm44CJugMksvP3NwMQMw==", + "dev": true, + "requires": { + "archy": "1.0.0", + "arrify": "1.0.1", + "caching-transform": "1.0.1", + "convert-source-map": "1.5.1", + "debug-log": "1.0.1", + "default-require-extensions": "1.0.0", + "find-cache-dir": "0.1.1", + "find-up": "2.1.0", + "foreground-child": "1.5.6", + "glob": "7.1.2", + "istanbul-lib-coverage": "1.2.0", + "istanbul-lib-hook": "1.1.0", + "istanbul-lib-instrument": "1.10.1", + "istanbul-lib-report": "1.1.3", + "istanbul-lib-source-maps": "1.2.3", + "istanbul-reports": "1.4.0", + "md5-hex": "1.3.0", + "merge-source-map": "1.1.0", + "micromatch": "2.3.11", + "mkdirp": "0.5.1", + "resolve-from": "2.0.0", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "spawn-wrap": "1.4.2", + "test-exclude": "4.2.1", + "yargs": "11.1.0", + "yargs-parser": "8.1.0" + }, + "dependencies": { + "align-text": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + } + }, + "amdefine": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "append-transform": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "requires": { + "default-require-extensions": "1.0.0" + } + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "bundled": true, + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "arrify": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "async": { + "version": "1.5.2", + "bundled": true, + "dev": true + }, + "atob": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "babel-generator": { + "version": "6.26.1", + "bundled": true, + "dev": true, + "requires": { + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.5", + "source-map": "0.5.7", + "trim-right": "1.0.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "core-js": "2.5.5", + "regenerator-runtime": "0.11.1" + } + }, + "babel-template": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.5" + } + }, + "babel-traverse": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.4", + "lodash": "4.17.5" + } + }, + "babel-types": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.5", + "to-fast-properties": "1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "base": { + "version": "0.11.2", + "bundled": true, + "dev": true, + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "bundled": true, + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" + } + }, + "builtin-modules": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "caching-transform": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "md5-hex": "1.3.0", + "mkdirp": "0.5.1", + "write-file-atomic": "1.3.4" + } + }, + "camelcase": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true + }, + "center-align": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chalk": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "class-utils": { + "version": "0.3.6", + "bundled": true, + "dev": true, + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "cliui": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "map-visit": "1.0.0", + "object-visit": "1.0.1" + } + }, + "commondir": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "convert-source-map": { + "version": "1.5.1", + "bundled": true, + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "core-js": { + "version": "2.5.5", + "bundled": true, + "dev": true + }, + "cross-spawn": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "4.1.2", + "which": "1.3.0" + } + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "default-require-extensions": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "strip-bom": "2.0.0" + } + }, + "define-property": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "1.0.2", + "isobject": "3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "detect-indent": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "error-ex": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "esutils": { + "version": "2.0.2", + "bundled": true, + "dev": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "4.1.2", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + } + } + }, + "expand-brackets": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "expand-range": { + "version": "1.8.2", + "bundled": true, + "dev": true, + "requires": { + "fill-range": "2.2.3" + } + }, + "extend-shallow": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "extglob": { + "version": "0.3.2", + "bundled": true, + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "bundled": true, + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" + } + }, + "find-cache-dir": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "requires": { + "commondir": "1.0.1", + "mkdirp": "0.5.1", + "pkg-dir": "1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "for-in": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "for-own": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "foreground-child": { + "version": "1.5.6", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "4.0.2", + "signal-exit": "3.0.2" + } + }, + "fragment-cache": { + "version": "0.2.1", + "bundled": true, + "dev": true, + "requires": { + "map-cache": "0.2.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "get-value": { + "version": "2.0.6", + "bundled": true, + "dev": true + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "glob-base": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + } + }, + "glob-parent": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "globals": { + "version": "9.18.0", + "bundled": true, + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "handlebars": { + "version": "4.0.11", + "bundled": true, + "dev": true, + "requires": { + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "bundled": true, + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "has-ansi": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "has-value": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "has-values": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "hosted-git-info": { + "version": "2.6.0", + "bundled": true, + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "invariant": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "loose-envify": "1.3.1" + } + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-arrayish": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "bundled": true, + "dev": true + } + } + }, + "is-dotfile": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "is-number": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-odd": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-number": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "bundled": true, + "dev": true + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "isobject": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "istanbul-lib-coverage": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "append-transform": "0.4.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.10.1", + "bundled": true, + "dev": true, + "requires": { + "babel-generator": "6.26.1", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "istanbul-lib-coverage": "1.2.0", + "semver": "5.5.0" + } + }, + "istanbul-lib-report": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "path-parse": "1.0.5", + "supports-color": "3.2.3" + }, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.3", + "bundled": true, + "dev": true, + "requires": { + "debug": "3.1.0", + "istanbul-lib-coverage": "1.2.0", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "source-map": "0.5.7" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "istanbul-reports": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "handlebars": "4.0.11" + } + }, + "js-tokens": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "jsesc": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + }, + "lazy-cache": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "bundled": true, + "dev": true + } + } + }, + "lodash": { + "version": "4.17.5", + "bundled": true, + "dev": true + }, + "longest": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "loose-envify": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "js-tokens": "3.0.2" + } + }, + "lru-cache": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "map-cache": { + "version": "0.2.2", + "bundled": true, + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "object-visit": "1.0.1" + } + }, + "md5-hex": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "md5-o-matic": "0.1.1" + } + }, + "md5-o-matic": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "mimic-fn": "1.2.0" + } + }, + "merge-source-map": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "bundled": true, + "dev": true + } + } + }, + "micromatch": { + "version": "2.3.11", + "bundled": true, + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } + }, + "mimic-fn": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mixin-deep": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "for-in": "1.0.2", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "nanomatch": { + "version": "1.2.9", + "bundled": true, + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.4.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "2.6.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" + } + }, + "normalize-path": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "path-key": "2.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "bundled": true, + "dev": true, + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "object.omit": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "optimist": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.3" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "p-limit": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "p-try": "1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-limit": "1.2.0" + } + }, + "p-try": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "parse-glob": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + } + }, + "parse-json": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "requires": { + "error-ex": "1.3.1" + } + }, + "pascalcase": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "path-type": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "pify": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pkg-dir": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "find-up": "1.1.2" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "preserve": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "bundled": true, + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "read-pkg": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + } + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "bundled": true, + "dev": true + }, + "regex-cache": { + "version": "0.4.4", + "bundled": true, + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "bundled": true, + "dev": true + }, + "repeating": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-finite": "1.0.2" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "resolve-from": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "ret": { + "version": "0.1.15", + "bundled": true, + "dev": true + }, + "right-align": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "align-text": "0.1.4" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-regex": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "ret": "0.1.15" + } + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "set-value": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "slide": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "bundled": true, + "dev": true, + "requires": { + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.1", + "use": "3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "source-map": { + "version": "0.5.7", + "bundled": true, + "dev": true + }, + "source-map-resolve": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "atob": "2.1.0", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "bundled": true, + "dev": true + }, + "spawn-wrap": { + "version": "1.4.2", + "bundled": true, + "dev": true, + "requires": { + "foreground-child": "1.5.6", + "mkdirp": "0.5.1", + "os-homedir": "1.0.2", + "rimraf": "2.6.2", + "signal-exit": "3.0.2", + "which": "1.3.0" + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "split-string": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "3.0.2" + } + }, + "static-extend": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "requires": { + "define-property": "0.2.5", + "object-copy": "0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "test-exclude": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "requires": { + "arrify": "1.0.1", + "micromatch": "3.1.10", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "require-main-filename": "1.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "braces": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "bundled": true, + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "bundled": true, + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + } + } + }, + "to-fast-properties": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "to-regex": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + } + } + }, + "trim-right": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "union-value": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "set-value": { + "version": "0.4.3", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" + } + } + } + }, + "unset-value": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "has-value": "0.3.1", + "isobject": "3.0.1" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "bundled": true, + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "bundled": true, + "dev": true + }, + "use": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "validate-npm-package-license": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" + } + }, + "which": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "window-size": { + "version": "0.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "wordwrap": { + "version": "0.0.3", + "bundled": true, + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "slide": "1.1.6" + } + }, + "y18n": { + "version": "3.2.1", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "yargs": { + "version": "11.1.0", + "bundled": true, + "dev": true, + "requires": { + "cliui": "4.0.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "cliui": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "dev": true, + "requires": { + "camelcase": "4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "8.1.0", + "bundled": true, + "dev": true, + "requires": { + "camelcase": "4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "bundled": true, + "dev": true + } + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "dev": true, + "requires": { + "p-try": "1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "1.2.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-headers": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.1.tgz", + "integrity": "sha1-aug6eqJanZtwCswoaYzR8e1+lTY=", + "dev": true, + "requires": { + "for-each": "0.3.2", + "trim": "0.0.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "1.3.1", + "json-parse-better-errors": "1.0.2" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", + "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "dev": true, + "requires": { + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.1", + "sha.js": "2.4.11" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "dev": true, + "requires": { + "find-up": "2.1.0", + "load-json-file": "4.0.0" + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "dev": true, + "requires": { + "debug-log": "1.0.1", + "find-root": "1.1.0", + "xtend": "4.0.1" + } + }, + "pluralize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + }, + "proxyquire": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-1.8.0.tgz", + "integrity": "sha1-AtUUpb7ZhvBMuyCTrxZ0FTX3ntw=", + "dev": true, + "requires": { + "fill-keys": "1.0.2", + "module-not-found-error": "1.0.1", + "resolve": "1.1.7" + } + }, + "pushdata-bitcoin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz", + "integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=", + "requires": { + "bitcoin-ops": "1.4.1" + } + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "mute-stream": "0.0.5" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "1.1.7" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "1.1.1", + "onetime": "1.1.0" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.3" + } + }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "dev": true, + "requires": { + "once": "1.4.0" + } + }, + "run-parallel": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.8.tgz", + "integrity": "sha512-e5t1NVhr5VWmD9V9U4KjjSGkf5w6CcTPgw11A3CfIvkkQxlAKzX3usPUp1NUQTmpOOjU+f9QRICU3tMbKwn9ZQ==", + "dev": true + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "shelljs": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "dev": true, + "requires": { + "glob": "7.1.2", + "interpret": "1.1.0", + "rechoir": "0.6.2" + } + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "standard": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/standard/-/standard-9.0.2.tgz", + "integrity": "sha1-m9O5RnSS4hKxkU14VTlD/5tI/Zk=", + "dev": true, + "requires": { + "eslint": "3.18.0", + "eslint-config-standard": "7.1.0", + "eslint-config-standard-jsx": "3.3.0", + "eslint-plugin-promise": "3.4.2", + "eslint-plugin-react": "6.9.0", + "eslint-plugin-standard": "2.0.1", + "standard-engine": "5.4.0" + } + }, + "standard-engine": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-5.4.0.tgz", + "integrity": "sha1-4OhpWeoHhkJdM4PkDBv3DS+YVXk=", + "dev": true, + "requires": { + "deglob": "2.1.0", + "get-stdin": "5.0.1", + "home-or-tmp": "2.0.0", + "minimist": "1.2.0", + "pkg-conf": "2.1.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "table": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "dev": true, + "requires": { + "ajv": "4.11.8", + "ajv-keywords": "1.5.1", + "chalk": "1.1.3", + "lodash": "4.17.5", + "slice-ansi": "0.0.4", + "string-width": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typeforce": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.12.0.tgz", + "integrity": "sha512-fvnkvueAOFLhtAqDgIA/wMP21SMwS/NQESFKZuwVrj5m/Ew6eK2S0z0iB++cwtROPWDOhaT6OUfla8UwMw4Adg==" + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "unorm": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.4.1.tgz", + "integrity": "sha1-NkIA1fE2RsqLzURJAnEzVhR5IwA=", + "dev": true + }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "varuint-bitcoin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz", + "integrity": "sha512-jCEPG+COU/1Rp84neKTyDJQr478/hAfVp5xxYn09QEH0yBjbmPeMfuuQIrp+BUD83hybtYZKhr5elV3bvdV1bA==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", + "requires": { + "bs58check": "2.1.1" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "0.5.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } +} diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 74cd487..3b45836 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -22,7 +22,8 @@ describe('bitcoinjs-lib (addresses)', function () { var keyPair = new bitcoin.ECPair(d) var address = keyPair.getAddress() - + // Generating addresses from SHA256 hashes is not secure if the input to the hash function is predictable + // Do not use with predictable inputs assert.strictEqual(address, '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') }) From c3edc9929c6c55e8d0719b37e7646cfb8d1f72f8 Mon Sep 17 00:00:00 2001 From: Prahalad Belavadi <prahaladbelavadi@gmail.com> Date: Sun, 22 Apr 2018 17:47:48 +0530 Subject: [PATCH 012/568] remove package-lock.json --- package-lock.json | 4608 --------------------------------------------- 1 file changed, 4608 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 49be3bf..0000000 --- a/package-lock.json +++ /dev/null @@ -1,4608 +0,0 @@ -{ - "name": "bitcoinjs-lib", - "version": "3.3.2", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "3.3.0" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", - "dev": true - }, - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "1.0.3" - } - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "1.0.3" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array.prototype.find": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz", - "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.11.0" - } - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base-x": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.4.tgz", - "integrity": "sha512-UYOadoSIkEI/VrRGSG6qp93rp2WdokiAiNYDfGW5qURAY8GiAQkvMbwNNSDYiVJopqv4gCna7xqf4rrNGp+5AA==", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "bech32": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.3.tgz", - "integrity": "sha512-yuVFUvrNcoJi0sv5phmqc6P+Fl1HjRDRNOOkHY2X/3LBy2bIGNSFx4fZ95HMaXHupuS7cZR15AsvtmCIF4UEyg==" - }, - "bigi": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz", - "integrity": "sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU=" - }, - "bip39": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-2.5.0.tgz", - "integrity": "sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA==", - "dev": true, - "requires": { - "create-hash": "1.2.0", - "pbkdf2": "3.0.16", - "randombytes": "2.0.6", - "safe-buffer": "5.1.1", - "unorm": "1.4.1" - } - }, - "bip65": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bip65/-/bip65-1.0.3.tgz", - "integrity": "sha512-RQ1nc7xtnLa5XltnCqkoR2zmhuz498RjMJwrLKQzOE049D1HUqnYfon7cVSbwS5UGm0/EQlC2CH+NY3MyITA4Q==", - "dev": true - }, - "bip66": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", - "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "bitcoin-ops": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz", - "integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "requires": { - "base-x": "3.0.4" - } - }, - "bs58check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.1.tgz", - "integrity": "sha512-okRQiWc5FJuA2VOwQ1hB7Sf0MyEFg/EwRN12h4b8HrJoGkZ3xq1CGjkaAfYloLcZyqixQnO5mhPpN6IcHSplVg==", - "requires": { - "bs58": "4.0.1", - "create-hash": "1.2.0" - } - }, - "buffer-from": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", - "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", - "dev": true - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "1.0.1" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "1.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "typedarray": "0.0.6" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "md5.js": "1.3.4", - "ripemd160": "2.0.2", - "sha.js": "2.4.11" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.2.0", - "inherits": "2.0.3", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.1", - "sha.js": "2.4.11" - } - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "0.10.42" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "debug-log": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", - "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "dev": true, - "requires": { - "foreach": "2.0.5", - "object-keys": "1.0.11" - } - }, - "deglob": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.0.tgz", - "integrity": "sha1-TUSr4W7zLHebSXK9FBqAMlApoUo=", - "dev": true, - "requires": { - "find-root": "1.1.0", - "glob": "7.1.2", - "ignore": "3.3.7", - "pkg-config": "1.1.1", - "run-parallel": "1.1.8", - "uniq": "1.0.1" - } - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.1", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" - } - }, - "dhttp": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/dhttp/-/dhttp-2.4.2.tgz", - "integrity": "sha512-tuip8Xn15O5adhKh3XCCUbGq8+hZPATbsgdg8lw32fPwk/wkzCBu61H26VQBfOQnrHqcO0YWdprDXMRLks0vzQ==", - "dev": true, - "requires": { - "http-status-codes": "1.3.0", - "parse-headers": "2.0.1" - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "2.0.2" - } - }, - "ecurve": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/ecurve/-/ecurve-1.0.6.tgz", - "integrity": "sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w==", - "requires": { - "bigi": "1.4.2", - "safe-buffer": "5.1.1" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "0.2.1" - } - }, - "es-abstract": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", - "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", - "dev": true, - "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.1", - "is-callable": "1.1.3", - "is-regex": "1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, - "requires": { - "is-callable": "1.1.3", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" - } - }, - "es5-ext": { - "version": "0.10.42", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", - "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", - "dev": true, - "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1", - "next-tick": "1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.42", - "es6-symbol": "3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.42", - "es6-iterator": "2.0.3", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.42", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.42" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.42", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.1", - "estraverse": "4.2.0" - } - }, - "eslint": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.18.0.tgz", - "integrity": "sha1-ZH6YXErnFQLSCsYsEJ9m1RBMiks=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "chalk": "1.1.3", - "concat-stream": "1.6.2", - "debug": "2.6.9", - "doctrine": "2.1.0", - "escope": "3.6.0", - "espree": "3.5.4", - "esquery": "1.0.1", - "estraverse": "4.2.0", - "esutils": "2.0.2", - "file-entry-cache": "2.0.0", - "glob": "7.1.2", - "globals": "9.18.0", - "ignore": "3.3.7", - "imurmurhash": "0.1.4", - "inquirer": "0.12.0", - "is-my-json-valid": "2.17.2", - "is-resolvable": "1.1.0", - "js-yaml": "3.11.0", - "json-stable-stringify": "1.0.1", - "levn": "0.3.0", - "lodash": "4.17.5", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "optionator": "0.8.2", - "path-is-inside": "1.0.2", - "pluralize": "1.2.1", - "progress": "1.1.8", - "require-uncached": "1.0.3", - "shelljs": "0.7.8", - "strip-bom": "3.0.0", - "strip-json-comments": "2.0.1", - "table": "3.8.3", - "text-table": "0.2.0", - "user-home": "2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "eslint-config-standard": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-7.1.0.tgz", - "integrity": "sha1-R+dp6gc59bLVaTsaUBwhyWUPr88=", - "dev": true - }, - "eslint-config-standard-jsx": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-3.3.0.tgz", - "integrity": "sha1-yrCAGhWjYL9j+suXqyL73YjYpeA=", - "dev": true - }, - "eslint-plugin-promise": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.4.2.tgz", - "integrity": "sha1-G+J5Pq/i0YtbEjuBNsJp+AT+cSI=", - "dev": true - }, - "eslint-plugin-react": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.9.0.tgz", - "integrity": "sha1-VMLpkGt2+dEBQgML3DTp1oQKC7I=", - "dev": true, - "requires": { - "array.prototype.find": "2.0.4", - "doctrine": "1.5.0", - "jsx-ast-utils": "1.4.1" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "2.0.2", - "isarray": "1.0.0" - } - } - } - }, - "eslint-plugin-standard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-2.0.1.tgz", - "integrity": "sha1-NYlpn/nJF/LCX3apFmh/ZBw2n/M=", - "dev": true - }, - "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "dev": true, - "requires": { - "acorn": "5.5.3", - "acorn-jsx": "3.0.1" - } - }, - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "4.2.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "4.2.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.42" - } - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "1.3.0", - "object-assign": "4.1.1" - } - }, - "fill-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", - "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", - "dev": true, - "requires": { - "is-object": "1.0.1", - "merge-descriptors": "1.0.1" - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", - "dev": true, - "requires": { - "circular-json": "0.3.3", - "del": "2.2.2", - "graceful-fs": "4.1.11", - "write": "0.2.1" - } - }, - "for-each": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", - "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=", - "dev": true, - "requires": { - "is-function": "1.0.1" - } - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, - "requires": { - "is-property": "1.0.2" - } - }, - "get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", - "dev": true - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", - "dev": true - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true, - "requires": { - "function-bind": "1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "hoodwink": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hoodwink/-/hoodwink-1.0.0.tgz", - "integrity": "sha512-BcnI8v1qTuvQ9ngAdgkdaaC9b6IrAsPloMGEkatPuryG1GF57VH1EacnrhDZ8QL2+8JqK8VOR97STS5JdWWNew==", - "dev": true - }, - "http-status-codes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.3.0.tgz", - "integrity": "sha1-nNDnE5F3PQZxtInUHLxQlKpBY7Y=", - "dev": true - }, - "ignore": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", - "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "inquirer": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", - "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", - "dev": true, - "requires": { - "ansi-escapes": "1.4.0", - "ansi-regex": "2.1.1", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-width": "2.2.0", - "figures": "1.7.0", - "lodash": "4.17.5", - "readline2": "1.0.1", - "run-async": "0.1.0", - "rx-lite": "3.1.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "through": "2.3.8" - } - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", - "dev": true - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-function": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", - "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", - "dev": true - }, - "is-my-ip-valid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", - "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", - "dev": true - }, - "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", - "dev": true, - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "is-my-ip-valid": "1.0.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } - }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "1.0.1" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "1.0.2" - } - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "1.0.1" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", - "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", - "dev": true, - "requires": { - "argparse": "1.0.10", - "esprima": "4.0.0" - } - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true - }, - "jsx-ast-utils": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", - "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "4.0.0", - "pify": "3.0.0", - "strip-bom": "3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - } - }, - "lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", - "dev": true - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merkle-lib": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", - "integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY=" - }, - "minimaldata": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minimaldata/-/minimaldata-1.0.2.tgz", - "integrity": "sha1-AfOywB2LJzmEP9hT1AY2xaLrdms=", - "dev": true, - "requires": { - "bitcoin-ops": "1.4.1", - "pushdata-bitcoin": "1.0.1" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mocha": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz", - "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - } - }, - "module-not-found-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", - "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "mute-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", - "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "nyc": { - "version": "11.7.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.7.1.tgz", - "integrity": "sha512-EGePURSKUEpS1jWnEKAMhY+GWZzi7JC+f8iBDOATaOsLZW5hM/9eYx2dHGaEXa1ITvMm44CJugMksvP3NwMQMw==", - "dev": true, - "requires": { - "archy": "1.0.0", - "arrify": "1.0.1", - "caching-transform": "1.0.1", - "convert-source-map": "1.5.1", - "debug-log": "1.0.1", - "default-require-extensions": "1.0.0", - "find-cache-dir": "0.1.1", - "find-up": "2.1.0", - "foreground-child": "1.5.6", - "glob": "7.1.2", - "istanbul-lib-coverage": "1.2.0", - "istanbul-lib-hook": "1.1.0", - "istanbul-lib-instrument": "1.10.1", - "istanbul-lib-report": "1.1.3", - "istanbul-lib-source-maps": "1.2.3", - "istanbul-reports": "1.4.0", - "md5-hex": "1.3.0", - "merge-source-map": "1.1.0", - "micromatch": "2.3.11", - "mkdirp": "0.5.1", - "resolve-from": "2.0.0", - "rimraf": "2.6.2", - "signal-exit": "3.0.2", - "spawn-wrap": "1.4.2", - "test-exclude": "4.2.1", - "yargs": "11.1.0", - "yargs-parser": "8.1.0" - }, - "dependencies": { - "align-text": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" - } - }, - "amdefine": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "bundled": true, - "dev": true - }, - "append-transform": { - "version": "0.4.0", - "bundled": true, - "dev": true, - "requires": { - "default-require-extensions": "1.0.0" - } - }, - "archy": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "arr-diff": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "arr-flatten": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "bundled": true, - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "bundled": true, - "dev": true - }, - "arrify": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "async": { - "version": "1.5.2", - "bundled": true, - "dev": true - }, - "atob": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "babel-code-frame": { - "version": "6.26.0", - "bundled": true, - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - } - }, - "babel-generator": { - "version": "6.26.1", - "bundled": true, - "dev": true, - "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.5", - "source-map": "0.5.7", - "trim-right": "1.0.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "bundled": true, - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "bundled": true, - "dev": true, - "requires": { - "core-js": "2.5.5", - "regenerator-runtime": "0.11.1" - } - }, - "babel-template": { - "version": "6.26.0", - "bundled": true, - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.5" - } - }, - "babel-traverse": { - "version": "6.26.0", - "bundled": true, - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.4", - "lodash": "4.17.5" - } - }, - "babel-types": { - "version": "6.26.0", - "bundled": true, - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.5", - "to-fast-properties": "1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "bundled": true, - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "base": { - "version": "0.11.2", - "bundled": true, - "dev": true, - "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "bundled": true, - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "builtin-modules": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "cache-base": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, - "caching-transform": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "md5-hex": "1.3.0", - "mkdirp": "0.5.1", - "write-file-atomic": "1.3.4" - } - }, - "camelcase": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true - }, - "center-align": { - "version": "0.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" - } - }, - "chalk": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "class-utils": { - "version": "0.3.6", - "bundled": true, - "dev": true, - "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, - "cliui": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" - } - }, - "commondir": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "convert-source-map": { - "version": "1.5.1", - "bundled": true, - "dev": true - }, - "copy-descriptor": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "core-js": { - "version": "2.5.5", - "bundled": true, - "dev": true - }, - "cross-spawn": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "4.1.2", - "which": "1.3.0" - } - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "debug-log": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "default-require-extensions": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "strip-bom": "2.0.0" - } - }, - "define-property": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, - "detect-indent": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "error-ex": { - "version": "1.3.1", - "bundled": true, - "dev": true, - "requires": { - "is-arrayish": "0.2.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "bundled": true, - "dev": true - }, - "esutils": { - "version": "2.0.2", - "bundled": true, - "dev": true - }, - "execa": { - "version": "0.7.0", - "bundled": true, - "dev": true, - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "4.1.2", - "shebang-command": "1.2.0", - "which": "1.3.0" - } - } - } - }, - "expand-brackets": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "expand-range": { - "version": "1.8.2", - "bundled": true, - "dev": true, - "requires": { - "fill-range": "2.2.3" - } - }, - "extend-shallow": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "extglob": { - "version": "0.3.2", - "bundled": true, - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "filename-regex": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "fill-range": { - "version": "2.2.3", - "bundled": true, - "dev": true, - "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" - } - }, - "find-cache-dir": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "requires": { - "commondir": "1.0.1", - "mkdirp": "0.5.1", - "pkg-dir": "1.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "for-in": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "for-own": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, - "foreground-child": { - "version": "1.5.6", - "bundled": true, - "dev": true, - "requires": { - "cross-spawn": "4.0.2", - "signal-exit": "3.0.2" - } - }, - "fragment-cache": { - "version": "0.2.1", - "bundled": true, - "dev": true, - "requires": { - "map-cache": "0.2.2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "get-caller-file": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "get-value": { - "version": "2.0.6", - "bundled": true, - "dev": true - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-base": { - "version": "0.3.0", - "bundled": true, - "dev": true, - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "globals": { - "version": "9.18.0", - "bundled": true, - "dev": true - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true, - "dev": true - }, - "handlebars": { - "version": "4.0.11", - "bundled": true, - "dev": true, - "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "bundled": true, - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "has-ansi": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-flag": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "has-value": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, - "has-values": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "hosted-git-info": { - "version": "2.6.0", - "bundled": true, - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "invariant": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "requires": { - "loose-envify": "1.3.1" - } - }, - "invert-kv": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-arrayish": { - "version": "0.2.1", - "bundled": true, - "dev": true - }, - "is-buffer": { - "version": "1.1.6", - "bundled": true, - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "builtin-modules": "1.1.1" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "bundled": true, - "dev": true - } - } - }, - "is-dotfile": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "bundled": true, - "dev": true, - "requires": { - "is-primitive": "2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "is-number": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-odd": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-number": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "bundled": true, - "dev": true - } - } - }, - "is-plain-object": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "bundled": true, - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isexe": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "isobject": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "istanbul-lib-coverage": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "istanbul-lib-hook": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "append-transform": "0.4.0" - } - }, - "istanbul-lib-instrument": { - "version": "1.10.1", - "bundled": true, - "dev": true, - "requires": { - "babel-generator": "6.26.1", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "istanbul-lib-coverage": "1.2.0", - "semver": "5.5.0" - } - }, - "istanbul-lib-report": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "requires": { - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "path-parse": "1.0.5", - "supports-color": "3.2.3" - }, - "dependencies": { - "supports-color": { - "version": "3.2.3", - "bundled": true, - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "1.2.3", - "bundled": true, - "dev": true, - "requires": { - "debug": "3.1.0", - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "source-map": "0.5.7" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "istanbul-reports": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "handlebars": "4.0.11" - } - }, - "js-tokens": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "jsesc": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - }, - "lazy-cache": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "optional": true - }, - "lcid": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "invert-kv": "1.0.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "bundled": true, - "dev": true - } - } - }, - "lodash": { - "version": "4.17.5", - "bundled": true, - "dev": true - }, - "longest": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "loose-envify": { - "version": "1.3.1", - "bundled": true, - "dev": true, - "requires": { - "js-tokens": "3.0.2" - } - }, - "lru-cache": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, - "map-cache": { - "version": "0.2.2", - "bundled": true, - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "object-visit": "1.0.1" - } - }, - "md5-hex": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "md5-o-matic": "0.1.1" - } - }, - "md5-o-matic": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "mem": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "mimic-fn": "1.2.0" - } - }, - "merge-source-map": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "bundled": true, - "dev": true - } - } - }, - "micromatch": { - "version": "2.3.11", - "bundled": true, - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - }, - "mimic-fn": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "mixin-deep": { - "version": "1.3.1", - "bundled": true, - "dev": true, - "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "nanomatch": { - "version": "1.2.9", - "bundled": true, - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, - "normalize-package-data": { - "version": "2.4.0", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "2.6.0", - "is-builtin-module": "1.0.0", - "semver": "5.5.0", - "validate-npm-package-license": "3.0.3" - } - }, - "normalize-path": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, - "npm-run-path": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "path-key": "2.0.1" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "bundled": true, - "dev": true, - "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, - "object.omit": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "optimist": { - "version": "0.6.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "os-locale": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" - } - }, - "p-finally": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "p-limit": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "1.2.0" - } - }, - "p-try": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "parse-glob": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - } - }, - "parse-json": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "requires": { - "error-ex": "1.3.1" - } - }, - "pascalcase": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "path-exists": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "path-key": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "path-parse": { - "version": "1.0.5", - "bundled": true, - "dev": true - }, - "path-type": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "pify": { - "version": "2.3.0", - "bundled": true, - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "bundled": true, - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "pinkie": "2.0.4" - } - }, - "pkg-dir": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "find-up": "1.1.2" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "preserve": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "randomatic": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "read-pkg": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - } - } - }, - "regenerator-runtime": { - "version": "0.11.1", - "bundled": true, - "dev": true - }, - "regex-cache": { - "version": "0.4.4", - "bundled": true, - "dev": true, - "requires": { - "is-equal-shallow": "0.1.3" - } - }, - "regex-not": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "repeat-element": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "bundled": true, - "dev": true - }, - "repeating": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-finite": "1.0.2" - } - }, - "require-directory": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "resolve-from": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "bundled": true, - "dev": true - }, - "ret": { - "version": "0.1.15", - "bundled": true, - "dev": true - }, - "right-align": { - "version": "0.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "align-text": "0.1.4" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-regex": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "ret": "0.1.15" - } - }, - "semver": { - "version": "5.5.0", - "bundled": true, - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "set-value": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "shebang-command": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "slide": { - "version": "1.1.6", - "bundled": true, - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "bundled": true, - "dev": true, - "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.1", - "use": "3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "source-map": { - "version": "0.5.7", - "bundled": true, - "dev": true - }, - "source-map-resolve": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "atob": "2.1.0", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "bundled": true, - "dev": true - }, - "spawn-wrap": { - "version": "1.4.2", - "bundled": true, - "dev": true, - "requires": { - "foreground-child": "1.5.6", - "mkdirp": "0.5.1", - "os-homedir": "1.0.2", - "rimraf": "2.6.2", - "signal-exit": "3.0.2", - "which": "1.3.0" - } - }, - "spdx-correct": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "spdx-expression-parse": "3.0.0", - "spdx-license-ids": "3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "spdx-exceptions": "2.1.0", - "spdx-license-ids": "3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "split-string": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "3.0.2" - } - }, - "static-extend": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - } - } - }, - "string-width": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - }, - "strip-eof": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "test-exclude": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "requires": { - "arrify": "1.0.1", - "micromatch": "3.1.10", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "require-main-filename": "1.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "bundled": true, - "dev": true - }, - "braces": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - } - }, - "kind-of": { - "version": "5.1.0", - "bundled": true, - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "bundled": true, - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - } - } - } - }, - "to-fast-properties": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "to-regex": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - } - } - }, - "trim-right": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "uglify-js": { - "version": "2.8.29", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "yargs": { - "version": "3.10.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "union-value": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "set-value": { - "version": "0.4.3", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" - } - } - } - }, - "unset-value": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "bundled": true, - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, - "urix": { - "version": "0.1.0", - "bundled": true, - "dev": true - }, - "use": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, - "validate-npm-package-license": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "requires": { - "spdx-correct": "3.0.0", - "spdx-expression-parse": "3.0.0" - } - }, - "which": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "isexe": "2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "window-size": { - "version": "0.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "wordwrap": { - "version": "0.0.3", - "bundled": true, - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "write-file-atomic": { - "version": "1.3.4", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "slide": "1.1.6" - } - }, - "y18n": { - "version": "3.2.1", - "bundled": true, - "dev": true - }, - "yallist": { - "version": "2.1.2", - "bundled": true, - "dev": true - }, - "yargs": { - "version": "11.1.0", - "bundled": true, - "dev": true, - "requires": { - "cliui": "4.0.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "cliui": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - }, - "yargs-parser": { - "version": "9.0.2", - "bundled": true, - "dev": true, - "requires": { - "camelcase": "4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "8.1.0", - "bundled": true, - "dev": true, - "requires": { - "camelcase": "4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "bundled": true, - "dev": true - } - } - } - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", - "dev": true, - "requires": { - "p-try": "1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "1.2.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parse-headers": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.1.tgz", - "integrity": "sha1-aug6eqJanZtwCswoaYzR8e1+lTY=", - "dev": true, - "requires": { - "for-each": "0.3.2", - "trim": "0.0.1" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "1.3.1", - "json-parse-better-errors": "1.0.2" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "pbkdf2": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", - "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", - "dev": true, - "requires": { - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.1", - "sha.js": "2.4.11" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "2.0.4" - } - }, - "pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", - "dev": true, - "requires": { - "find-up": "2.1.0", - "load-json-file": "4.0.0" - } - }, - "pkg-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", - "dev": true, - "requires": { - "debug-log": "1.0.1", - "find-root": "1.1.0", - "xtend": "4.0.1" - } - }, - "pluralize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", - "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "dev": true - }, - "proxyquire": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-1.8.0.tgz", - "integrity": "sha1-AtUUpb7ZhvBMuyCTrxZ0FTX3ntw=", - "dev": true, - "requires": { - "fill-keys": "1.0.2", - "module-not-found-error": "1.0.1", - "resolve": "1.1.7" - } - }, - "pushdata-bitcoin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz", - "integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=", - "requires": { - "bitcoin-ops": "1.4.1" - } - }, - "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" - } - }, - "readline2": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", - "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "mute-stream": "0.0.5" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "1.1.7" - } - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "0.1.0", - "resolve-from": "1.0.1" - } - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - } - }, - "run-async": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", - "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", - "dev": true, - "requires": { - "once": "1.4.0" - } - }, - "run-parallel": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.8.tgz", - "integrity": "sha512-e5t1NVhr5VWmD9V9U4KjjSGkf5w6CcTPgw11A3CfIvkkQxlAKzX3usPUp1NUQTmpOOjU+f9QRICU3tMbKwn9ZQ==", - "dev": true - }, - "rx-lite": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", - "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", - "dev": true - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "dev": true, - "requires": { - "glob": "7.1.2", - "interpret": "1.1.0", - "rechoir": "0.6.2" - } - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "standard": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/standard/-/standard-9.0.2.tgz", - "integrity": "sha1-m9O5RnSS4hKxkU14VTlD/5tI/Zk=", - "dev": true, - "requires": { - "eslint": "3.18.0", - "eslint-config-standard": "7.1.0", - "eslint-config-standard-jsx": "3.3.0", - "eslint-plugin-promise": "3.4.2", - "eslint-plugin-react": "6.9.0", - "eslint-plugin-standard": "2.0.1", - "standard-engine": "5.4.0" - } - }, - "standard-engine": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-5.4.0.tgz", - "integrity": "sha1-4OhpWeoHhkJdM4PkDBv3DS+YVXk=", - "dev": true, - "requires": { - "deglob": "2.1.0", - "get-stdin": "5.0.1", - "home-or-tmp": "2.0.0", - "minimist": "1.2.0", - "pkg-conf": "2.1.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - }, - "table": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", - "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", - "dev": true, - "requires": { - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "chalk": "1.1.3", - "lodash": "4.17.5", - "slice-ansi": "0.0.4", - "string-width": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typeforce": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.12.0.tgz", - "integrity": "sha512-fvnkvueAOFLhtAqDgIA/wMP21SMwS/NQESFKZuwVrj5m/Ew6eK2S0z0iB++cwtROPWDOhaT6OUfla8UwMw4Adg==" - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "unorm": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.4.1.tgz", - "integrity": "sha1-NkIA1fE2RsqLzURJAnEzVhR5IwA=", - "dev": true - }, - "user-home": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", - "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", - "dev": true, - "requires": { - "os-homedir": "1.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "varuint-bitcoin": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz", - "integrity": "sha512-jCEPG+COU/1Rp84neKTyDJQr478/hAfVp5xxYn09QEH0yBjbmPeMfuuQIrp+BUD83hybtYZKhr5elV3bvdV1bA==", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "wif": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", - "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", - "requires": { - "bs58check": "2.1.1" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "0.5.1" - } - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - } - } -} From 6344be50ae7bbab02672d120a1d2bcd695306753 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 30 Apr 2018 10:46:17 +1000 Subject: [PATCH 013/568] integration: adhere to new nullData API --- test/integration/transactions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 56ef033..fa79d3c 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -84,7 +84,7 @@ describe('bitcoinjs-lib (transactions)', function () { var txb = new bitcoin.TransactionBuilder(regtest) var data = Buffer.from('bitcoinjs-lib', 'utf8') - var dataScript = bitcoin.script.nullData.output.encode(data) + var dataScript = bitcoin.script.nullData.output.encode([data]) txb.addInput(unspent.txId, unspent.vout) txb.addOutput(dataScript, 1000) From c3c98709e256137b81b92abd27f5fa52f8925b48 Mon Sep 17 00:00:00 2001 From: Wesley Smith <wesleysmith@Wesleys-MacBook-Pro.local> Date: Sun, 6 May 2018 22:40:27 -0700 Subject: [PATCH 014/568] fixes adding version field to transaction instance --- src/transaction_builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 51bc396..4574831 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -476,7 +476,7 @@ function TransactionBuilder (network, maximumFeeRate) { this.__inputs = [] this.__tx = new Transaction() - this.tx.version = 2 + this.__tx.version = 2 } TransactionBuilder.prototype.setLockTime = function (locktime) { From c58ada362eaf7946e33df1c7c15854bca7faa413 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 13 Oct 2016 23:45:08 +1100 Subject: [PATCH 015/568] rm ECSignature, add script.signature instead --- src/ecdsa.js | 6 +- src/ecsignature.js | 97 --------------------- src/index.js | 1 - src/script.js | 1 + src/script_signature.js | 51 +++++++++++ src/transaction_builder.js | 7 +- test/bitcoin.core.js | 9 +- test/ecdsa.js | 31 ++++--- test/fixtures/ecdsa.json | 61 +++++++++----- test/fixtures/signature.json | 140 +++++++++++++++++++++++++++++++ test/integration/cltv.js | 11 +-- test/integration/crypto.js | 2 +- test/integration/transactions.js | 2 +- test/signature.js | 65 ++++++++++++++ 14 files changed, 335 insertions(+), 149 deletions(-) delete mode 100644 src/ecsignature.js create mode 100644 src/script_signature.js create mode 100644 test/fixtures/signature.json create mode 100644 test/signature.js diff --git a/src/ecdsa.js b/src/ecdsa.js index 8841f6e..403ffd5 100644 --- a/src/ecdsa.js +++ b/src/ecdsa.js @@ -4,7 +4,6 @@ var typeforce = require('typeforce') var types = require('./types') var BigInteger = require('bigi') -var ECSignature = require('./ecsignature') var ZERO = Buffer.alloc(1, 0) var ONE = Buffer.alloc(1, 1) @@ -102,7 +101,10 @@ function sign (hash, d) { s = n.subtract(s) } - return new ECSignature(r, s) + return { + r: r, + s: s + } } function verify (hash, signature, Q) { diff --git a/src/ecsignature.js b/src/ecsignature.js deleted file mode 100644 index ebdcc36..0000000 --- a/src/ecsignature.js +++ /dev/null @@ -1,97 +0,0 @@ -var bip66 = require('bip66') -var typeforce = require('typeforce') -var types = require('./types') - -var BigInteger = require('bigi') - -function ECSignature (r, s) { - typeforce(types.tuple(types.BigInt, types.BigInt), arguments) - - this.r = r - this.s = s -} - -ECSignature.parseCompact = function (buffer) { - typeforce(types.BufferN(65), buffer) - - var flagByte = buffer.readUInt8(0) - 27 - if (flagByte !== (flagByte & 7)) throw new Error('Invalid signature parameter') - - var compressed = !!(flagByte & 4) - var recoveryParam = flagByte & 3 - var signature = ECSignature.fromRSBuffer(buffer.slice(1)) - - return { - compressed: compressed, - i: recoveryParam, - signature: signature - } -} - -ECSignature.fromRSBuffer = function (buffer) { - typeforce(types.BufferN(64), buffer) - - var r = BigInteger.fromBuffer(buffer.slice(0, 32)) - var s = BigInteger.fromBuffer(buffer.slice(32, 64)) - return new ECSignature(r, s) -} - -ECSignature.fromDER = function (buffer) { - var decode = bip66.decode(buffer) - var r = BigInteger.fromDERInteger(decode.r) - var s = BigInteger.fromDERInteger(decode.s) - - return new ECSignature(r, s) -} - -// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) -ECSignature.parseScriptSignature = function (buffer) { - var hashType = buffer.readUInt8(buffer.length - 1) - var hashTypeMod = hashType & ~0x80 - - if (hashTypeMod <= 0x00 || hashTypeMod >= 0x04) throw new Error('Invalid hashType ' + hashType) - - return { - signature: ECSignature.fromDER(buffer.slice(0, -1)), - hashType: hashType - } -} - -ECSignature.prototype.toCompact = function (i, compressed) { - if (compressed) { - i += 4 - } - - i += 27 - - var buffer = Buffer.alloc(65) - buffer.writeUInt8(i, 0) - this.toRSBuffer(buffer, 1) - return buffer -} - -ECSignature.prototype.toDER = function () { - var r = Buffer.from(this.r.toDERInteger()) - var s = Buffer.from(this.s.toDERInteger()) - - return bip66.encode(r, s) -} - -ECSignature.prototype.toRSBuffer = function (buffer, offset) { - buffer = buffer || Buffer.alloc(64) - this.r.toBuffer(32).copy(buffer, offset) - this.s.toBuffer(32).copy(buffer, offset + 32) - return buffer -} - -ECSignature.prototype.toScriptSignature = function (hashType) { - var hashTypeMod = hashType & ~0x80 - if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) - - var hashTypeBuffer = Buffer.alloc(1) - hashTypeBuffer.writeUInt8(hashType, 0) - - return Buffer.concat([this.toDER(), hashTypeBuffer]) -} - -module.exports = ECSignature diff --git a/src/index.js b/src/index.js index a809b3c..e0c58cb 100644 --- a/src/index.js +++ b/src/index.js @@ -8,7 +8,6 @@ for (var key in templates) { module.exports = { Block: require('./block'), ECPair: require('./ecpair'), - ECSignature: require('./ecsignature'), HDNode: require('./hdnode'), Transaction: require('./transaction'), TransactionBuilder: require('./transaction_builder'), diff --git a/src/script.js b/src/script.js index 1335f5d..4bd2fdf 100644 --- a/src/script.js +++ b/src/script.js @@ -206,6 +206,7 @@ module.exports = { toStack: toStack, number: require('./script_number'), + signature: require('./script_signature'), isCanonicalPubKey: isCanonicalPubKey, isCanonicalSignature: isCanonicalSignature, diff --git a/src/script_signature.js b/src/script_signature.js new file mode 100644 index 0000000..463c448 --- /dev/null +++ b/src/script_signature.js @@ -0,0 +1,51 @@ +var bip66 = require('bip66') +var BigInteger = require('bigi') +var typeforce = require('typeforce') +var types = require('./types') + +// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) +function decode (buffer) { + var hashType = buffer.readUInt8(buffer.length - 1) + var hashTypeMod = hashType & ~0x80 + if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) + + var decode = bip66.decode(buffer.slice(0, -1)) + + return { + signature: { + r: BigInteger.fromDERInteger(decode.r), + s: BigInteger.fromDERInteger(decode.s) + }, + hashType: hashType + } +} + +function fromRSBuffer (buffer) { + typeforce(types.BufferN(64), buffer) + + var r = BigInteger.fromBuffer(buffer.slice(0, 32)) + var s = BigInteger.fromBuffer(buffer.slice(32, 64)) + return { r: r, s: s } +} + +function encode (signature, hashType) { + var hashTypeMod = hashType & ~0x80 + if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) + + var hashTypeBuffer = new Buffer(1) + hashTypeBuffer.writeUInt8(hashType, 0) + + var r = new Buffer(signature.r.toDERInteger()) + var s = new Buffer(signature.s.toDERInteger()) + + return Buffer.concat([ + bip66.encode(r, s), + hashTypeBuffer + ]) +} + +module.exports = { + fromRSBuffer, + decode: decode, + encode: encode +} diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 4574831..1f1f490 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -12,7 +12,6 @@ var SIGNABLE = [btemplates.types.P2PKH, btemplates.types.P2PK, btemplates.types. var P2SH = SIGNABLE.concat([btemplates.types.P2WPKH, btemplates.types.P2WSH]) var ECPair = require('./ecpair') -var ECSignature = require('./ecsignature') var Transaction = require('./transaction') function supportedType (type) { @@ -190,7 +189,7 @@ function fixMultisigOrder (input, transaction, vin) { if (!signature) return false // TODO: avoid O(n) hashForSignature - var parsed = ECSignature.parseScriptSignature(signature) + var parsed = bscript.signature.decode(signature) var hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType) // skip if signature does not match pubKey @@ -717,9 +716,9 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy )) throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH') var signature = keyPair.sign(signatureHash) - if (Buffer.isBuffer(signature)) signature = ECSignature.fromRSBuffer(signature) + if (Buffer.isBuffer(signature)) signature = bscript.signature.fromRSBuffer(signature) - input.signatures[i] = signature.toScriptSignature(hashType) + input.signatures[i] = bscript.signature.encode(signature, hashType) return true }) diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index 2eab7de..b042ae1 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -198,13 +198,14 @@ describe('Bitcoin-core', function () { }) }) - describe('ECSignature.parseScriptSignature', function () { + describe('script.signature.decode', function () { sigCanonical.forEach(function (hex) { var buffer = Buffer.from(hex, 'hex') it('can parse ' + hex, function () { - var parsed = bitcoin.ECSignature.parseScriptSignature(buffer) - var actual = parsed.signature.toScriptSignature(parsed.hashType) + var parsed = bitcoin.script.signature.decode(buffer) + var actual = bitcoin.script.signature.encode(parsed.signature, parsed.hashType) + assert.strictEqual(actual.toString('hex'), hex) }) }) @@ -218,7 +219,7 @@ describe('Bitcoin-core', function () { it('throws on ' + description, function () { assert.throws(function () { - bitcoin.ECSignature.parseScriptSignature(buffer) + bitcoin.script.signature.decode(buffer) }, /Expected DER (integer|sequence)|(R|S) value (excessively padded|is negative)|(R|S|DER sequence) length is (zero|too short|too long|invalid)|Invalid hashType/) }) }) diff --git a/test/ecdsa.js b/test/ecdsa.js index f6c6004..a0e07d1 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -2,17 +2,31 @@ var assert = require('assert') var bcrypto = require('../src/crypto') +var bscript = require('../src/script') var ecdsa = require('../src/ecdsa') var hoodwink = require('hoodwink') var BigInteger = require('bigi') -var ECSignature = require('../src/ecsignature') var curve = ecdsa.__curve var fixtures = require('./fixtures/ecdsa.json') describe('ecdsa', function () { + function fromRaw (signature) { + return { + r: new BigInteger(signature.r, 16), + s: new BigInteger(signature.s, 16) + } + } + + function toRaw (signature) { + return { + r: signature.r.toHex(), + s: signature.s.toHex() + } + } + describe('deterministicGenerateK', function () { function checkSig () { return true @@ -80,9 +94,9 @@ describe('ecdsa', function () { it('produces a deterministic signature for "' + f.message + '"', function () { var d = BigInteger.fromHex(f.d) var hash = bcrypto.sha256(f.message) - var signature = ecdsa.sign(hash, d).toDER() + var signature = ecdsa.sign(hash, d) - assert.strictEqual(signature.toString('hex'), f.signature) + assert.deepEqual(toRaw(signature), f.signature) }) }) @@ -101,7 +115,7 @@ describe('ecdsa', function () { it('verifies a valid signature for "' + f.message + '"', function () { var d = BigInteger.fromHex(f.d) var H = bcrypto.sha256(f.message) - var signature = ECSignature.fromDER(Buffer.from(f.signature, 'hex')) + var signature = fromRaw(f.signature) var Q = curve.G.multiply(d) assert(ecdsa.verify(H, signature, Q)) @@ -112,14 +126,7 @@ describe('ecdsa', function () { it('fails to verify with ' + f.description, function () { var H = bcrypto.sha256(f.message) var d = BigInteger.fromHex(f.d) - - var signature - if (f.signature) { - signature = ECSignature.fromDER(Buffer.from(f.signature, 'hex')) - } else if (f.signatureRaw) { - signature = new ECSignature(new BigInteger(f.signatureRaw.r, 16), new BigInteger(f.signatureRaw.s, 16)) - } - + var signature = fromRaw(f.signature) var Q = curve.G.multiply(d) assert.strictEqual(ecdsa.verify(H, signature, Q), false) diff --git a/test/fixtures/ecdsa.json b/test/fixtures/ecdsa.json index bd2fc3c..780ad74 100644 --- a/test/fixtures/ecdsa.json +++ b/test/fixtures/ecdsa.json @@ -5,50 +5,64 @@ "d": "01", "k": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5", "message": "Everything should be made as simple as possible, but not simpler.", - "i": 0, - "signature": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262" + "signature": { + "r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9", + "s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262" + } }, { "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "k": "9dc74cbfd383980fb4ae5d2680acddac9dac956dca65a28c80ac9c847c2374e4", "message": "Equations are more important to me, because politics is for the present, but an equation is something for eternity.", - "i": 0, - "signature": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5" + "signature": { + "r": "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed", + "s": "07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5" + } }, { "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "k": "fd27071f01648ebbdd3e1cfbae48facc9fa97edc43bbbc9a7fdc28eae13296f5", "message": "Not only is the Universe stranger than we think, it is stranger than we can think.", - "i": 0, - "signature": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283" + "signature": { + "r": "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd0", + "s": "6fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283" + } }, { "d": "0000000000000000000000000000000000000000000000000000000000000001", "k": "f0cd2ba5fc7c183de589f6416220a36775a146740798756d8d949f7166dcc87f", "message": "How wonderful that we have met with a paradox. Now we have some hope of making progress.", - "i": 1, - "signature": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3" + "signature": { + "r": "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3", + "s": "75afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3" + } }, { "d": "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64", "k": "6bb4a594ad57c1aa22dbe991a9d8501daf4688bf50a4892ef21bd7c711afda97", "message": "Computer science is no more about computers than astronomy is about telescopes.", - "i": 0, - "signature": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6" + "signature": { + "r": "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d", + "s": "0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6" + } }, { "d": "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637", "k": "097b5c8ee22c3ea78a4d3635e0ff6fe85a1eb92ce317ded90b9e71aab2b861cb", "message": "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough", - "i": 1, - "signature": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37" + "signature": { + "r": "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda487", + "s": "0e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37" + } }, { "d": "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3", "k": "19355c36c8cbcdfb2382e23b194b79f8c97bf650040fc7728dfbf6b39a97c25b", "message": "The question of whether computers can think is like the question of whether submarines can swim.", - "i": 1, - "signature": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef" + "signature": { + "r": "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9", + "s": "06ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef" + } } ], "rfc6979": [ @@ -130,13 +144,16 @@ "description": "The wrong signature", "d": "01", "message": "foo", - "signature": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5" + "signature": { + "r": "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed", + "s": "07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5" + } }, { "description": "Invalid r value (< 0)", "d": "01", "message": "foo", - "signatureRaw": { + "signature": { "r": "-01", "s": "02" } @@ -145,7 +162,7 @@ "description": "Invalid r value (== 0)", "d": "01", "message": "foo", - "signatureRaw": { + "signature": { "r": "00", "s": "02" } @@ -154,7 +171,7 @@ "description": "Invalid r value (>= n)", "d": "01", "message": "foo", - "signatureRaw": { + "signature": { "r": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", "s": "02" } @@ -163,7 +180,7 @@ "description": "Invalid s value (< 0)", "d": "01", "message": "foo", - "signatureRaw": { + "signature": { "r": "02", "s": "-01" } @@ -172,7 +189,7 @@ "description": "Invalid s value (== 0)", "d": "01", "message": "foo", - "signatureRaw": { + "signature": { "r": "02", "s": "00" } @@ -181,7 +198,7 @@ "description": "Invalid s value (>= n)", "d": "01", "message": "foo", - "signatureRaw": { + "signature": { "r": "02", "s": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" } @@ -190,7 +207,7 @@ "description": "Invalid r, s values (r = s = -n)", "d": "01", "message": "foo", - "signatureRaw": { + "signature": { "r": "-fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", "s": "-fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" } diff --git a/test/fixtures/signature.json b/test/fixtures/signature.json new file mode 100644 index 0000000..9f4023d --- /dev/null +++ b/test/fixtures/signature.json @@ -0,0 +1,140 @@ +{ + "valid": [ + { + "hashType": 1, + "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226201", + "raw": { + "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", + "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" + } + }, + { + "hashType": 2, + "hex": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a502", + "raw": { + "r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", + "s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" + } + }, + { + "hashType": 3, + "hex": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b28303", + "raw": { + "r": "115464191557905790016094131873849783294273568009648050793030031933291767741904", + "s": "50562520307781850052192542766631199590053690478900449960232079510155113443971" + } + }, + { + "hashType": 129, + "hex": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d381", + "raw": { + "r": "87230998027579607140680851455601772643840468630989315269459846730712163783123", + "s": "53231320085894623106179381504478252331065330583563809963303318469380290929875" + } + }, + { + "hashType": 130, + "hex": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df682", + "raw": { + "r": "51348483531757779992459563033975330355971795607481991320287437101831125115997", + "s": "6277080015686056199074771961940657638578000617958603212944619747099038735862" + } + }, + { + "hashType": 131, + "hex": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd3783", + "raw": { + "r": "113979859486826658566290715281614250298918272782414232881639314569529560769671", + "s": "6517071009538626957379450615706485096874328019806177698938278220732027419959" + } + }, + { + "hashType": 129, + "hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef81", + "raw": { + "r": "93122007060065279508564838030979550535085999589142852106617159184757394422777", + "s": "3078539468410661027472930027406594684630312677495124015420811882501887769839" + } + } + ], + "invalid": [ + { + "exception": "DER sequence length is too short", + "hex": "ffffffffffffff01" + }, + { + "exception": "DER sequence length is too long", + "hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01" + }, + { + "exception": "Expected DER sequence", + "hex": "00ffff0400ffffff020400ffffff01" + }, + { + "exception": "DER sequence length is invalid", + "hex": "30ff020400ffffff020400ffffff01" + }, + { + "exception": "DER sequence length is invalid", + "hex": "300c030400ffffff030400ffffff000001" + }, + { + "exception": "Expected DER integer", + "hex": "300cff0400ffffff020400ffffff01" + }, + { + "exception": "Expected DER integer \\(2\\)", + "hex": "300c020200ffffff020400ffffff01" + }, + { + "exception": "R length is zero", + "hex": "30080200020400ffffff01" + }, + { + "exception": "S length is zero", + "hex": "3008020400ffffff020001" + }, + { + "exception": "R length is too long", + "hex": "300c02dd00ffffff020400ffffff01" + }, + { + "exception": "S length is invalid", + "hex": "300c020400ffffff02dd00ffffff01" + }, + { + "exception": "R value is negative", + "hex": "300c020480000000020400ffffff01" + }, + { + "exception": "S value is negative", + "hex": "300c020400ffffff02048000000001" + }, + { + "exception": "R value excessively padded", + "hex": "300c02040000ffff020400ffffff01" + }, + { + "exception": "S value excessively padded", + "hex": "300c020400ffffff02040000ffff01" + }, + { + "exception": "Invalid hashType 7", + "hashType": 7, + "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226207", + "raw": { + "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", + "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" + } + }, + { + "exception": "Invalid hashType 140", + "hashType": 140, + "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa543422628c", + "raw": { + "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", + "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" + } + } + ] +} diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 8f1488f..82c629e 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -56,7 +56,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { var tx = txb.buildIncomplete() var signatureHash = tx.hashForSignature(0, redeemScript, hashType) var redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - alice.sign(signatureHash).toScriptSignature(hashType), + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.opcodes.OP_TRUE ], redeemScript) tx.setInputScript(0, redeemScriptSig) @@ -100,7 +100,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { var tx = txb.buildIncomplete() var signatureHash = tx.hashForSignature(0, redeemScript, hashType) var redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - alice.sign(signatureHash).toScriptSignature(hashType), + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.opcodes.OP_TRUE ], redeemScript) tx.setInputScript(0, redeemScriptSig) @@ -154,8 +154,8 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { var tx = txb.buildIncomplete() var signatureHash = tx.hashForSignature(0, redeemScript, hashType) var redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - alice.sign(signatureHash).toScriptSignature(hashType), - bob.sign(signatureHash).toScriptSignature(hashType), + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.opcodes.OP_FALSE ], redeemScript) tx.setInputScript(0, redeemScriptSig) @@ -196,7 +196,8 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { var tx = txb.buildIncomplete() var signatureHash = tx.hashForSignature(0, redeemScript, hashType) var redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - alice.sign(signatureHash).toScriptSignature(hashType), + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.opcodes.OP_TRUE ], redeemScript) tx.setInputScript(0, redeemScriptSig) diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 716cb50..183c50e 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -21,7 +21,7 @@ describe('bitcoinjs-lib (crypto)', function () { assert(bitcoin.script.pubKeyHash.input.check(scriptChunks), 'Expected pubKeyHash script') var prevOutScript = bitcoin.address.toOutputScript('1ArJ9vRaQcoQ29mTWZH768AmRwzb6Zif1z') - var scriptSignature = bitcoin.ECSignature.parseScriptSignature(scriptChunks[0]) + var scriptSignature = bitcoin.script.signature.decode(scriptChunks[0]) var publicKey = bitcoin.ECPair.fromPublicKeyBuffer(scriptChunks[1]) var m = tx.hashForSignature(vin, prevOutScript, scriptSignature.hashType) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 4c1cc26..2377095 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -230,7 +230,7 @@ describe('bitcoinjs-lib (transactions)', function () { var keyPair = keyPairs[i] var prevOutScript = bitcoin.address.toOutputScript(keyPair.getAddress()) var scriptSig = bitcoin.script.pubKeyHash.input.decode(input.script) - var ss = bitcoin.ECSignature.parseScriptSignature(scriptSig.signature) + var ss = bitcoin.script.signature.decode(scriptSig.signature) var hash = tx.hashForSignature(i, prevOutScript, ss.hashType) assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.getPublicKeyBuffer().toString('hex')) diff --git a/test/signature.js b/test/signature.js new file mode 100644 index 0000000..26b2136 --- /dev/null +++ b/test/signature.js @@ -0,0 +1,65 @@ +/* global describe, it */ + +var assert = require('assert') +var bscriptSig = require('../src/script').signature +var BigInteger = require('bigi') +var fixtures = require('./fixtures/signature.json') + +describe('Script Signatures', function () { + function fromRaw (signature) { + return { + r: new BigInteger(signature.r), + s: new BigInteger(signature.s) + } + } + + function toRaw (signature) { + return { + r: signature.r.toString(), + s: signature.s.toString() + } + } + + describe('encode', function () { + fixtures.valid.forEach(function (f) { + it('encodes ' + f.hex, function () { + var buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType) + + assert.strictEqual(buffer.toString('hex'), f.hex) + }) + }) + + fixtures.invalid.forEach(function (f) { + if (!f.raw) return + + it('throws ' + f.exception, function () { + var signature = fromRaw(f.raw) + + assert.throws(function () { + bscriptSig.encode(signature, f.hashType) + }, new RegExp(f.exception)) + }) + }) + }) + + describe('decode', function () { + fixtures.valid.forEach(function (f) { + it('decodes ' + f.hex, function () { + var decode = bscriptSig.decode(new Buffer(f.hex, 'hex')) + + assert.deepEqual(toRaw(decode.signature), f.raw) + assert.strictEqual(decode.hashType, f.hashType) + }) + }) + + fixtures.invalid.forEach(function (f) { + it('throws on ' + f.hex, function () { + var buffer = new Buffer(f.hex, 'hex') + + assert.throws(function () { + bscriptSig.decode(buffer) + }, new RegExp(f.exception)) + }) + }) + }) +}) From c16847434ca7a1b15383c91c3600223c5192b20d Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 13 Apr 2018 16:02:45 +1000 Subject: [PATCH 016/568] add toRSBuffer for less breaking changes --- src/script_signature.js | 10 +- test/ecsignature.js | 122 ------------------- test/fixtures/ecsignature.json | 214 --------------------------------- 3 files changed, 9 insertions(+), 337 deletions(-) delete mode 100644 test/ecsignature.js delete mode 100644 test/fixtures/ecsignature.json diff --git a/src/script_signature.js b/src/script_signature.js index 463c448..f628c99 100644 --- a/src/script_signature.js +++ b/src/script_signature.js @@ -20,6 +20,13 @@ function decode (buffer) { } } +function toRSBuffer (signature, buffer, offset) { + buffer = buffer || Buffer.alloc(64) + signature.r.toBuffer(32).copy(buffer, offset) + signature.s.toBuffer(32).copy(buffer, offset + 32) + return buffer +} + function fromRSBuffer (buffer) { typeforce(types.BufferN(64), buffer) @@ -45,7 +52,8 @@ function encode (signature, hashType) { } module.exports = { - fromRSBuffer, + fromRSBuffer: fromRSBuffer, + toRSBuffer: toRSBuffer, decode: decode, encode: encode } diff --git a/test/ecsignature.js b/test/ecsignature.js deleted file mode 100644 index d0bc15d..0000000 --- a/test/ecsignature.js +++ /dev/null @@ -1,122 +0,0 @@ -/* global describe, it */ - -var assert = require('assert') - -var BigInteger = require('bigi') -var ECSignature = require('../src/ecsignature') - -var fixtures = require('./fixtures/ecsignature.json') - -describe('ECSignature', function () { - describe('toCompact', function () { - fixtures.valid.forEach(function (f) { - it('exports ' + f.compact.hex + ' correctly', function () { - var signature = new ECSignature(new BigInteger(f.signature.r), new BigInteger(f.signature.s)) - - var buffer = signature.toCompact(f.compact.i, f.compact.compressed) - assert.strictEqual(buffer.toString('hex'), f.compact.hex) - }) - }) - }) - - describe('parseCompact', function () { - fixtures.valid.forEach(function (f) { - it('imports ' + f.compact.hex + ' correctly', function () { - var buffer = Buffer.from(f.compact.hex, 'hex') - var parsed = ECSignature.parseCompact(buffer) - - assert.strictEqual(parsed.compressed, f.compact.compressed) - assert.strictEqual(parsed.i, f.compact.i) - assert.strictEqual(parsed.signature.r.toString(), f.signature.r) - assert.strictEqual(parsed.signature.s.toString(), f.signature.s) - }) - }) - - fixtures.invalid.compact.forEach(function (f) { - it('throws on ' + f.hex, function () { - var buffer = Buffer.from(f.hex, 'hex') - - assert.throws(function () { - ECSignature.parseCompact(buffer) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('toDER', function () { - fixtures.valid.forEach(function (f) { - it('exports ' + f.DER + ' correctly', function () { - var signature = new ECSignature(new BigInteger(f.signature.r), new BigInteger(f.signature.s)) - - var DER = signature.toDER() - assert.strictEqual(DER.toString('hex'), f.DER) - }) - }) - }) - - describe('fromDER', function () { - fixtures.valid.forEach(function (f) { - it('imports ' + f.DER + ' correctly', function () { - var buffer = Buffer.from(f.DER, 'hex') - var signature = ECSignature.fromDER(buffer) - - assert.strictEqual(signature.r.toString(), f.signature.r) - assert.strictEqual(signature.s.toString(), f.signature.s) - }) - }) - - fixtures.invalid.DER.forEach(function (f) { - it('throws "' + f.exception + '" for ' + f.hex, function () { - var buffer = Buffer.from(f.hex, 'hex') - - assert.throws(function () { - ECSignature.fromDER(buffer) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('toScriptSignature', function () { - fixtures.valid.forEach(function (f) { - it('exports ' + f.scriptSignature.hex + ' correctly', function () { - var signature = new ECSignature(new BigInteger(f.signature.r), new BigInteger(f.signature.s)) - - var scriptSignature = signature.toScriptSignature(f.scriptSignature.hashType) - assert.strictEqual(scriptSignature.toString('hex'), f.scriptSignature.hex) - }) - }) - - fixtures.invalid.scriptSignature.forEach(function (f) { - it('throws ' + f.exception, function () { - var signature = new ECSignature(new BigInteger(f.signature.r), new BigInteger(f.signature.s)) - - assert.throws(function () { - signature.toScriptSignature(f.hashType) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('parseScriptSignature', function () { - fixtures.valid.forEach(function (f) { - it('imports ' + f.scriptSignature.hex + ' correctly', function () { - var buffer = Buffer.from(f.scriptSignature.hex, 'hex') - var parsed = ECSignature.parseScriptSignature(buffer) - - assert.strictEqual(parsed.signature.r.toString(), f.signature.r) - assert.strictEqual(parsed.signature.s.toString(), f.signature.s) - assert.strictEqual(parsed.hashType, f.scriptSignature.hashType) - }) - }) - - fixtures.invalid.scriptSignature.forEach(function (f) { - it('throws on ' + f.hex, function () { - var buffer = Buffer.from(f.hex, 'hex') - - assert.throws(function () { - ECSignature.parseScriptSignature(buffer) - }, new RegExp(f.exception)) - }) - }) - }) -}) diff --git a/test/fixtures/ecsignature.json b/test/fixtures/ecsignature.json deleted file mode 100644 index ee948ad..0000000 --- a/test/fixtures/ecsignature.json +++ /dev/null @@ -1,214 +0,0 @@ -{ - "valid": [ - { - "compact": { - "hex": "1f33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262", - "compressed": true, - "i": 0 - }, - "scriptSignature": { - "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226201", - "hashType": 1 - }, - "DER": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262", - "signature": { - "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", - "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" - } - }, - { - "compact": { - "hex": "1b54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5", - "compressed": false, - "i": 0 - }, - "DER": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5", - "scriptSignature": { - "hex": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a502", - "hashType": 2 - }, - "signature": { - "r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", - "s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" - } - }, - { - "compact": { - "hex": "1fff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283", - "compressed": true, - "i": 0 - }, - "scriptSignature": { - "hex": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b28303", - "hashType": 3 - }, - "DER": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283", - "signature": { - "r": "115464191557905790016094131873849783294273568009648050793030031933291767741904", - "s": "50562520307781850052192542766631199590053690478900449960232079510155113443971" - } - }, - { - "compact": { - "hex": "1cc0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3", - "compressed": false, - "i": 1 - }, - "scriptSignature": { - "hex": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d381", - "hashType": 129 - }, - "DER": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3", - "signature": { - "r": "87230998027579607140680851455601772643840468630989315269459846730712163783123", - "s": "53231320085894623106179381504478252331065330583563809963303318469380290929875" - } - }, - { - "compact": { - "hex": "1f7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6", - "compressed": true, - "i": 0 - }, - "scriptSignature": { - "hex": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df682", - "hashType": 130 - }, - "DER": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6", - "signature": { - "r": "51348483531757779992459563033975330355971795607481991320287437101831125115997", - "s": "6277080015686056199074771961940657638578000617958603212944619747099038735862" - } - }, - { - "compact": { - "hex": "1cfbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37", - "compressed": false, - "i": 1 - }, - "scriptSignature": { - "hex": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd3783", - "hashType": 131 - }, - "DER": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37", - "signature": { - "r": "113979859486826658566290715281614250298918272782414232881639314569529560769671", - "s": "6517071009538626957379450615706485096874328019806177698938278220732027419959" - } - }, - { - "compact": { - "hex": "20cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef", - "compressed": true, - "i": 1 - }, - "scriptSignature": { - "hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef81", - "hashType": 129 - }, - "DER": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef", - "signature": { - "r": "93122007060065279508564838030979550535085999589142852106617159184757394422777", - "s": "3078539468410661027472930027406594684630312677495124015420811882501887769839" - } - } - ], - "invalid": { - "compact": [ - { - "exception": "Invalid signature parameter", - "hex": "23987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62" - }, - { - "exception": "Expected Buffer\\(Length: 65\\), got Buffer\\(Length: 68\\)", - "hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62000000" - }, - { - "exception": "Expected Buffer\\(Length: 65\\), got Buffer\\(Length: 59\\)", - "hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e9379" - } - ], - "DER": [ - { - "exception": "DER sequence length is too short", - "hex": "ffffffffffffff" - }, - { - "exception": "DER sequence length is too long", - "hex": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - }, - { - "exception": "Expected DER sequence", - "hex": "00ffff0400ffffff020400ffffff" - }, - { - "exception": "DER sequence length is invalid", - "hex": "30ff020400ffffff020400ffffff" - }, - { - "exception": "DER sequence length is invalid", - "hex": "300c030400ffffff030400ffffff0000" - }, - { - "exception": "Expected DER integer", - "hex": "300cff0400ffffff020400ffffff" - }, - { - "exception": "Expected DER integer \\(2\\)", - "hex": "300c020200ffffff020400ffffff" - }, - { - "exception": "R length is zero", - "hex": "30080200020400ffffff" - }, - { - "exception": "S length is zero", - "hex": "3008020400ffffff0200" - }, - { - "exception": "R length is too long", - "hex": "300c02dd00ffffff020400ffffff" - }, - { - "exception": "S length is invalid", - "hex": "300c020400ffffff02dd00ffffff" - }, - { - "exception": "R value is negative", - "hex": "300c020480000000020400ffffff" - }, - { - "exception": "S value is negative", - "hex": "300c020400ffffff020480000000" - }, - { - "exception": "R value excessively padded", - "hex": "300c02040000ffff020400ffffff" - }, - { - "exception": "S value excessively padded", - "hex": "300c020400ffffff02040000ffff" - } - ], - "scriptSignature": [ - { - "exception": "Invalid hashType 7", - "hashType": 7, - "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226207", - "signature": { - "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", - "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" - } - }, - { - "exception": "Invalid hashType 140", - "hashType": 140, - "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa543422628c", - "signature": { - "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", - "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" - } - } - ] - } -} From 90a73e195e5fb20af5aa3571b754c73601cd0675 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 13 Apr 2018 16:06:41 +1000 Subject: [PATCH 017/568] use safe-buffer --- package.json | 2 +- src/script_signature.js | 7 ++++--- test/{signature.js => script_signature.js} | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) rename test/{signature.js => script_signature.js} (90%) diff --git a/package.json b/package.json index f785872..0b73b4e 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "merkle-lib": "^2.0.10", "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", - "safe-buffer": "^5.0.1", + "safe-buffer": "^5.1.1", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" diff --git a/src/script_signature.js b/src/script_signature.js index f628c99..29344e8 100644 --- a/src/script_signature.js +++ b/src/script_signature.js @@ -1,5 +1,6 @@ var bip66 = require('bip66') var BigInteger = require('bigi') +var Buffer = require('safe-buffer').Buffer var typeforce = require('typeforce') var types = require('./types') @@ -39,11 +40,11 @@ function encode (signature, hashType) { var hashTypeMod = hashType & ~0x80 if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) - var hashTypeBuffer = new Buffer(1) + var hashTypeBuffer = Buffer.allocUnsafe(1) hashTypeBuffer.writeUInt8(hashType, 0) - var r = new Buffer(signature.r.toDERInteger()) - var s = new Buffer(signature.s.toDERInteger()) + var r = Buffer.from(signature.r.toDERInteger()) + var s = Buffer.from(signature.s.toDERInteger()) return Buffer.concat([ bip66.encode(r, s), diff --git a/test/signature.js b/test/script_signature.js similarity index 90% rename from test/signature.js rename to test/script_signature.js index 26b2136..d57e575 100644 --- a/test/signature.js +++ b/test/script_signature.js @@ -3,6 +3,7 @@ var assert = require('assert') var bscriptSig = require('../src/script').signature var BigInteger = require('bigi') +var Buffer = require('safe-buffer').Buffer var fixtures = require('./fixtures/signature.json') describe('Script Signatures', function () { @@ -45,7 +46,7 @@ describe('Script Signatures', function () { describe('decode', function () { fixtures.valid.forEach(function (f) { it('decodes ' + f.hex, function () { - var decode = bscriptSig.decode(new Buffer(f.hex, 'hex')) + var decode = bscriptSig.decode(Buffer.from(f.hex, 'hex')) assert.deepEqual(toRaw(decode.signature), f.raw) assert.strictEqual(decode.hashType, f.hashType) @@ -54,7 +55,7 @@ describe('Script Signatures', function () { fixtures.invalid.forEach(function (f) { it('throws on ' + f.hex, function () { - var buffer = new Buffer(f.hex, 'hex') + var buffer = Buffer.from(f.hex, 'hex') assert.throws(function () { bscriptSig.decode(buffer) From dd9455c0dfadfebc4ac06d9178d41ca243107cea Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 15 May 2018 09:58:10 +1000 Subject: [PATCH 018/568] rm unused bscript --- test/ecdsa.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/ecdsa.js b/test/ecdsa.js index a0e07d1..1f20a41 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -2,7 +2,6 @@ var assert = require('assert') var bcrypto = require('../src/crypto') -var bscript = require('../src/script') var ecdsa = require('../src/ecdsa') var hoodwink = require('hoodwink') From bd0f2503b2dc175e95ba33e498ff886e6abe8978 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 22 May 2018 13:04:05 +1000 Subject: [PATCH 019/568] drop Node 4,5 until LTS --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 390acdc..94e6343 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,18 @@ sudo: false language: node_js node_js: - - "4" - - "5" - "6" - "7" - "8" + - "9" + - "10" matrix: include: - - node_js: "7" + - node_js: "6" env: TEST_SUITE=standard - - node_js: "7" + - node_js: "6" env: TEST_SUITE=coverage env: - - TEST_SUITE=integration - TEST_SUITE=unit + - TEST_SUITE=integration script: npm run-script $TEST_SUITE From 884f3fd57d910637c7ae8ea8219d261571458cd9 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 15 May 2018 22:17:46 +1000 Subject: [PATCH 020/568] rm HDNode, use bip32 module --- README.md | 3 - package.json | 3 + src/hdnode.js | 316 ----------------------------- src/index.js | 9 +- test/fixtures/hdnode.json | 327 ------------------------------ test/hdnode.js | 397 ------------------------------------- test/integration/bip32.js | 47 +++-- test/integration/crypto.js | 28 ++- 8 files changed, 47 insertions(+), 1083 deletions(-) delete mode 100644 src/hdnode.js delete mode 100644 test/fixtures/hdnode.json delete mode 100644 test/hdnode.js diff --git a/README.md b/README.md index dfe8b7f..14faf3d 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,6 @@ npm install @types/bitcoinjs-lib ``` You can now use `bitcoinjs-lib` as a typescript compliant library. -``` javascript -import { HDNode, Transaction } from 'bitcoinjs-lib' -``` For VSCode (and other editors), users are advised to install the type declarations, as Intellisense uses that information to help you code (autocompletion, static analysis). diff --git a/package.json b/package.json index 0b73b4e..254289f 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "dependencies": { "bech32": "^1.1.2", "bigi": "^1.4.0", + "bip32": "0.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.3.0", "bs58check": "^2.0.0", @@ -42,11 +43,13 @@ "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", "safe-buffer": "^5.1.1", + "tiny-secp256k1": "0.0.5", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" }, "devDependencies": { + "bigi": "^1.4.2", "bip39": "^2.3.0", "bip65": "^1.0.1", "bs58": "^4.0.0", diff --git a/src/hdnode.js b/src/hdnode.js deleted file mode 100644 index a96b20c..0000000 --- a/src/hdnode.js +++ /dev/null @@ -1,316 +0,0 @@ -var Buffer = require('safe-buffer').Buffer -var base58check = require('bs58check') -var bcrypto = require('./crypto') -var createHmac = require('create-hmac') -var typeforce = require('typeforce') -var types = require('./types') -var NETWORKS = require('./networks') - -var BigInteger = require('bigi') -var ECPair = require('./ecpair') - -var ecurve = require('ecurve') -var curve = ecurve.getCurveByName('secp256k1') - -function HDNode (keyPair, chainCode) { - typeforce(types.tuple('ECPair', types.Buffer256bit), arguments) - - if (!keyPair.compressed) throw new TypeError('BIP32 only allows compressed keyPairs') - - this.keyPair = keyPair - this.chainCode = chainCode - this.depth = 0 - this.index = 0 - this.parentFingerprint = 0x00000000 -} - -HDNode.HIGHEST_BIT = 0x80000000 -HDNode.LENGTH = 78 -HDNode.MASTER_SECRET = Buffer.from('Bitcoin seed', 'utf8') - -HDNode.fromSeedBuffer = function (seed, network) { - typeforce(types.tuple(types.Buffer, types.maybe(types.Network)), arguments) - - if (seed.length < 16) throw new TypeError('Seed should be at least 128 bits') - if (seed.length > 64) throw new TypeError('Seed should be at most 512 bits') - - var I = createHmac('sha512', HDNode.MASTER_SECRET).update(seed).digest() - var IL = I.slice(0, 32) - var IR = I.slice(32) - - // In case IL is 0 or >= n, the master key is invalid - // This is handled by the ECPair constructor - var pIL = BigInteger.fromBuffer(IL) - var keyPair = new ECPair(pIL, null, { - network: network - }) - - return new HDNode(keyPair, IR) -} - -HDNode.fromSeedHex = function (hex, network) { - return HDNode.fromSeedBuffer(Buffer.from(hex, 'hex'), network) -} - -HDNode.fromBase58 = function (string, networks) { - var buffer = base58check.decode(string) - if (buffer.length !== 78) throw new Error('Invalid buffer length') - - // 4 bytes: version bytes - var version = buffer.readUInt32BE(0) - var network - - // list of networks? - if (Array.isArray(networks)) { - network = networks.filter(function (x) { - return version === x.bip32.private || - version === x.bip32.public - }).pop() - - if (!network) throw new Error('Unknown network version') - - // otherwise, assume a network object (or default to bitcoin) - } else { - network = networks || NETWORKS.bitcoin - } - - if (version !== network.bip32.private && - version !== network.bip32.public) throw new Error('Invalid network version') - - // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ... - var depth = buffer[4] - - // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) - var parentFingerprint = buffer.readUInt32BE(5) - if (depth === 0) { - if (parentFingerprint !== 0x00000000) throw new Error('Invalid parent fingerprint') - } - - // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. - // This is encoded in MSB order. (0x00000000 if master key) - var index = buffer.readUInt32BE(9) - if (depth === 0 && index !== 0) throw new Error('Invalid index') - - // 32 bytes: the chain code - var chainCode = buffer.slice(13, 45) - var keyPair - - // 33 bytes: private key data (0x00 + k) - if (version === network.bip32.private) { - if (buffer.readUInt8(45) !== 0x00) throw new Error('Invalid private key') - - var d = BigInteger.fromBuffer(buffer.slice(46, 78)) - keyPair = new ECPair(d, null, { network: network }) - - // 33 bytes: public key data (0x02 + X or 0x03 + X) - } else { - var Q = ecurve.Point.decodeFrom(curve, buffer.slice(45, 78)) - // Q.compressed is assumed, if somehow this assumption is broken, `new HDNode` will throw - - // Verify that the X coordinate in the public point corresponds to a point on the curve. - // If not, the extended public key is invalid. - curve.validate(Q) - - keyPair = new ECPair(null, Q, { network: network }) - } - - var hd = new HDNode(keyPair, chainCode) - hd.depth = depth - hd.index = index - hd.parentFingerprint = parentFingerprint - - return hd -} - -HDNode.prototype.getAddress = function () { - return this.keyPair.getAddress() -} - -HDNode.prototype.getIdentifier = function () { - return bcrypto.hash160(this.keyPair.getPublicKeyBuffer()) -} - -HDNode.prototype.getFingerprint = function () { - return this.getIdentifier().slice(0, 4) -} - -HDNode.prototype.getNetwork = function () { - return this.keyPair.getNetwork() -} - -HDNode.prototype.getPublicKeyBuffer = function () { - return this.keyPair.getPublicKeyBuffer() -} - -HDNode.prototype.neutered = function () { - var neuteredKeyPair = new ECPair(null, this.keyPair.Q, { - network: this.keyPair.network - }) - - var neutered = new HDNode(neuteredKeyPair, this.chainCode) - neutered.depth = this.depth - neutered.index = this.index - neutered.parentFingerprint = this.parentFingerprint - - return neutered -} - -HDNode.prototype.sign = function (hash) { - return this.keyPair.sign(hash) -} - -HDNode.prototype.verify = function (hash, signature) { - return this.keyPair.verify(hash, signature) -} - -HDNode.prototype.toBase58 = function (__isPrivate) { - if (__isPrivate !== undefined) throw new TypeError('Unsupported argument in 2.0.0') - - // Version - var network = this.keyPair.network - var version = (!this.isNeutered()) ? network.bip32.private : network.bip32.public - var buffer = Buffer.allocUnsafe(78) - - // 4 bytes: version bytes - buffer.writeUInt32BE(version, 0) - - // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, .... - buffer.writeUInt8(this.depth, 4) - - // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) - buffer.writeUInt32BE(this.parentFingerprint, 5) - - // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized. - // This is encoded in big endian. (0x00000000 if master key) - buffer.writeUInt32BE(this.index, 9) - - // 32 bytes: the chain code - this.chainCode.copy(buffer, 13) - - // 33 bytes: the public key or private key data - if (!this.isNeutered()) { - // 0x00 + k for private keys - buffer.writeUInt8(0, 45) - this.keyPair.d.toBuffer(32).copy(buffer, 46) - - // 33 bytes: the public key - } else { - // X9.62 encoding for public keys - this.keyPair.getPublicKeyBuffer().copy(buffer, 45) - } - - return base58check.encode(buffer) -} - -// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions -HDNode.prototype.derive = function (index) { - typeforce(types.UInt32, index) - - var isHardened = index >= HDNode.HIGHEST_BIT - var data = Buffer.allocUnsafe(37) - - // Hardened child - if (isHardened) { - if (this.isNeutered()) throw new TypeError('Could not derive hardened child key') - - // data = 0x00 || ser256(kpar) || ser32(index) - data[0] = 0x00 - this.keyPair.d.toBuffer(32).copy(data, 1) - data.writeUInt32BE(index, 33) - - // Normal child - } else { - // data = serP(point(kpar)) || ser32(index) - // = serP(Kpar) || ser32(index) - this.keyPair.getPublicKeyBuffer().copy(data, 0) - data.writeUInt32BE(index, 33) - } - - var I = createHmac('sha512', this.chainCode).update(data).digest() - var IL = I.slice(0, 32) - var IR = I.slice(32) - - var pIL = BigInteger.fromBuffer(IL) - - // In case parse256(IL) >= n, proceed with the next value for i - if (pIL.compareTo(curve.n) >= 0) { - return this.derive(index + 1) - } - - // Private parent key -> private child key - var derivedKeyPair - if (!this.isNeutered()) { - // ki = parse256(IL) + kpar (mod n) - var ki = pIL.add(this.keyPair.d).mod(curve.n) - - // In case ki == 0, proceed with the next value for i - if (ki.signum() === 0) { - return this.derive(index + 1) - } - - derivedKeyPair = new ECPair(ki, null, { - network: this.keyPair.network - }) - - // Public parent key -> public child key - } else { - // Ki = point(parse256(IL)) + Kpar - // = G*IL + Kpar - var Ki = curve.G.multiply(pIL).add(this.keyPair.Q) - - // In case Ki is the point at infinity, proceed with the next value for i - if (curve.isInfinity(Ki)) { - return this.derive(index + 1) - } - - derivedKeyPair = new ECPair(null, Ki, { - network: this.keyPair.network - }) - } - - var hd = new HDNode(derivedKeyPair, IR) - hd.depth = this.depth + 1 - hd.index = index - hd.parentFingerprint = this.getFingerprint().readUInt32BE(0) - - return hd -} - -HDNode.prototype.deriveHardened = function (index) { - typeforce(types.UInt31, index) - - // Only derives hardened private keys by default - return this.derive(index + HDNode.HIGHEST_BIT) -} - -// Private === not neutered -// Public === neutered -HDNode.prototype.isNeutered = function () { - return !(this.keyPair.d) -} - -HDNode.prototype.derivePath = function (path) { - typeforce(types.BIP32Path, path) - - var splitPath = path.split('/') - if (splitPath[0] === 'm') { - if (this.parentFingerprint) { - throw new Error('Not a master node') - } - - splitPath = splitPath.slice(1) - } - - return splitPath.reduce(function (prevHd, indexStr) { - var index - if (indexStr.slice(-1) === "'") { - index = parseInt(indexStr.slice(0, -1), 10) - return prevHd.deriveHardened(index) - } else { - index = parseInt(indexStr, 10) - return prevHd.derive(index) - } - }, this) -} - -module.exports = HDNode diff --git a/src/index.js b/src/index.js index e0c58cb..e1876f0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,18 +1,17 @@ -var script = require('./script') - -var templates = require('./templates') -for (var key in templates) { +let script = require('./script') +let templates = require('./templates') +for (let key in templates) { script[key] = templates[key] } module.exports = { Block: require('./block'), ECPair: require('./ecpair'), - HDNode: require('./hdnode'), Transaction: require('./transaction'), TransactionBuilder: require('./transaction_builder'), address: require('./address'), + bip32: require('bip32'), crypto: require('./crypto'), networks: require('./networks'), opcodes: require('bitcoin-ops'), diff --git a/test/fixtures/hdnode.json b/test/fixtures/hdnode.json deleted file mode 100644 index 56e97d6..0000000 --- a/test/fixtures/hdnode.json +++ /dev/null @@ -1,327 +0,0 @@ -{ - "valid": [ - { - "network": "bitcoin", - "master": { - "seed": "000102030405060708090a0b0c0d0e0f", - "wif": "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW", - "pubKey": "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", - "chainCode": "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508", - "base58": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", - "base58Priv": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - "identifier": "3442193e1bb70916e914552172cd4e2dbc9df811", - "fingerprint": "3442193e", - "address": "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma" - }, - "children": [ - { - "path": "m/0'", - "m": 0, - "hardened": true, - "wif": "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT", - "pubKey": "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56", - "chainCode": "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141", - "base58": "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", - "base58Priv": "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", - "identifier": "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7", - "fingerprint": "5c1bd648", - "address": "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh", - "index": 2147483648, - "depth": 1 - }, - { - "path": "m/0'/1", - "m": 1, - "wif": "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM", - "pubKey": "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c", - "chainCode": "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19", - "base58": "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", - "base58Priv": "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", - "identifier": "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe", - "fingerprint": "bef5a2f9", - "address": "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj", - "index": 1, - "depth": 2 - }, - { - "path": "m/0'/1/2'", - "m": 2, - "hardened": true, - "wif": "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU", - "pubKey": "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2", - "chainCode": "04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f", - "base58": "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", - "base58Priv": "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", - "identifier": "ee7ab90cde56a8c0e2bb086ac49748b8db9dce72", - "fingerprint": "ee7ab90c", - "address": "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x", - "index": 2147483650, - "depth": 3 - }, - { - "path": "m/0'/1/2'/2", - "m": 2, - "wif": "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR", - "pubKey": "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29", - "chainCode": "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd", - "base58": "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", - "base58Priv": "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", - "identifier": "d880d7d893848509a62d8fb74e32148dac68412f", - "fingerprint": "d880d7d8", - "address": "1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt", - "index": 2, - "depth": 4 - }, - { - "path": "m/0'/1/2'/2/1000000000", - "m": 1000000000, - "wif": "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs", - "pubKey": "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011", - "chainCode": "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e", - "base58": "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", - "base58Priv": "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", - "identifier": "d69aa102255fed74378278c7812701ea641fdf32", - "fingerprint": "d69aa102", - "address": "1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam", - "index": 1000000000, - "depth": 5 - } - ] - }, - { - "network": "bitcoin", - "master": { - "seed": "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", - "wif": "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy", - "pubKey": "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7", - "chainCode": "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689", - "base58": "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", - "base58Priv": "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", - "identifier": "bd16bee53961a47d6ad888e29545434a89bdfe95", - "fingerprint": "bd16bee5", - "address": "1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg" - }, - "children": [ - { - "path": "m/0", - "m": 0, - "wif": "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj", - "pubKey": "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea", - "chainCode": "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c", - "base58": "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", - "base58Priv": "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", - "identifier": "5a61ff8eb7aaca3010db97ebda76121610b78096", - "fingerprint": "5a61ff8e", - "address": "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ", - "index": 0, - "depth": 1 - }, - { - "path": "m/0/2147483647'", - "m": 2147483647, - "hardened": true, - "wif": "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr", - "pubKey": "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b", - "chainCode": "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9", - "base58": "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", - "base58Priv": "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", - "identifier": "d8ab493736da02f11ed682f88339e720fb0379d1", - "fingerprint": "d8ab4937", - "address": "1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk", - "index": 4294967295, - "depth": 2 - }, - { - "path": "m/0/2147483647'/1", - "m": 1, - "wif": "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2", - "pubKey": "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9", - "chainCode": "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb", - "base58": "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", - "base58Priv": "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", - "identifier": "78412e3a2296a40de124307b6485bd19833e2e34", - "fingerprint": "78412e3a", - "address": "1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW", - "index": 1, - "depth": 3 - }, - { - "path": "m/0/2147483647'/1/2147483646'", - "m": 2147483646, - "hardened": true, - "wif": "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF", - "pubKey": "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0", - "chainCode": "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29", - "base58": "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", - "base58Priv": "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", - "identifier": "31a507b815593dfc51ffc7245ae7e5aee304246e", - "fingerprint": "31a507b8", - "address": "15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R", - "index": 4294967294, - "depth": 4 - }, - { - "path": "m/0/2147483647'/1/2147483646'/2", - "m": 2, - "wif": "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK", - "pubKey": "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", - "chainCode": "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", - "base58": "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", - "base58Priv": "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", - "identifier": "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220", - "fingerprint": "26132fdb", - "address": "14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt", - "index": 2, - "depth": 5 - } - ] - }, - { - "comment": "Private key has leading zeros, see PR #673", - "network": "bitcoin", - "master": { - "seed": "d13de7bd1e54422d1a3b3b699a27fb460de2849e7e66a005c647e8e4a54075cb", - "wif": "KwDiCU5bs8xQwsRgxjhkcJcVuR7NE4Mei8X9uSAVviVTE7JmMoS6", - "pubKey": "0298ccc720d5dea817c7077605263bae52bca083cf8888fee77ff4c1b4797ee180", - "chainCode": "c23ab32b36ddff49fae350a1bed8ec6b4d9fc252238dd789b7273ba4416054eb", - "base58": "xpub661MyMwAqRbcGUbHLLJ5n2DzFAt8mmaDxbmbdimh68m8EiXGEQPiJya4BJat5yMzy4e68VSUoLGCu5uvzf8dUoGvwuJsLE6F1cibmWsxFNn", - "base58Priv": "xprv9s21ZrQH143K3zWpEJm5QtHFh93eNJrNbNqzqLN5XoE9MvC7gs5TmBFaL2PpaXpDc8FBYVe5EChc73ApjSQ5fWsXS7auHy1MmG6hdpywE1q", - "identifier": "1a87677be6f73cc9655e8b4c5d2fd0aeeb1b23c7", - "fingerprint": "1a87677b", - "address": "KyDarNhq8WK8rSU36UY7bDv9MAwdpKFZYKPN89Geh2dUwHjTqVh5" - }, - "children": [ - { - "path": "m/44'/0'/0'/0/0'", - "wif": "L3z3MSqZtDQ1FPHKi7oWf1nc9rMEGFtZUDCoFa7n4F695g5qZiSu", - "pubKey": "027c3591221e28939e45f8ea297d62c3640ebb09d7058b01d09c963d984a40ad49", - "chainCode": "ca27553aa89617e982e621637d6478f564b32738f8bbe2e48d0a58a8e0f6da40", - "base58": "xpub6GcBnm7FfDg5ERWACCvtuotN6Tdoc37r3SZ1asBHvCWzPkqWn3MVKPWKzy6GsfmdMUGanR3D12dH1cp5tJauuubwc4FAJDn67SH2uUjwAT1", - "base58Priv": "xprvA3cqPFaMpr7n1wRh6BPtYfwdYRoKCaPzgDdQnUmgMrz1WxWNEW3EmbBr9ieh9BJAsRGKFPLvotb4p4Aq79jddUVKPVJt7exVzLHcv777JVf", - "identifier": "e371d69b5dae6eacee832a130ee9f55545275a09", - "fingerprint": "e371d69b", - "address": "1MjcmArHeqorgm9uJi4kPNQ6CbsrmCtASH", - "index": 2147483648, - "depth": 5 - } - ] - }, - { - "network": "litecoin", - "master": { - "seed": "000102030405060708090a0b0c0d0e0f", - "wif": "TAroS5Knm8GZcnpPycBgzjwwDLWMyQjDrcuGPPoArgrbW7Ln22qp", - "pubKey": "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", - "chainCode": "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508", - "base58": "Ltub2SSUS19CirucWFod2ZsYA2J4v4U76YiCXHdcQttnoiy5aGanFHCPDBX7utfG6f95u1cUbZJNafmvzNCzZZJTw1EmyFoL8u1gJbGM8ipu491", - "base58Priv": "Ltpv71G8qDifUiNetP6nmxPA5STrUVmv2J9YSmXajv8VsYBUyuPhvN9xCaQrfX2wo5xxJNtEazYCFRUu5FmokYMM79pcqz8pcdo4rNXAFPgyB4k", - "identifier": "3442193e1bb70916e914552172cd4e2dbc9df811", - "fingerprint": "3442193e", - "address": "LPzGaoLUtXFkmNo3u1chDxGxDnSaBQTTxm" - }, - "children": [ - { - "path": "m/0'", - "m": 0, - "hardened": true, - "wif": "TB22qU2V9EJCVKJ8cdYaTfvDhnYcCzthcWgFm1k6hbvbKM1NLxoL", - "pubKey": "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56", - "chainCode": "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141", - "base58": "Ltub2UhtRiSfp82berwLEKkB34QBEt2TUdCDCu4WNzGumvAMwYsxfWjULKsXhADxqy3cuDu3TnqoKJr1xmB8Wb2qzthWAtbb4CutpXPuSU1YMgG", - "base58Priv": "Ltpv73XYpw28ZyVe2zEVyiFnxUZxoKLGQNdZ8NxUi1WcqjNmMBgtLbh3KimGSnPHCoLv1RmvxHs4dnKmo1oXQ8dXuDu8uroxrbVxZPA1gXboYvx", - "identifier": "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7", - "fingerprint": "5c1bd648", - "address": "LTcyn1jun6g9hvxtsT7cqMRSyix7AULC76", - "index": 2147483648, - "depth": 1 - } - ] - } - ], - "invalid": { - "fromBase58": [ - { - "exception": "Invalid checksum", - "string": "xprvQQQQQQQQQQQQQQQQCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334" - }, - { - "exception": "Invalid buffer length", - "network": "bitcoin", - "string": "HAsbc6CgKmTYEQg2CTz7m5STEPAB" - }, - { - "exception": "Invalid parent fingerprint", - "network": "bitcoin", - "string": "xprv9tnJFvAXAXPfPnMTKfwpwnkty7MzJwELVgp4NTBquaKXy4RndyfJJCJJf7zNaVpBpzrwVRutZNLRCVLEcZHcvuCNG3zGbGBcZn57FbNnmSP" - }, - { - "exception": "Invalid private key", - "network": "bitcoin", - "string": "xprv9s21ZrQH143K3yLysFvsu3n1dMwhNusmNHr7xArzAeCc7MQYqDBBStmqnZq6WLi668siBBNs3SjiyaexduHu9sXT9ixTsqptL67ADqcaBdm" - }, - { - "exception": "Invalid index", - "network": "bitcoin", - "string": "xprv9s21ZrQYdgnodnKW4Drm1Qg7poU6Gf2WUDsjPxvYiK7iLBMrsjbnF1wsZZQgmXNeMSG3s7jmHk1b3JrzhG5w8mwXGxqFxfrweico7k8DtxR" - }, - { - "exception": "Unknown network version", - "string": "1111111111111adADjFaSNPxwXqLjHLj4mBfYxuewDPbw9hEj1uaXCzMxRPXDFF3cUoezTFYom4sEmEVSQmENPPR315cFk9YUFVek73wE9" - }, - { - "exception": "Invalid network version", - "network": "bitcoin", - "string": "Ltpv73XYpw28ZyVe2zEVyiFnxUZxoKLGQNdZ8NxUi1WcqjNmMBgtLbh3KimGSnPHCoLv1RmvxHs4dnKmo1oXQ8dXuDu8uroxrbVxZPA1gXboYvx" - }, - { - "exception": "Invalid buffer length", - "string": "9XpNiB4DberdMn4jZiMhNGtuZUd7xUrCEGw4MG967zsVNvUKBEC9XLrmVmFasanWGp15zXfTNw4vW4KdvUAynEwyKjdho9QdLMPA2H5uyt" - }, - { - "exception": "Invalid buffer length", - "string": "7JJikZQ2NUXjSAnAF2SjFYE3KXbnnVxzRBNddFE1DjbDEHVGEJzYC7zqSgPoauBJS3cWmZwsER94oYSFrW9vZ4Ch5FtGeifdzmtS3FGYDB1vxFZsYKgMc" - }, - { - "exception": "Invalid parent fingerprint", - "string": "xpub67tVq9SuNQCfm2PXBqjGRAtNZ935kx2uHJaURePth4JBpMfEy6jum7Euj7FTpbs7fnjhfZcNEktCucWHcJf74dbKLKNSTZCQozdDVwvkJhs" - }, - { - "exception": "Invalid index", - "string": "xpub661MyMwTWkfYZq6BEh3ywGVXFvNj5hhzmWMhFBHSqmub31B1LZ9wbJ3DEYXZ8bHXGqnHKfepTud5a2XxGdnnePzZa2m2DyzTnFGBUXtaf9M" - }, - { - "exception": "Unknown network version", - "string": "8FH81Rao5EgGmdScoN66TJAHsQP7phEMeyMTku9NBJd7hXgaj3HTvSNjqJjoqBpxdbuushwPEM5otvxXt2p9dcw33AqNKzZEPMqGHmz7Dpayi6Vb" - }, - { - "exception": "Point is not on the curve", - "string": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gYymDsxxRe3WWeZQ7TadaLSdKUffezzczTCpB8j3JP96UwE2n6w1" - } - ], - "deriveHardened": [ - 2147483648, - null, - "foo", - -1 - ], - "derive": [ - 4294967296, - null, - "foo", - -1 - ], - "derivePath": [ - 2, - [ - 2, - 3, - 4 - ], - "/", - "m/m/123", - "a/0/1/2", - "m/0/ 1 /2", - "m/0/1.5/2" - ] - } -} diff --git a/test/hdnode.js b/test/hdnode.js deleted file mode 100644 index 42a2d14..0000000 --- a/test/hdnode.js +++ /dev/null @@ -1,397 +0,0 @@ -/* global describe, it, beforeEach */ -/* eslint-disable no-new */ - -var assert = require('assert') -var ecdsa = require('../src/ecdsa') -var hoodwink = require('hoodwink') - -var BigInteger = require('bigi') -var ECPair = require('../src/ecpair') -var HDNode = require('../src/hdnode') - -var fixtures = require('./fixtures/hdnode.json') -var curve = ecdsa.__curve - -var NETWORKS = require('../src/networks') -var NETWORKS_LIST = [] // Object.values(NETWORKS) -for (var networkName in NETWORKS) { - NETWORKS_LIST.push(NETWORKS[networkName]) -} - -var validAll = [] -fixtures.valid.forEach(function (f) { - function addNetwork (n) { - n.network = f.network - return n - } - - validAll = validAll.concat(addNetwork(f.master), f.children.map(addNetwork)) -}) - -describe('HDNode', function () { - describe('Constructor', function () { - var keyPair, chainCode - - beforeEach(function () { - var d = BigInteger.ONE - - keyPair = new ECPair(d, null) - chainCode = Buffer.alloc(32, 1) - }) - - it('stores the keyPair/chainCode directly', function () { - var hd = new HDNode(keyPair, chainCode) - - assert.strictEqual(hd.keyPair, keyPair) - assert.strictEqual(hd.chainCode, chainCode) - }) - - it('has a default depth/index of 0', function () { - var hd = new HDNode(keyPair, chainCode) - - assert.strictEqual(hd.depth, 0) - assert.strictEqual(hd.index, 0) - }) - - it('throws on uncompressed keyPair', function () { - keyPair.compressed = false - - assert.throws(function () { - new HDNode(keyPair, chainCode) - }, /BIP32 only allows compressed keyPairs/) - }) - - it('throws when an invalid length chain code is given', function () { - assert.throws(function () { - new HDNode(keyPair, Buffer.alloc(20)) - }, /Expected property "1" of type Buffer\(Length: 32\), got Buffer\(Length: 20\)/) - }) - }) - - describe('fromSeed*', function () { - fixtures.valid.forEach(function (f) { - it('calculates privKey and chainCode for ' + f.master.fingerprint, function () { - var network = NETWORKS[f.network] - var hd = HDNode.fromSeedHex(f.master.seed, network) - - assert.strictEqual(hd.keyPair.toWIF(), f.master.wif) - assert.strictEqual(hd.chainCode.toString('hex'), f.master.chainCode) - }) - }) - - it('throws if IL is not within interval [1, n - 1] | IL === 0', hoodwink(function () { - this.mock(BigInteger, 'fromBuffer', function () { - return BigInteger.ZERO - }, 1) - - assert.throws(function () { - HDNode.fromSeedHex('ffffffffffffffffffffffffffffffff') - }, /Private key must be greater than 0/) - })) - - it('throws if IL is not within interval [1, n - 1] | IL === n', hoodwink(function () { - this.mock(BigInteger, 'fromBuffer', function () { - return curve.n - }, 1) - - assert.throws(function () { - HDNode.fromSeedHex('ffffffffffffffffffffffffffffffff') - }, /Private key must be less than the curve order/) - })) - - it('throws on low entropy seed', function () { - assert.throws(function () { - HDNode.fromSeedHex('ffffffffff') - }, /Seed should be at least 128 bits/) - }) - - it('throws on too high entropy seed', function () { - assert.throws(function () { - HDNode.fromSeedHex('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') - }, /Seed should be at most 512 bits/) - }) - }) - - describe('ECPair wrappers', function () { - var keyPair, hd, hash - - beforeEach(function () { - keyPair = ECPair.makeRandom() - hash = Buffer.alloc(32) - - var chainCode = Buffer.alloc(32) - hd = new HDNode(keyPair, chainCode) - }) - - describe('getAddress', function () { - it('wraps keyPair.getAddress', hoodwink(function () { - this.mock(hd.keyPair, 'getAddress', function () { - return 'foo' - }, 1) - - assert.strictEqual(hd.getAddress(), 'foo') - })) - }) - - describe('getNetwork', function () { - it('wraps keyPair.getNetwork', hoodwink(function () { - this.mock(hd.keyPair, 'getNetwork', function () { - return 'foo' - }, 1) - - assert.strictEqual(hd.getNetwork(), 'foo') - })) - }) - - describe('getPublicKeyBuffer', function () { - it('wraps keyPair.getPublicKeyBuffer', hoodwink(function () { - this.mock(hd.keyPair, 'getPublicKeyBuffer', function () { - return 'foo' - }, 1) - - assert.strictEqual(hd.getPublicKeyBuffer(), 'foo') - })) - }) - - describe('sign', function () { - it('wraps keyPair.sign', hoodwink(function () { - this.mock(hd.keyPair, 'sign', function (h) { - assert.strictEqual(hash, h) - return 'foo' - }, 1) - - assert.strictEqual(hd.sign(hash), 'foo') - })) - }) - - describe('verify', function () { - var signature - - beforeEach(function () { - signature = hd.sign(hash) - }) - - it('wraps keyPair.verify', hoodwink(function () { - this.mock(hd.keyPair, 'verify', function (h, s) { - assert.strictEqual(hash, h) - assert.strictEqual(signature, s) - return 'foo' - }, 1) - - assert.strictEqual(hd.verify(hash, signature), 'foo') - })) - }) - }) - - describe('fromBase58 / toBase58', function () { - validAll.forEach(function (f) { - it('exports ' + f.base58 + ' (public) correctly', function () { - var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) - - assert.strictEqual(hd.toBase58(), f.base58) - assert.throws(function () { hd.keyPair.toWIF() }, /Missing private key/) - }) - }) - - validAll.forEach(function (f) { - it('exports ' + f.base58Priv + ' (private) correctly', function () { - var hd = HDNode.fromBase58(f.base58Priv, NETWORKS_LIST) - - assert.strictEqual(hd.toBase58(), f.base58Priv) - assert.strictEqual(hd.keyPair.toWIF(), f.wif) - }) - }) - - fixtures.invalid.fromBase58.forEach(function (f) { - it('throws on ' + f.string, function () { - assert.throws(function () { - var networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST - - HDNode.fromBase58(f.string, networks) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('getIdentifier', function () { - validAll.forEach(function (f) { - it('returns the identifier for ' + f.fingerprint, function () { - var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) - - assert.strictEqual(hd.getIdentifier().toString('hex'), f.identifier) - }) - }) - }) - - describe('getFingerprint', function () { - validAll.forEach(function (f) { - it('returns the fingerprint for ' + f.fingerprint, function () { - var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) - - assert.strictEqual(hd.getFingerprint().toString('hex'), f.fingerprint) - }) - }) - }) - - describe('neutered / isNeutered', function () { - validAll.forEach(function (f) { - it('drops the private key for ' + f.fingerprint, function () { - var hd = HDNode.fromBase58(f.base58Priv, NETWORKS_LIST) - var hdn = hd.neutered() - - assert.notEqual(hdn.keyPair, hd.keyPair) - assert.throws(function () { hdn.keyPair.toWIF() }, /Missing private key/) - assert.strictEqual(hdn.toBase58(), f.base58) - assert.strictEqual(hdn.chainCode, hd.chainCode) - assert.strictEqual(hdn.depth, f.depth >>> 0) - assert.strictEqual(hdn.index, f.index >>> 0) - assert.strictEqual(hdn.isNeutered(), true) - - // does not modify the original - assert.strictEqual(hd.toBase58(), f.base58Priv) - assert.strictEqual(hd.isNeutered(), false) - }) - }) - }) - - describe('derive', function () { - function verifyVector (hd, v) { - if (hd.isNeutered()) { - assert.strictEqual(hd.toBase58(), v.base58) - } else { - assert.strictEqual(hd.neutered().toBase58(), v.base58) - assert.strictEqual(hd.toBase58(), v.base58Priv) - } - - assert.strictEqual(hd.getFingerprint().toString('hex'), v.fingerprint) - assert.strictEqual(hd.getIdentifier().toString('hex'), v.identifier) - assert.strictEqual(hd.getAddress(), v.address) - assert.strictEqual(hd.keyPair.toWIF(), v.wif) - assert.strictEqual(hd.keyPair.getPublicKeyBuffer().toString('hex'), v.pubKey) - assert.strictEqual(hd.chainCode.toString('hex'), v.chainCode) - assert.strictEqual(hd.depth, v.depth >>> 0) - assert.strictEqual(hd.index, v.index >>> 0) - } - - fixtures.valid.forEach(function (f) { - var network = NETWORKS[f.network] - var hd = HDNode.fromSeedHex(f.master.seed, network) - var master = hd - - // testing deriving path from master - f.children.forEach(function (c) { - it(c.path + ' from ' + f.master.fingerprint + ' by path', function () { - var child = master.derivePath(c.path) - var childNoM = master.derivePath(c.path.slice(2)) // no m/ on path - - verifyVector(child, c) - verifyVector(childNoM, c) - }) - }) - - // testing deriving path from children - f.children.forEach(function (c, i) { - var cn = master.derivePath(c.path) - - f.children.slice(i + 1).forEach(function (cc) { - it(cc.path + ' from ' + c.fingerprint + ' by path', function () { - var ipath = cc.path.slice(2).split('/').slice(i + 1).join('/') - var child = cn.derivePath(ipath) - verifyVector(child, cc) - - assert.throws(function () { - cn.derivePath('m/' + ipath) - }, /Not a master node/) - }) - }) - }) - - // FIXME: test data is only testing Private -> private for now - f.children.forEach(function (c) { - if (c.m === undefined) return - - it(c.path + ' from ' + f.master.fingerprint, function () { - if (c.hardened) { - hd = hd.deriveHardened(c.m) - } else { - hd = hd.derive(c.m) - } - - verifyVector(hd, c) - }) - }) - }) - - it('works for Private -> public (neutered)', function () { - var f = fixtures.valid[1] - var c = f.children[0] - - var master = HDNode.fromBase58(f.master.base58Priv, NETWORKS_LIST) - var child = master.derive(c.m).neutered() - - assert.strictEqual(child.toBase58(), c.base58) - }) - - it('works for Private -> public (neutered, hardened)', function () { - var f = fixtures.valid[0] - var c = f.children[0] - - var master = HDNode.fromBase58(f.master.base58Priv, NETWORKS_LIST) - var child = master.deriveHardened(c.m).neutered() - - assert.strictEqual(c.base58, child.toBase58()) - }) - - it('works for Public -> public', function () { - var f = fixtures.valid[1] - var c = f.children[0] - - var master = HDNode.fromBase58(f.master.base58, NETWORKS_LIST) - var child = master.derive(c.m) - - assert.strictEqual(c.base58, child.toBase58()) - }) - - it('throws on Public -> public (hardened)', function () { - var f = fixtures.valid[0] - var c = f.children[0] - - var master = HDNode.fromBase58(f.master.base58, NETWORKS_LIST) - - assert.throws(function () { - master.deriveHardened(c.m) - }, /Could not derive hardened child key/) - }) - - it('throws on wrong types', function () { - var f = fixtures.valid[0] - var master = HDNode.fromBase58(f.master.base58, NETWORKS_LIST) - - fixtures.invalid.derive.forEach(function (fx) { - assert.throws(function () { - master.derive(fx) - }, /Expected UInt32/) - }) - - fixtures.invalid.deriveHardened.forEach(function (fx) { - assert.throws(function () { - master.deriveHardened(fx) - }, /Expected UInt31/) - }) - - fixtures.invalid.derivePath.forEach(function (fx) { - assert.throws(function () { - master.derivePath(fx) - }, /Expected BIP32 derivation path/) - }) - }) - - it('works when private key has leading zeros', function () { - var key = 'xprv9s21ZrQH143K3ckY9DgU79uMTJkQRLdbCCVDh81SnxTgPzLLGax6uHeBULTtaEtcAvKjXfT7ZWtHzKjTpujMkUd9dDb8msDeAfnJxrgAYhr' - var hdkey = HDNode.fromBase58(key) - assert.strictEqual(hdkey.keyPair.d.toBuffer(32).toString('hex'), '00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd') - var child = hdkey.derivePath('m/44\'/0\'/0\'/0/0\'') - assert.strictEqual(child.keyPair.d.toBuffer().toString('hex'), '3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb') - }) - }) -}) diff --git a/test/integration/bip32.js b/test/integration/bip32.js index 1908166..b7f0bb2 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -1,32 +1,39 @@ /* global describe, it */ var assert = require('assert') +let bip32 = require('bip32') var bip39 = require('bip39') var bitcoin = require('../../') +var baddress = bitcoin.address +var bcrypto = bitcoin.crypto +function getAddress (node) { + return baddress.toBase58Check(bcrypto.hash160(node.publicKey), bitcoin.networks.bitcoin.pubKeyHash) +} + describe('bitcoinjs-lib (BIP32)', function () { it('can import a BIP32 testnet xpriv and export to WIF', function () { var xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' - var node = bitcoin.HDNode.fromBase58(xpriv, bitcoin.networks.testnet) + var node = bip32.fromBase58(xpriv, bitcoin.networks.testnet) - assert.equal(node.keyPair.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') + assert.equal(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') }) it('can export a BIP32 xpriv, then import it', function () { var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' var seed = bip39.mnemonicToSeed(mnemonic) - var node = bitcoin.HDNode.fromSeedBuffer(seed) + var node = bip32.fromSeed(seed) var string = node.toBase58() - var restored = bitcoin.HDNode.fromBase58(string) + var restored = bip32.fromBase58(string) - assert.equal(node.getAddress(), restored.getAddress()) // same public key - assert.equal(node.keyPair.toWIF(), restored.keyPair.toWIF()) // same private key + assert.equal(getAddress(node), getAddress(restored)) // same public key + assert.equal(node.toWIF(), restored.toWIF()) // same private key }) it('can export a BIP32 xpub', function () { var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' var seed = bip39.mnemonicToSeed(mnemonic) - var node = bitcoin.HDNode.fromSeedBuffer(seed) + var node = bip32.fromSeed(seed) var string = node.neutered().toBase58() assert.equal(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') @@ -34,7 +41,7 @@ describe('bitcoinjs-lib (BIP32)', function () { it('can create a BIP32, bitcoin, account 0, external address', function () { var path = "m/0'/0/0" - var root = bitcoin.HDNode.fromSeedHex('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd') + var root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) var child1 = root.derivePath(path) @@ -43,12 +50,12 @@ describe('bitcoinjs-lib (BIP32)', function () { .derive(0) .derive(0) - assert.equal(child1.getAddress(), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') - assert.equal(child1b.getAddress(), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') + assert.equal(getAddress(child1), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') + assert.equal(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') }) it('can create a BIP44, bitcoin, account 0, external address', function () { - var root = bitcoin.HDNode.fromSeedHex('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd') + var root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) var child1 = root.derivePath("m/44'/0'/0'/0/0") @@ -59,19 +66,19 @@ describe('bitcoinjs-lib (BIP32)', function () { .derive(0) .derive(0) - assert.equal(child1.getAddress(), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') - assert.equal(child1b.getAddress(), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') + assert.equal(getAddress(child1), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') + assert.equal(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') }) it('can create a BIP49, bitcoin testnet, account 0, external address', function () { var mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' var seed = bip39.mnemonicToSeed(mnemonic) - var root = bitcoin.HDNode.fromSeedBuffer(seed) + var root = bip32.fromSeed(seed) var path = "m/49'/1'/0'/0/0" var child = root.derivePath(path) - var keyhash = bitcoin.crypto.hash160(child.getPublicKeyBuffer()) + var keyhash = bitcoin.crypto.hash160(child.publicKey) var scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash) var addressBytes = bitcoin.crypto.hash160(scriptSig) var outputScript = bitcoin.script.scriptHash.output.encode(addressBytes) @@ -86,14 +93,14 @@ describe('bitcoinjs-lib (BIP32)', function () { assert(bip39.validateMnemonic(mnemonic)) var seed = bip39.mnemonicToSeed(mnemonic) - var root = bitcoin.HDNode.fromSeedBuffer(seed) + var root = bip32.fromSeed(seed) // receive addresses - assert.strictEqual(root.derivePath("m/0'/0/0").getAddress(), '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC') - assert.strictEqual(root.derivePath("m/0'/0/1").getAddress(), '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL') + assert.strictEqual(getAddress(root.derivePath("m/0'/0/0")), '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC') + assert.strictEqual(getAddress(root.derivePath("m/0'/0/1")), '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL') // change addresses - assert.strictEqual(root.derivePath("m/0'/1/0").getAddress(), '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn') - assert.strictEqual(root.derivePath("m/0'/1/1").getAddress(), '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6') + assert.strictEqual(getAddress(root.derivePath("m/0'/1/0")), '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn') + assert.strictEqual(getAddress(root.derivePath("m/0'/1/1")), '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6') }) }) diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 183c50e..4b53769 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -3,7 +3,9 @@ var assert = require('assert') var bigi = require('bigi') var bitcoin = require('../../') +var bip32 = require('bip32') var crypto = require('crypto') +var tinysecp = require('tiny-secp256k1') var ecurve = require('ecurve') var secp256k1 = ecurve.getCurveByName('secp256k1') @@ -68,35 +70,31 @@ describe('bitcoinjs-lib (crypto)', function () { it('can recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key', function () { function recoverParent (master, child) { - assert(!master.keyPair.d, 'You already have the parent private key') - assert(child.keyPair.d, 'Missing child private key') + assert(master.isNeutered(), 'You already have the parent private key') + assert(!child.isNeutered(), 'Missing child private key') - var curve = secp256k1 - var QP = master.keyPair.Q - var serQP = master.keyPair.getPublicKeyBuffer() - - var d1 = child.keyPair.d + var serQP = master.publicKey + var d1 = child.privateKey var d2 var data = Buffer.alloc(37) serQP.copy(data, 0) // search index space until we find it - for (var i = 0; i < bitcoin.HDNode.HIGHEST_BIT; ++i) { + for (var i = 0; i < 0x80000000; ++i) { data.writeUInt32BE(i, 33) // calculate I var I = crypto.createHmac('sha512', master.chainCode).update(data).digest() var IL = I.slice(0, 32) - var pIL = bigi.fromBuffer(IL) - // See hdnode.js:273 to understand - d2 = d1.subtract(pIL).mod(curve.n) + // See bip32.js:273 to understand + d2 = tinysecp.privateSub(d1, IL) - var Qp = new bitcoin.ECPair(d2).Q - if (Qp.equals(QP)) break + var Qp = bip32.fromPrivateKey(d2, Buffer.alloc(32, 0)).publicKey + if (Qp.equals(serQP)) break } - var node = new bitcoin.HDNode(new bitcoin.ECPair(d2), master.chainCode, master.network) + var node = bip32.fromPrivateKey(d2, master.chainCode, master.network) node.depth = master.depth node.index = master.index node.masterFingerprint = master.masterFingerprint @@ -104,7 +102,7 @@ describe('bitcoinjs-lib (crypto)', function () { } var seed = crypto.randomBytes(32) - var master = bitcoin.HDNode.fromSeedBuffer(seed) + var master = bip32.fromSeed(seed) var child = master.derive(6) // m/6 // now for the recovery From b267a9e6063478f7736536e9acbd0c9ac808b3c9 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 22 May 2018 14:05:23 +1000 Subject: [PATCH 021/568] rm potential race conditions in non-final mixed test --- test/integration/cltv.js | 47 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 82c629e..547ac79 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -105,28 +105,29 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { ], redeemScript) tx.setInputScript(0, redeemScriptSig) - regtestUtils.broadcast(tx.toHex(), function (err) { - // fails before the expiry - assert.throws(function () { - if (err) throw err - }, /Error: 64: non-final/) + // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently +// regtestUtils.broadcast(tx.toHex(), function (err) { +// // fails before the expiry +// assert.throws(function () { +// if (err) throw err +// }, /Error: 64: non-final/) - // into the future! - regtestUtils.mine(51, function (err) { + // into the future! + regtestUtils.mine(51, function (err) { + if (err) return done(err) + + regtestUtils.broadcast(tx.toHex(), function (err) { if (err) return done(err) - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) - - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4 - }, done) - }) + regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4 + }, done) }) }) +// }) }) }) }) @@ -136,8 +137,8 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { this.timeout(30000) // two hours ago - var timeUtc = utcNow() - (3600 * 2) - var redeemScript = cltvCheckSigOutput(alice, bob, timeUtc) + var lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) + var redeemScript = cltvCheckSigOutput(alice, bob, lockTime) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) @@ -146,7 +147,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { if (err) return done(err) var txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(timeUtc) + txb.setLockTime(lockTime) txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4) @@ -178,8 +179,8 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { this.timeout(30000) // two hours from now - var timeUtc = utcNow() + (3600 * 2) - var redeemScript = cltvCheckSigOutput(alice, bob, timeUtc) + var lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) + var redeemScript = cltvCheckSigOutput(alice, bob, lockTime) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) @@ -188,7 +189,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { if (err) return done(err) var txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(timeUtc) + txb.setLockTime(lockTime) txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) From 6c4977983d7b453fb724c7b85b1843f395f6e4b9 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 15 May 2018 10:07:11 +1000 Subject: [PATCH 022/568] ECPair/script_signature: switch to 64-byte RS buffers only --- src/ecpair.js | 8 ++++- src/script_signature.js | 70 +++++++++++++++++++----------------- src/transaction_builder.js | 4 +-- test/ecpair.js | 7 +++- test/fixtures/signature.json | 36 +++++++++---------- test/integration/crypto.js | 12 ++++--- test/script_signature.js | 13 ++++--- 7 files changed, 82 insertions(+), 68 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index 960f270..cfc3649 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -115,7 +115,8 @@ ECPair.prototype.getPublicKeyBuffer = function () { ECPair.prototype.sign = function (hash) { if (!this.d) throw new Error('Missing private key') - return ecdsa.sign(hash, this.d) + let signature = ecdsa.sign(hash, this.d) + return Buffer.concat([signature.r.toBuffer(32), signature.s.toBuffer(32)], 64) } ECPair.prototype.toWIF = function () { @@ -125,6 +126,11 @@ ECPair.prototype.toWIF = function () { } ECPair.prototype.verify = function (hash, signature) { + signature = { + r: BigInteger.fromBuffer(signature.slice(0, 32)), + s: BigInteger.fromBuffer(signature.slice(32, 64)) + } + return ecdsa.verify(hash, signature, this.Q) } diff --git a/src/script_signature.js b/src/script_signature.js index 29344e8..2d915d4 100644 --- a/src/script_signature.js +++ b/src/script_signature.js @@ -1,50 +1,56 @@ -var bip66 = require('bip66') -var BigInteger = require('bigi') -var Buffer = require('safe-buffer').Buffer -var typeforce = require('typeforce') -var types = require('./types') +let bip66 = require('bip66') +let Buffer = require('safe-buffer').Buffer +let typeforce = require('typeforce') +let types = require('./types') + +let ZERO = Buffer.alloc(1, 0) +function toDER (x) { + let i = 0 + while (x[i] === 0) ++i + if (i === x.length) return ZERO + x = x.slice(i) + if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length) + return x +} + +function fromDER (x) { + if (x[0] === 0x00) x = x.slice(1) + let buffer = Buffer.alloc(32, 0) + let bstart = Math.max(0, 32 - x.length) + x.copy(buffer, bstart) + return buffer +} // BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) function decode (buffer) { - var hashType = buffer.readUInt8(buffer.length - 1) - var hashTypeMod = hashType & ~0x80 + let hashType = buffer.readUInt8(buffer.length - 1) + let hashTypeMod = hashType & ~0x80 if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) - var decode = bip66.decode(buffer.slice(0, -1)) + let decode = bip66.decode(buffer.slice(0, -1)) + let r = fromDER(decode.r) + let s = fromDER(decode.s) return { - signature: { - r: BigInteger.fromDERInteger(decode.r), - s: BigInteger.fromDERInteger(decode.s) - }, + signature: Buffer.concat([r, s], 64), hashType: hashType } } -function toRSBuffer (signature, buffer, offset) { - buffer = buffer || Buffer.alloc(64) - signature.r.toBuffer(32).copy(buffer, offset) - signature.s.toBuffer(32).copy(buffer, offset + 32) - return buffer -} - -function fromRSBuffer (buffer) { - typeforce(types.BufferN(64), buffer) - - var r = BigInteger.fromBuffer(buffer.slice(0, 32)) - var s = BigInteger.fromBuffer(buffer.slice(32, 64)) - return { r: r, s: s } -} - function encode (signature, hashType) { - var hashTypeMod = hashType & ~0x80 + typeforce({ + signature: types.BufferN(64), + hashType: types.UInt8 + }, { signature, hashType }) + + let hashTypeMod = hashType & ~0x80 if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) - var hashTypeBuffer = Buffer.allocUnsafe(1) + let hashTypeBuffer = Buffer.allocUnsafe(1) hashTypeBuffer.writeUInt8(hashType, 0) - var r = Buffer.from(signature.r.toDERInteger()) - var s = Buffer.from(signature.s.toDERInteger()) + let r = toDER(signature.slice(0, 32)) + let s = toDER(signature.slice(32, 64)) return Buffer.concat([ bip66.encode(r, s), @@ -53,8 +59,6 @@ function encode (signature, hashType) { } module.exports = { - fromRSBuffer: fromRSBuffer, - toRSBuffer: toRSBuffer, decode: decode, encode: encode } diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 1f1f490..acfc232 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -715,9 +715,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy input.prevOutType === scriptTypes.P2WSH )) throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH') - var signature = keyPair.sign(signatureHash) - if (Buffer.isBuffer(signature)) signature = bscript.signature.fromRSBuffer(signature) - + let signature = keyPair.sign(signatureHash) input.signatures[i] = bscript.signature.encode(signature, hashType) return true }) diff --git a/test/ecpair.js b/test/ecpair.js index 2530e32..6b617da 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -230,6 +230,7 @@ describe('ECPair', function () { this.mock(ecdsa, 'sign', function (h, d) { assert.strictEqual(h, hash) assert.strictEqual(d, keyPair.d) + return { r: BigInteger.ONE, s: BigInteger.ONE } }, 1) keyPair.sign(hash) @@ -254,7 +255,11 @@ describe('ECPair', function () { it('wraps ecdsa.verify', hoodwink(function () { this.mock(ecdsa, 'verify', function (h, s, q) { assert.strictEqual(h, hash) - assert.strictEqual(s, signature) +// assert.strictEqual(s, signature) + assert.deepEqual(s, { + r: BigInteger.fromBuffer(signature.slice(0, 32)), + s: BigInteger.fromBuffer(signature.slice(32, 64)) + }) assert.strictEqual(q, keyPair.Q) }, 1) diff --git a/test/fixtures/signature.json b/test/fixtures/signature.json index 9f4023d..e7949c5 100644 --- a/test/fixtures/signature.json +++ b/test/fixtures/signature.json @@ -4,56 +4,56 @@ "hashType": 1, "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226201", "raw": { - "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", - "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" + "r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9", + "s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262" } }, { "hashType": 2, "hex": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a502", "raw": { - "r": "38341707918488238920692284707283974715538935465589664377561695343399725051885", - "s": "3180566392414476763164587487324397066658063772201694230600609996154610926757" + "r": "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed", + "s": "07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5" } }, { "hashType": 3, "hex": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b28303", "raw": { - "r": "115464191557905790016094131873849783294273568009648050793030031933291767741904", - "s": "50562520307781850052192542766631199590053690478900449960232079510155113443971" + "r": "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd0", + "s": "6fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283" } }, { "hashType": 129, "hex": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d381", "raw": { - "r": "87230998027579607140680851455601772643840468630989315269459846730712163783123", - "s": "53231320085894623106179381504478252331065330583563809963303318469380290929875" + "r": "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3", + "s": "75afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3" } }, { "hashType": 130, "hex": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df682", "raw": { - "r": "51348483531757779992459563033975330355971795607481991320287437101831125115997", - "s": "6277080015686056199074771961940657638578000617958603212944619747099038735862" + "r": "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d", + "s": "0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6" } }, { "hashType": 131, "hex": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd3783", "raw": { - "r": "113979859486826658566290715281614250298918272782414232881639314569529560769671", - "s": "6517071009538626957379450615706485096874328019806177698938278220732027419959" + "r": "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda487", + "s": "0e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37" } }, { "hashType": 129, "hex": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef81", "raw": { - "r": "93122007060065279508564838030979550535085999589142852106617159184757394422777", - "s": "3078539468410661027472930027406594684630312677495124015420811882501887769839" + "r": "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9", + "s": "06ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef" } } ], @@ -123,8 +123,8 @@ "hashType": 7, "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa5434226207", "raw": { - "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", - "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" + "r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9", + "s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262" } }, { @@ -132,8 +132,8 @@ "hashType": 140, "hex": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa543422628c", "raw": { - "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561", - "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938" + "r": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9", + "s": "6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262" } } ] diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 4b53769..8566628 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -43,12 +43,14 @@ describe('bitcoinjs-lib (crypto)', function () { var inputB = tx.ins[j] // enforce matching r values - assert.strictEqual(inputA.signature.r.toString(), inputB.signature.r.toString()) - var r = inputA.signature.r - var rInv = r.modInverse(n) + let r = inputA.signature.slice(0, 32) + let rB = inputB.signature.slice(0, 32) + assert.strictEqual(r.toString('hex'), rB.toString('hex')) - var s1 = inputA.signature.s - var s2 = inputB.signature.s + var rInv = bigi.fromBuffer(r).modInverse(n) + + var s1 = bigi.fromBuffer(inputA.signature.slice(32, 64)) + var s2 = bigi.fromBuffer(inputB.signature.slice(32, 64)) var z1 = inputA.z var z2 = inputB.z diff --git a/test/script_signature.js b/test/script_signature.js index d57e575..f53bc52 100644 --- a/test/script_signature.js +++ b/test/script_signature.js @@ -2,22 +2,21 @@ var assert = require('assert') var bscriptSig = require('../src/script').signature -var BigInteger = require('bigi') var Buffer = require('safe-buffer').Buffer var fixtures = require('./fixtures/signature.json') describe('Script Signatures', function () { function fromRaw (signature) { - return { - r: new BigInteger(signature.r), - s: new BigInteger(signature.s) - } + return Buffer.concat([ + Buffer.from(signature.r, 'hex'), + Buffer.from(signature.s, 'hex') + ], 64) } function toRaw (signature) { return { - r: signature.r.toString(), - s: signature.s.toString() + r: signature.slice(0, 32).toString('hex'), + s: signature.slice(32, 64).toString('hex') } } From a463c41fb00ae78f7fbfc7dacb5165a862d122c2 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 22 May 2018 16:33:43 +1000 Subject: [PATCH 023/568] tests/txb: fix bad BIP66 encoding (R = 0) --- test/transaction_builder.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index cc24571..1a98c3d 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -298,7 +298,7 @@ describe('TransactionBuilder', function () { it('supports the alternative abstract interface { publicKey, sign }', function () { var keyPair = { publicKey: Buffer.alloc(33, 0x03), - sign: function (hash) { return Buffer.alloc(64) } + sign: function (hash) { return Buffer.alloc(64, 0x5f) } } var txb = new TransactionBuilder() @@ -306,7 +306,7 @@ describe('TransactionBuilder', function () { txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) txb.addOutput('1111111111111111111114oLvT2', 100000) txb.sign(0, keyPair) - assert.equal(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000002c0930060201000201000121030303030303030303030303030303030303030303030303030303030303030303ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + assert.equal(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121030303030303030303030303030303030303030303030303030303030303030303ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) fixtures.invalid.sign.forEach(function (f) { From 581f57ff6fb0e953004bc2795a058a09af2101f2 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sat, 14 Apr 2018 01:42:48 +1000 Subject: [PATCH 024/568] script/tx: decompile returns maybe chunks --- src/script.js | 4 ++-- src/templates/index.js | 4 ++++ src/templates/scripthash/input.js | 2 +- src/transaction_builder.js | 14 ++++++++------ test/script.js | 4 ++-- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/script.js b/src/script.js index 4bd2fdf..0277061 100644 --- a/src/script.js +++ b/src/script.js @@ -98,11 +98,11 @@ function decompile (buffer) { var d = pushdata.decode(buffer, i) // did reading a pushDataInt fail? empty script - if (d === null) return [] + if (d === null) return null i += d.size // attempt to read too much data? empty script - if (i + d.number > buffer.length) return [] + if (i + d.number > buffer.length) return null var data = buffer.slice(i, i + d.number) i += d.number diff --git a/src/templates/index.js b/src/templates/index.js index 4916c78..af09112 100644 --- a/src/templates/index.js +++ b/src/templates/index.js @@ -28,6 +28,8 @@ function classifyOutput (script) { // XXX: optimization, below functions .decompile before use var chunks = decompile(script) + if (!chunks) throw new TypeError('Invalid script') + if (multisig.output.check(chunks)) return types.MULTISIG if (pubKey.output.check(chunks)) return types.P2PK if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT @@ -39,6 +41,7 @@ function classifyOutput (script) { function classifyInput (script, allowIncomplete) { // XXX: optimization, below functions .decompile before use var chunks = decompile(script) + if (!chunks) throw new TypeError('Invalid script') if (pubKeyHash.input.check(chunks)) return types.P2PKH if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH @@ -51,6 +54,7 @@ function classifyInput (script, allowIncomplete) { function classifyWitness (script, allowIncomplete) { // XXX: optimization, below functions .decompile before use var chunks = decompile(script) + if (!chunks) throw new TypeError('Invalid script') if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH if (witnessScriptHash.input.check(chunks, allowIncomplete)) return types.P2WSH diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js index cc5bb74..2ce55e3 100644 --- a/src/templates/scripthash/input.js +++ b/src/templates/scripthash/input.js @@ -21,7 +21,7 @@ function check (script, allowIncomplete) { var redeemScriptChunks = bscript.decompile(lastChunk) // is redeemScript a valid script? - if (redeemScriptChunks.length === 0) return false + if (!redeemScriptChunks) return false // is redeemScriptSig push only? if (!bscript.isPushOnly(scriptSigChunks)) return false diff --git a/src/transaction_builder.js b/src/transaction_builder.js index acfc232..3a1310b 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -71,7 +71,7 @@ function expandInput (scriptSig, witnessStack) { var witnessProgram var chunks - var scriptSigChunks = bscript.decompile(scriptSig) + var scriptSigChunks = bscript.decompile(scriptSig) || [] var sigType = btemplates.classifyInput(scriptSigChunks, true) if (sigType === scriptTypes.P2SH) { p2sh = true @@ -209,7 +209,7 @@ function fixMultisigOrder (input, transaction, vin) { function expandOutput (script, scriptType, ourPubKey) { typeforce(types.Buffer, script) - var scriptChunks = bscript.decompile(script) + var scriptChunks = bscript.decompile(script) || [] if (!scriptType) { scriptType = btemplates.classifyOutput(script) } @@ -257,8 +257,9 @@ function checkP2SHInput (input, redeemScriptHash) { if (input.prevOutType) { if (input.prevOutType !== scriptTypes.P2SH) throw new Error('PrevOutScript must be P2SH') - var prevOutScriptScriptHash = bscript.decompile(input.prevOutScript)[1] - if (!prevOutScriptScriptHash.equals(redeemScriptHash)) throw new Error('Inconsistent hash160(redeemScript)') + var chunks = bscript.decompile(input.prevOutScript) + if (!chunks) throw new Error('Invalid prevOutScript') + if (!chunks[1].equals(redeemScriptHash)) throw new Error('Inconsistent hash160(redeemScript)') } } @@ -266,8 +267,9 @@ function checkP2WSHInput (input, witnessScriptHash) { if (input.prevOutType) { if (input.prevOutType !== scriptTypes.P2WSH) throw new Error('PrevOutScript must be P2WSH') - var scriptHash = bscript.decompile(input.prevOutScript)[1] - if (!scriptHash.equals(witnessScriptHash)) throw new Error('Inconsistent sha256(witnessScript)') + var chunks = bscript.decompile(input.prevOutScript) + if (!chunks) throw new Error('Invalid witnessScript') + if (!chunks[1].equals(witnessScriptHash)) throw new Error('Inconsistent sha256(witnessScript)') } } diff --git a/test/script.js b/test/script.js index 07f907f..5a51dbd 100644 --- a/test/script.js +++ b/test/script.js @@ -124,10 +124,10 @@ describe('script', function () { }) fixtures.invalid.decompile.forEach(function (f) { - it('decompiles ' + f.script + ' to [] because of "' + f.description + '"', function () { + it('fails to decompile ' + f.script + ', because "' + f.description + '"', function () { var chunks = bscript.decompile(Buffer.from(f.script, 'hex')) - assert.strictEqual(chunks.length, 0) + assert.strictEqual(chunks, null) }) }) }) From 4d7b5167aec5ddcaa909e10a60ecf24e774d02c8 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 22 May 2018 18:49:51 +1000 Subject: [PATCH 025/568] txbuilder: isolate Invalid script test from Non-standard test --- test/fixtures/transaction_builder.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 29642d1..9393de5 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -1917,10 +1917,16 @@ } ] }, + { + "description": "Transaction w/ invalid scripts", + "exception": "Invalid script", + "incomplete": true, + "txHex": "010000000100000000171a0000e028f2000000000050178500000000000d0000000e000000000000002009f691b2263260e71f363d1db51ff3100d285956a40cc0e4f8c8c2c4a80559b1ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + }, { "description": "Complete transaction w/ non-standard inputs", "exception": "nonstandard not supported", - "txHex": "010000000100000000171a0000e028f2000000000050178500000000000d0000000e000000000000002009f691b2263260e71f363d1db51ff3100d285956a40cc0e4f8c8c2c4a80559b1ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + "txHex": "010000000100000000171a0000e028f2000000000050178500000000000d0000000e00000000000000201ff691b2263260e71f363d1db51ff3100d285956a40cc0e4f8c8c2c4a80559b1ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" } ], "sign": [ From ab1fba987a817da138ae1c00d00245ba261231df Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 1 Dec 2017 13:13:55 +1100 Subject: [PATCH 026/568] add BIP68/BIP112 CSV tests --- package.json | 3 +- test/integration/cltv.js | 14 ++--- test/integration/csv.js | 121 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 test/integration/csv.js diff --git a/package.json b/package.json index 254289f..62ffd5c 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "bigi": "^1.4.0", "bip32": "0.0.3", "bip66": "^1.1.0", - "bitcoin-ops": "^1.3.0", + "bitcoin-ops": "^1.4.0", "bs58check": "^2.0.0", "create-hash": "^1.1.0", "create-hmac": "^1.1.3", @@ -52,6 +52,7 @@ "bigi": "^1.4.2", "bip39": "^2.3.0", "bip65": "^1.0.1", + "bip68": "^1.0.3", "bs58": "^4.0.0", "dhttp": "^2.4.2", "hoodwink": "^1.0.0", diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 547ac79..d58c1a8 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -81,8 +81,8 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { regtestUtils.height(function (err, height) { if (err) return done(err) - // 50 blocks from now - var lockTime = bip65.encode({ blocks: height + 50 }) + // 5 blocks from now + var lockTime = bip65.encode({ blocks: height + 5 }) var redeemScript = cltvCheckSigOutput(alice, bob, lockTime) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) @@ -106,14 +106,9 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { tx.setInputScript(0, redeemScriptSig) // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently -// regtestUtils.broadcast(tx.toHex(), function (err) { -// // fails before the expiry -// assert.throws(function () { -// if (err) throw err -// }, /Error: 64: non-final/) - + // ... // into the future! - regtestUtils.mine(51, function (err) { + regtestUtils.mine(5, function (err) { if (err) return done(err) regtestUtils.broadcast(tx.toHex(), function (err) { @@ -127,7 +122,6 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { }, done) }) }) -// }) }) }) }) diff --git a/test/integration/csv.js b/test/integration/csv.js new file mode 100644 index 0000000..5c9dd3b --- /dev/null +++ b/test/integration/csv.js @@ -0,0 +1,121 @@ +/* global describe, it */ + +let assert = require('assert') +let bitcoin = require('../../') +let regtestUtils = require('./_regtest') +let regtest = regtestUtils.network +let bip68 = require('bip68') + +let alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) +let bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) + +describe('bitcoinjs-lib (transactions w/ CSV)', function () { + let hashType = bitcoin.Transaction.SIGHASH_ALL + + // IF MTP (from when confirmed) > seconds, aQ can redeem + function csvCheckSigOutput (aQ, bQ, sequence) { + return bitcoin.script.compile([ + bitcoin.opcodes.OP_IF, + bitcoin.script.number.encode(sequence), + bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, + bitcoin.opcodes.OP_DROP, + + bitcoin.opcodes.OP_ELSE, + bQ.getPublicKeyBuffer(), + bitcoin.opcodes.OP_CHECKSIGVERIFY, + bitcoin.opcodes.OP_ENDIF, + + aQ.getPublicKeyBuffer(), + bitcoin.opcodes.OP_CHECKSIG + ]) + } + + // expiry will pass, {Alice's signature} OP_TRUE + it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', function (done) { + this.timeout(30000) + + regtestUtils.height(function (err, height) { + if (err) return done(err) + + // 5 blocks from now + let sequence = bip68.encode({ blocks: 5 }) + let redeemScript = csvCheckSigOutput(alice, bob, sequence) + let scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + let address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + + // fund the P2SH(CSV) address + regtestUtils.faucet(address, 1e5, function (err, unspent) { + if (err) return done(err) + + let txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, sequence) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + + // {Alice's signature} OP_TRUE + let tx = txb.buildIncomplete() + let signatureHash = tx.hashForSignature(0, redeemScript, hashType) + let redeemScriptSig = bitcoin.script.scriptHash.input.encode([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ], redeemScript) + tx.setInputScript(0, redeemScriptSig) + + // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently + // ... + // into the future! + regtestUtils.mine(10, function (err) { + if (err) return done(err) + + regtestUtils.broadcast(tx.toHex(), function (err) { + if (err) return done(err) + + regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4 + }, done) + }) + }) + }) + }) + }) + + // expiry in the future, {Alice's signature} OP_TRUE + it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', function (done) { + this.timeout(30000) + + // two hours after confirmation + let sequence = bip68.encode({ seconds: 7168 }) + let redeemScript = csvCheckSigOutput(alice, bob, sequence) + let scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + let address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + + // fund the P2SH(CSV) address + regtestUtils.faucet(address, 2e4, function (err, unspent) { + if (err) return done(err) + + let txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, sequence) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + + // {Alice's signature} OP_TRUE + let tx = txb.buildIncomplete() + let signatureHash = tx.hashForSignature(0, redeemScript, hashType) + let redeemScriptSig = bitcoin.script.scriptHash.input.encode([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ], redeemScript) + tx.setInputScript(0, redeemScriptSig) + + regtestUtils.broadcast(tx.toHex(), function (err) { + assert.throws(function () { + if (err) throw err + }, /Error: 64: non-BIP68-final/) + + done() + }) + }) + }) +}) From b2d34c5fa8577b424531195200a144f14c6f9f87 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 29 May 2018 11:08:38 +1000 Subject: [PATCH 027/568] bump dependencies --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 62ffd5c..f5cceb8 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "dependencies": { "bech32": "^1.1.2", "bigi": "^1.4.0", - "bip32": "0.0.3", + "bip32": "^0.1.0", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", "bs58check": "^2.0.0", @@ -43,7 +43,7 @@ "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", "safe-buffer": "^5.1.1", - "tiny-secp256k1": "0.0.5", + "tiny-secp256k1": "^0.1.0", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" @@ -54,13 +54,13 @@ "bip65": "^1.0.1", "bip68": "^1.0.3", "bs58": "^4.0.0", - "dhttp": "^2.4.2", + "dhttp": "^2.5.0", "hoodwink": "^1.0.0", "minimaldata": "^1.0.2", - "mocha": "^5.0.1", - "nyc": "^11.4.1", - "proxyquire": "^1.4.0", - "standard": "^9.0.2" + "mocha": "^5.2.0", + "nyc": "^11.8.0", + "proxyquire": "^2.0.1", + "standard": "^11.0.1" }, "license": "MIT" } From 4aaf295cd5f9db1b3adb483316cbb4c85defe254 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 29 May 2018 11:37:03 +1000 Subject: [PATCH 028/568] fix standard 11 issues --- src/script.js | 2 +- src/script_number.js | 10 +++++----- test/ecpair.js | 2 +- test/integration/bip32.js | 2 +- test/integration/blocks.js | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/script.js b/src/script.js index 0277061..2860d66 100644 --- a/src/script.js +++ b/src/script.js @@ -187,7 +187,7 @@ function isCanonicalPubKey (buffer) { function isDefinedHashType (hashType) { var hashTypeMod = hashType & ~0x80 -// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE + // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE return hashTypeMod > 0x00 && hashTypeMod < 0x04 } diff --git a/src/script_number.js b/src/script_number.js index 44f4bec..3fd6326 100644 --- a/src/script_number.js +++ b/src/script_number.js @@ -35,11 +35,11 @@ function decode (buffer, maxLength, minimal) { function scriptNumSize (i) { return i > 0x7fffffff ? 5 - : i > 0x7fffff ? 4 - : i > 0x7fff ? 3 - : i > 0x7f ? 2 - : i > 0x00 ? 1 - : 0 + : i > 0x7fffff ? 4 + : i > 0x7fff ? 3 + : i > 0x7f ? 2 + : i > 0x00 ? 1 + : 0 } function encode (number) { diff --git a/test/ecpair.js b/test/ecpair.js index 6b617da..046808f 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -255,7 +255,7 @@ describe('ECPair', function () { it('wraps ecdsa.verify', hoodwink(function () { this.mock(ecdsa, 'verify', function (h, s, q) { assert.strictEqual(h, hash) -// assert.strictEqual(s, signature) + // assert.strictEqual(s, signature) assert.deepEqual(s, { r: BigInteger.fromBuffer(signature.slice(0, 32)), s: BigInteger.fromBuffer(signature.slice(32, 64)) diff --git a/test/integration/bip32.js b/test/integration/bip32.js index b7f0bb2..6695a96 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -88,7 +88,7 @@ describe('bitcoinjs-lib (BIP32)', function () { }) it('can use BIP39 to generate BIP32 addresses', function () { -// var mnemonic = bip39.generateMnemonic() + // var mnemonic = bip39.generateMnemonic() var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' assert(bip39.validateMnemonic(mnemonic)) diff --git a/test/integration/blocks.js b/test/integration/blocks.js index 58faab2..56eca68 100644 --- a/test/integration/blocks.js +++ b/test/integration/blocks.js @@ -12,7 +12,7 @@ describe('bitcoinjs-lib (blocks)', function () { assert.strictEqual(tx.ins.length, 1) let script = tx.ins[0].script -// bitcoin.script.decompile(script) // returns [] :( + // bitcoin.script.decompile(script) // returns [] :( assert.strictEqual(script[0], 0x03) let heightBuffer = script.slice(1, 4) From 4ed1a49dc5b6d2edefa135eb37c3a3669ce5a0b4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 30 May 2018 10:23:55 +1000 Subject: [PATCH 029/568] force update MTP if tests not run --- test/integration/cltv.js | 9 +++++++-- test/integration/csv.js | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index d58c1a8..14937c8 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -1,4 +1,4 @@ -/* global describe, it */ +/* global describe, it, before */ var assert = require('assert') var bitcoin = require('../../') @@ -10,7 +10,12 @@ var alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxp var bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) describe('bitcoinjs-lib (transactions w/ CLTV)', function () { - var hashType = bitcoin.Transaction.SIGHASH_ALL + // force update MTP + before(function (done) { + regtestUtils.mine(11, done) + }) + + let hashType = bitcoin.Transaction.SIGHASH_ALL function cltvCheckSigOutput (aQ, bQ, lockTime) { return bitcoin.script.compile([ diff --git a/test/integration/csv.js b/test/integration/csv.js index 5c9dd3b..ef9c30f 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -1,4 +1,4 @@ -/* global describe, it */ +/* global describe, it, before */ let assert = require('assert') let bitcoin = require('../../') @@ -10,6 +10,11 @@ let alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxp let bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) describe('bitcoinjs-lib (transactions w/ CSV)', function () { + // force update MTP + before(function (done) { + regtestUtils.mine(11, done) + }) + let hashType = bitcoin.Transaction.SIGHASH_ALL // IF MTP (from when confirmed) > seconds, aQ can redeem From c2a5d9dc1aa98545e47abe3fb0bd22d6b0c5e61d Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 22 May 2018 17:43:25 +1000 Subject: [PATCH 030/568] rm getAddress --- src/ecpair.js | 24 +++++++----------- test/ecpair.js | 10 -------- test/integration/_regtest.js | 12 +++++++-- test/integration/addresses.js | 43 +++++++++++++++++++------------- test/integration/bip32.js | 16 ++++++------ test/integration/cltv.js | 8 +++--- test/integration/stealth.js | 37 ++++++++++++++++----------- test/integration/transactions.js | 26 ++++++++++++------- test/transaction_builder.js | 31 ++++++++++++++--------- 9 files changed, 115 insertions(+), 92 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index cfc3649..b491f53 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -1,16 +1,14 @@ -var baddress = require('./address') -var bcrypto = require('./crypto') -var ecdsa = require('./ecdsa') -var randomBytes = require('randombytes') -var typeforce = require('typeforce') -var types = require('./types') -var wif = require('wif') +let ecdsa = require('./ecdsa') +let randomBytes = require('randombytes') +let typeforce = require('typeforce') +let types = require('./types') +let wif = require('wif') -var NETWORKS = require('./networks') -var BigInteger = require('bigi') +let NETWORKS = require('./networks') +let BigInteger = require('bigi') -var ecurve = require('ecurve') -var secp256k1 = ecdsa.__curve +let ecurve = require('ecurve') +let secp256k1 = ecdsa.__curve function ECPair (d, Q, options) { if (options) { @@ -100,10 +98,6 @@ ECPair.makeRandom = function (options) { return new ECPair(d, null, options) } -ECPair.prototype.getAddress = function () { - return baddress.toBase58Check(bcrypto.hash160(this.getPublicKeyBuffer()), this.getNetwork().pubKeyHash) -} - ECPair.prototype.getNetwork = function () { return this.network } diff --git a/test/ecpair.js b/test/ecpair.js index 046808f..52e5499 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -196,16 +196,6 @@ describe('ECPair', function () { })) }) - describe('getAddress', function () { - fixtures.valid.forEach(function (f) { - it('returns ' + f.address + ' for ' + f.WIF, function () { - var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) - - assert.strictEqual(keyPair.getAddress(), f.address) - }) - }) - }) - describe('getNetwork', function () { fixtures.valid.forEach(function (f) { it('returns ' + f.network + ' for ' + f.WIF, function () { diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 0bb1a40..0baaffa 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -67,10 +67,18 @@ function verify (txo, callback) { }) } +// TODO: remove +let baddress = bitcoin.address +let bcrypto = bitcoin.crypto +function getAddress (node, network) { + network = network || bitcoin.networks.bitcoin + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash) +} + function randomAddress () { - return bitcoin.ECPair.makeRandom({ + return getAddress(bitcoin.ECPair.makeRandom({ network: bitcoin.networks.testnet - }).getAddress() + }), bitcoin.networks.testnet) } module.exports = { diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 3b45836..0f8ae52 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -1,27 +1,34 @@ /* global describe, it */ -var assert = require('assert') -var bigi = require('bigi') -var bitcoin = require('../../') -var dhttp = require('dhttp/200') +let assert = require('assert') +let bitcoin = require('../../') +let dhttp = require('dhttp/200') // deterministic RNG for testing only function rng () { return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } +// TODO: remove +let baddress = bitcoin.address +let bcrypto = bitcoin.crypto +function getAddress (node, network) { + network = network || bitcoin.networks.bitcoin + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash) +} + describe('bitcoinjs-lib (addresses)', function () { it('can generate a random address', function () { var keyPair = bitcoin.ECPair.makeRandom({ rng: rng }) - var address = keyPair.getAddress() + var address = getAddress(keyPair) assert.strictEqual(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64') }) it('can generate an address from a SHA256 hash', function () { var hash = bitcoin.crypto.sha256(Buffer.from('correct horse battery staple')) - var d = bigi.fromBuffer(hash) - var keyPair = new bitcoin.ECPair(d) - var address = keyPair.getAddress() + var keyPair = bitcoin.ECPair.makeRandom({ rng: () => hash }) + var address = getAddress(keyPair) + // Generating addresses from SHA256 hashes is not secure if the input to the hash function is predictable // Do not use with predictable inputs assert.strictEqual(address, '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') @@ -29,7 +36,7 @@ describe('bitcoinjs-lib (addresses)', function () { it('can import an address via WIF', function () { var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var address = keyPair.getAddress() + var address = getAddress(keyPair) assert.strictEqual(address, '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31') }) @@ -100,7 +107,7 @@ describe('bitcoinjs-lib (addresses)', function () { it('can support the retrieval of transactions for an address (via 3PBP)', function (done) { var keyPair = bitcoin.ECPair.makeRandom() - var address = keyPair.getAddress() + var address = getAddress(keyPair) dhttp({ method: 'POST', @@ -120,20 +127,20 @@ describe('bitcoinjs-lib (addresses)', function () { // other networks it('can generate a Testnet address', function () { - var testnet = bitcoin.networks.testnet - var keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: rng }) - var wif = keyPair.toWIF() - var address = keyPair.getAddress() + let testnet = bitcoin.networks.testnet + let keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: rng }) + let wif = keyPair.toWIF() + let address = getAddress(keyPair, testnet) assert.strictEqual(address, 'mubSzQNtZfDj1YdNP6pNDuZy6zs6GDn61L') assert.strictEqual(wif, 'cRgnQe9MUu1JznntrLaoQpB476M8PURvXVQB5R2eqms5tXnzNsrr') }) it('can generate a Litecoin address', function () { - var litecoin = bitcoin.networks.litecoin - var keyPair = bitcoin.ECPair.makeRandom({ network: litecoin, rng: rng }) - var wif = keyPair.toWIF() - var address = keyPair.getAddress() + let litecoin = bitcoin.networks.litecoin + let keyPair = bitcoin.ECPair.makeRandom({ network: litecoin, rng: rng }) + let wif = keyPair.toWIF() + let address = getAddress(keyPair, litecoin) assert.strictEqual(address, 'LZJSxZbjqJ2XVEquqfqHg1RQTDdfST5PTn') assert.strictEqual(wif, 'T7A4PUSgTDHecBxW1ZiYFrDNRih2o7M8Gf9xpoCgudPF9gDiNvuS') diff --git a/test/integration/bip32.js b/test/integration/bip32.js index 6695a96..effdec1 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -1,14 +1,16 @@ /* global describe, it */ -var assert = require('assert') +let assert = require('assert') let bip32 = require('bip32') -var bip39 = require('bip39') -var bitcoin = require('../../') +let bip39 = require('bip39') +let bitcoin = require('../../') -var baddress = bitcoin.address -var bcrypto = bitcoin.crypto -function getAddress (node) { - return baddress.toBase58Check(bcrypto.hash160(node.publicKey), bitcoin.networks.bitcoin.pubKeyHash) +// TODO: remove +let baddress = bitcoin.address +let bcrypto = bitcoin.crypto +function getAddress (node, network) { + network = network || bitcoin.networks.bitcoin + return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) } describe('bitcoinjs-lib (BIP32)', function () { diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 14937c8..2b51ed7 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -43,10 +43,10 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { this.timeout(30000) // 3 hours ago - var lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) - var redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + let lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) + let redeemScript = cltvCheckSigOutput(alice, bob, lockTime) + let scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + let address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) // fund the P2SH(CLTV) address regtestUtils.faucet(address, 1e5, function (err, unspent) { diff --git a/test/integration/stealth.js b/test/integration/stealth.js index 0eabac1..b3cea41 100644 --- a/test/integration/stealth.js +++ b/test/integration/stealth.js @@ -1,13 +1,20 @@ /* global describe, it */ -var assert = require('assert') -var bigi = require('bigi') -var bitcoin = require('../../') +let assert = require('assert') +let bigi = require('bigi') +let bitcoin = require('../../') -var ecurve = require('ecurve') -var secp256k1 = ecurve.getCurveByName('secp256k1') -var G = secp256k1.G -var n = secp256k1.n +let ecurve = require('ecurve') +let secp256k1 = ecurve.getCurveByName('secp256k1') +let G = secp256k1.G +let n = secp256k1.n + +// TODO: remove +let baddress = bitcoin.address +let bcrypto = bitcoin.crypto +function getAddress (node) { + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), bitcoin.networks.bitcoin.pubKeyHash) +} // vG = (dG \+ sha256(e * dG)G) function stealthSend (e, Q) { @@ -74,16 +81,16 @@ describe('bitcoinjs-lib (crypto)', function () { // ... recipient reveals public key (recipient.Q) to sender var forSender = stealthSend(nonce.d, recipient.Q) - assert.equal(forSender.getAddress(), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') + assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient var forRecipient = stealthReceive(recipient.d, nonce.Q) - assert.equal(forRecipient.getAddress(), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') + assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n') // sender and recipient, both derived same address - assert.equal(forSender.getAddress(), forRecipient.getAddress()) + assert.equal(getAddress(forSender), getAddress(forRecipient)) }) it('can generate a single-key stealth address (randomly)', function () { @@ -99,7 +106,7 @@ describe('bitcoinjs-lib (crypto)', function () { assert.doesNotThrow(function () { forRecipient.toWIF() }) // sender and recipient, both derived same address - assert.equal(forSender.getAddress(), forRecipient.getAddress()) + assert.equal(getAddress(forSender), getAddress(forRecipient)) }) it('can recover parent recipient.d, if a derived private key is leaked [and nonce was revealed]', function () { @@ -138,8 +145,8 @@ describe('bitcoinjs-lib (crypto)', function () { assert.doesNotThrow(function () { forRecipient.toWIF() }) // scanner, sender and recipient, all derived same address - assert.equal(forSender.getAddress(), forScanner.getAddress()) - assert.equal(forSender.getAddress(), forRecipient.getAddress()) + assert.equal(getAddress(forSender), getAddress(forScanner)) + assert.equal(getAddress(forSender), getAddress(forRecipient)) }) it('can generate a dual-key stealth address (randomly)', function () { @@ -160,7 +167,7 @@ describe('bitcoinjs-lib (crypto)', function () { assert.doesNotThrow(function () { forRecipient.toWIF() }) // scanner, sender and recipient, all derived same address - assert.equal(forSender.getAddress(), forScanner.getAddress()) - assert.equal(forSender.getAddress(), forRecipient.getAddress()) + assert.equal(getAddress(forSender), getAddress(forScanner)) + assert.equal(getAddress(forSender), getAddress(forRecipient)) }) }) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index fb3fd03..c1e4862 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -1,9 +1,17 @@ /* global describe, it */ -var assert = require('assert') -var bitcoin = require('../../') -var regtestUtils = require('./_regtest') -var regtest = regtestUtils.network +let assert = require('assert') +let bitcoin = require('../../') +let regtestUtils = require('./_regtest') +let regtest = regtestUtils.network + +// TODO: remove +let baddress = bitcoin.address +let bcrypto = bitcoin.crypto +function getAddress (node, network) { + network = network || bitcoin.networks.bitcoin + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash) +} function rng () { return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64') @@ -52,17 +60,17 @@ describe('bitcoinjs-lib (transactions)', function () { var aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng }) // give Alice 2 unspent outputs - regtestUtils.faucet(alice1.getAddress(), 5e4, function (err, unspent0) { + regtestUtils.faucet(getAddress(alice1, regtest), 5e4, function (err, unspent0) { if (err) return done(err) - regtestUtils.faucet(alice2.getAddress(), 7e4, function (err, unspent1) { + regtestUtils.faucet(getAddress(alice2, regtest), 7e4, function (err, unspent1) { if (err) return done(err) var txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent0.txId, unspent0.vout) // alice1 unspent txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" - txb.addOutput(aliceChange.getAddress(), 1e4) // Alice's change + txb.addOutput(getAddress(aliceChange, regtest), 1e4) // Alice's change // (in)(4e4 + 2e4) - (out)(1e4 + 3e4) = (fee)2e4 = 20000, this is the miner fee // Alice signs each input with the respective private keys @@ -81,7 +89,7 @@ describe('bitcoinjs-lib (transactions)', function () { var keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - regtestUtils.faucet(keyPair.getAddress(), 2e5, function (err, unspent) { + regtestUtils.faucet(getAddress(keyPair, regtest), 2e5, function (err, unspent) { if (err) return done(err) var txb = new bitcoin.TransactionBuilder(regtest) @@ -228,7 +236,7 @@ describe('bitcoinjs-lib (transactions)', function () { tx.ins.forEach(function (input, i) { var keyPair = keyPairs[i] - var prevOutScript = bitcoin.address.toOutputScript(keyPair.getAddress()) + var prevOutScript = bitcoin.address.toOutputScript(getAddress(keyPair)) var scriptSig = bitcoin.script.pubKeyHash.input.decode(input.script) var ss = bitcoin.script.signature.decode(scriptSig.signature) var hash = tx.hashForSignature(i, prevOutScript, ss.hashType) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 1a98c3d..86f7eca 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -1,18 +1,24 @@ /* global describe, it, beforeEach */ -var assert = require('assert') -var baddress = require('../src/address') -var bscript = require('../src/script') -var btemplates = require('../src/templates') -var ops = require('bitcoin-ops') +let assert = require('assert') +let baddress = require('../src/address') +let bcrypto = require('../src/crypto') +let bscript = require('../src/script') +let btemplates = require('../src/templates') +let ops = require('bitcoin-ops') -var BigInteger = require('bigi') -var ECPair = require('../src/ecpair') -var Transaction = require('../src/transaction') -var TransactionBuilder = require('../src/transaction_builder') -var NETWORKS = require('../src/networks') +let BigInteger = require('bigi') +let ECPair = require('../src/ecpair') +let Transaction = require('../src/transaction') +let TransactionBuilder = require('../src/transaction_builder') +let NETWORKS = require('../src/networks') -var fixtures = require('./fixtures/transaction_builder') +let fixtures = require('./fixtures/transaction_builder') + +// TODO: remove +function getAddress (node) { + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), NETWORKS.bitcoin.pubKeyHash) +} function construct (f, dontSign) { var network = NETWORKS[f.network] @@ -220,7 +226,8 @@ describe('TransactionBuilder', function () { }) it('accepts an address string and value', function () { - var vout = txb.addOutput(keyPair.getAddress(), 1000) + let address = getAddress(keyPair) + var vout = txb.addOutput(address, 1000) assert.strictEqual(vout, 0) var txout = txb.__tx.outs[0] From fba0699dd385a8405a8f0cf4f61dda9b31a5123a Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 22 May 2018 16:33:43 +1000 Subject: [PATCH 031/568] rm ecdsa, add new ECPair using secp256k1 --- package.json | 2 - src/ecdsa.js | 163 ----------------------------- src/ecpair.js | 153 ++++++++++++--------------- src/transaction_builder.js | 4 +- src/types.js | 4 - test/bitcoin.core.js | 2 +- test/ecdsa.js | 135 ------------------------ test/ecpair.js | 172 +++++++++++++++---------------- test/fixtures/ecpair.json | 41 +++----- test/integration/_regtest.js | 2 +- test/integration/addresses.js | 8 +- test/integration/cltv.js | 4 +- test/integration/crypto.js | 2 +- test/integration/csv.js | 4 +- test/integration/stealth.js | 80 +++++++------- test/integration/transactions.js | 10 +- test/transaction_builder.js | 5 +- test/types.js | 12 --- 18 files changed, 227 insertions(+), 576 deletions(-) delete mode 100644 src/ecdsa.js delete mode 100644 test/ecdsa.js diff --git a/package.json b/package.json index f5cceb8..cf3db97 100644 --- a/package.json +++ b/package.json @@ -31,14 +31,12 @@ ], "dependencies": { "bech32": "^1.1.2", - "bigi": "^1.4.0", "bip32": "^0.1.0", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", "bs58check": "^2.0.0", "create-hash": "^1.1.0", "create-hmac": "^1.1.3", - "ecurve": "^1.0.0", "merkle-lib": "^2.0.10", "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", diff --git a/src/ecdsa.js b/src/ecdsa.js deleted file mode 100644 index 403ffd5..0000000 --- a/src/ecdsa.js +++ /dev/null @@ -1,163 +0,0 @@ -var Buffer = require('safe-buffer').Buffer -var createHmac = require('create-hmac') -var typeforce = require('typeforce') -var types = require('./types') - -var BigInteger = require('bigi') - -var ZERO = Buffer.alloc(1, 0) -var ONE = Buffer.alloc(1, 1) - -var ecurve = require('ecurve') -var secp256k1 = ecurve.getCurveByName('secp256k1') - -// https://tools.ietf.org/html/rfc6979#section-3.2 -function deterministicGenerateK (hash, x, checkSig) { - typeforce(types.tuple( - types.Hash256bit, - types.Buffer256bit, - types.Function - ), arguments) - - // Step A, ignored as hash already provided - // Step B - // Step C - var k = Buffer.alloc(32, 0) - var v = Buffer.alloc(32, 1) - - // Step D - k = createHmac('sha256', k) - .update(v) - .update(ZERO) - .update(x) - .update(hash) - .digest() - - // Step E - v = createHmac('sha256', k).update(v).digest() - - // Step F - k = createHmac('sha256', k) - .update(v) - .update(ONE) - .update(x) - .update(hash) - .digest() - - // Step G - v = createHmac('sha256', k).update(v).digest() - - // Step H1/H2a, ignored as tlen === qlen (256 bit) - // Step H2b - v = createHmac('sha256', k).update(v).digest() - - var T = BigInteger.fromBuffer(v) - - // Step H3, repeat until T is within the interval [1, n - 1] and is suitable for ECDSA - while (T.signum() <= 0 || T.compareTo(secp256k1.n) >= 0 || !checkSig(T)) { - k = createHmac('sha256', k) - .update(v) - .update(ZERO) - .digest() - - v = createHmac('sha256', k).update(v).digest() - - // Step H1/H2a, again, ignored as tlen === qlen (256 bit) - // Step H2b again - v = createHmac('sha256', k).update(v).digest() - T = BigInteger.fromBuffer(v) - } - - return T -} - -var N_OVER_TWO = secp256k1.n.shiftRight(1) - -function sign (hash, d) { - typeforce(types.tuple(types.Hash256bit, types.BigInt), arguments) - - var x = d.toBuffer(32) - var e = BigInteger.fromBuffer(hash) - var n = secp256k1.n - var G = secp256k1.G - - var r, s - deterministicGenerateK(hash, x, function (k) { - var Q = G.multiply(k) - - if (secp256k1.isInfinity(Q)) return false - - r = Q.affineX.mod(n) - if (r.signum() === 0) return false - - s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n) - if (s.signum() === 0) return false - - return true - }) - - // enforce low S values, see bip62: 'low s values in signatures' - if (s.compareTo(N_OVER_TWO) > 0) { - s = n.subtract(s) - } - - return { - r: r, - s: s - } -} - -function verify (hash, signature, Q) { - typeforce(types.tuple( - types.Hash256bit, - types.ECSignature, - types.ECPoint - ), arguments) - - var n = secp256k1.n - var G = secp256k1.G - - var r = signature.r - var s = signature.s - - // 1.4.1 Enforce r and s are both integers in the interval [1, n − 1] - if (r.signum() <= 0 || r.compareTo(n) >= 0) return false - if (s.signum() <= 0 || s.compareTo(n) >= 0) return false - - // 1.4.2 H = Hash(M), already done by the user - // 1.4.3 e = H - var e = BigInteger.fromBuffer(hash) - - // Compute s^-1 - var sInv = s.modInverse(n) - - // 1.4.4 Compute u1 = es^−1 mod n - // u2 = rs^−1 mod n - var u1 = e.multiply(sInv).mod(n) - var u2 = r.multiply(sInv).mod(n) - - // 1.4.5 Compute R = (xR, yR) - // R = u1G + u2Q - var R = G.multiplyTwo(u1, Q, u2) - - // 1.4.5 (cont.) Enforce R is not at infinity - if (secp256k1.isInfinity(R)) return false - - // 1.4.6 Convert the field element R.x to an integer - var xR = R.affineX - - // 1.4.7 Set v = xR mod n - var v = xR.mod(n) - - // 1.4.8 If v = r, output "valid", and if v != r, output "invalid" - return v.equals(r) -} - -module.exports = { - deterministicGenerateK: deterministicGenerateK, - sign: sign, - verify: verify, - - // TODO: remove - __curve: secp256k1 -} diff --git a/src/ecpair.js b/src/ecpair.js index b491f53..9467b1b 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -1,63 +1,73 @@ -let ecdsa = require('./ecdsa') +let ecc = require('tiny-secp256k1') let randomBytes = require('randombytes') let typeforce = require('typeforce') let types = require('./types') let wif = require('wif') let NETWORKS = require('./networks') -let BigInteger = require('bigi') -let ecurve = require('ecurve') -let secp256k1 = ecdsa.__curve +// TODO: why is the function name toJSON weird? +function isPoint (x) { return ecc.isPoint(x) } +let isOptions = typeforce.maybe(typeforce.compile({ + compressed: types.maybe(types.Boolean), + network: types.maybe(types.Network) +})) function ECPair (d, Q, options) { - if (options) { - typeforce({ - compressed: types.maybe(types.Boolean), - network: types.maybe(types.Network) - }, options) - } - options = options || {} - if (d) { - if (d.signum() <= 0) throw new Error('Private key must be greater than 0') - if (d.compareTo(secp256k1.n) >= 0) throw new Error('Private key must be less than the curve order') - if (Q) throw new TypeError('Unexpected publicKey parameter') - - this.d = d - } else { - typeforce(types.ECPoint, Q) - - this.__Q = Q - } - this.compressed = options.compressed === undefined ? true : options.compressed this.network = options.network || NETWORKS.bitcoin + + this.__d = d || null + this.__Q = null + if (Q) this.__Q = ecc.pointCompress(Q, this.compressed) } -Object.defineProperty(ECPair.prototype, 'Q', { - get: function () { - if (!this.__Q && this.d) { - this.__Q = secp256k1.G.multiply(this.d) - } - - return this.__Q - } -}) - -ECPair.fromPublicKeyBuffer = function (buffer, network) { - var Q = ecurve.Point.decodeFrom(secp256k1, buffer) - - return new ECPair(null, Q, { - compressed: Q.compressed, - network: network - }) +ECPair.prototype.getNetwork = function () { + return this.network } -ECPair.fromWIF = function (string, network) { - var decoded = wif.decode(string) - var version = decoded.version +ECPair.prototype.getPrivateKey = function () { + return this.__d +} + +ECPair.prototype.getPublicKey = function () { + if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed) + return this.__Q +} + +ECPair.prototype.toWIF = function () { + if (!this.__d) throw new Error('Missing private key') + return wif.encode(this.network.wif, this.__d, this.compressed) +} + +ECPair.prototype.sign = function (hash) { + if (!this.__d) throw new Error('Missing private key') + return ecc.sign(hash, this.__d) +} + +ECPair.prototype.verify = function (hash, signature) { + return ecc.verify(hash, this.getPublicKey(), signature) +} + +function fromPrivateKey (buffer, options) { + typeforce(types.Buffer256bit, buffer) + if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)') + typeforce(isOptions, options) + + return new ECPair(buffer, null, options) +} + +function fromPublicKey (buffer, options) { + typeforce(isPoint, buffer) + typeforce(isOptions, options) + return new ECPair(null, buffer, options) +} + +function fromWIF (string, network) { + let decoded = wif.decode(string) + let version = decoded.version // list of networks? if (types.Array(network)) { @@ -74,58 +84,29 @@ ECPair.fromWIF = function (string, network) { if (version !== network.wif) throw new Error('Invalid network version') } - var d = BigInteger.fromBuffer(decoded.privateKey) - - return new ECPair(d, null, { + return fromPrivateKey(decoded.privateKey, { compressed: decoded.compressed, network: network }) } -ECPair.makeRandom = function (options) { +function makeRandom (options) { + typeforce(isOptions, options) options = options || {} + let rng = options.rng || randomBytes - var rng = options.rng || randomBytes - - var d + let d do { - var buffer = rng(32) - typeforce(types.Buffer256bit, buffer) + d = rng(32) + typeforce(types.Buffer256bit, d) + } while (!ecc.isPrivate(d)) - d = BigInteger.fromBuffer(buffer) - } while (d.signum() <= 0 || d.compareTo(secp256k1.n) >= 0) - - return new ECPair(d, null, options) + return fromPrivateKey(d, options) } -ECPair.prototype.getNetwork = function () { - return this.network +module.exports = { + makeRandom, + fromPrivateKey, + fromPublicKey, + fromWIF } - -ECPair.prototype.getPublicKeyBuffer = function () { - return this.Q.getEncoded(this.compressed) -} - -ECPair.prototype.sign = function (hash) { - if (!this.d) throw new Error('Missing private key') - - let signature = ecdsa.sign(hash, this.d) - return Buffer.concat([signature.r.toBuffer(32), signature.s.toBuffer(32)], 64) -} - -ECPair.prototype.toWIF = function () { - if (!this.d) throw new Error('Missing private key') - - return wif.encode(this.network.wif, this.d.toBuffer(32), this.compressed) -} - -ECPair.prototype.verify = function (hash, signature) { - signature = { - r: BigInteger.fromBuffer(signature.slice(0, 32)), - s: BigInteger.fromBuffer(signature.slice(32, 64)) - } - - return ecdsa.verify(hash, signature, this.Q) -} - -module.exports = ECPair diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 3a1310b..4f731dc 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -180,7 +180,7 @@ function fixMultisigOrder (input, transaction, vin) { var unmatched = input.signatures.concat() input.signatures = input.pubKeys.map(function (pubKey) { - var keyPair = ECPair.fromPublicKeyBuffer(pubKey) + var keyPair = ECPair.fromPublicKey(pubKey) var match // check for a signature @@ -686,7 +686,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy throw new Error('Inconsistent redeemScript') } - var kpPubKey = keyPair.publicKey || keyPair.getPublicKeyBuffer() + var kpPubKey = keyPair.publicKey || keyPair.getPublicKey() if (!canSign(input)) { if (witnessValue !== undefined) { if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue') diff --git a/src/types.js b/src/types.js index c1e5fb1..302043f 100644 --- a/src/types.js +++ b/src/types.js @@ -16,11 +16,9 @@ function Satoshi (value) { } // external dependent types -var BigInt = typeforce.quacksLike('BigInteger') var ECPoint = typeforce.quacksLike('Point') // exposed, external API -var ECSignature = typeforce.compile({ r: BigInt, s: BigInt }) var Network = typeforce.compile({ messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), bip32: { @@ -34,11 +32,9 @@ var Network = typeforce.compile({ // extend typeforce types with ours var types = { - BigInt: BigInt, BIP32Path: BIP32Path, Buffer256bit: typeforce.BufferN(32), ECPoint: ECPoint, - ECSignature: ECSignature, Hash160bit: typeforce.BufferN(20), Hash256bit: typeforce.BufferN(32), Network: Network, diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index b042ae1..4bf6785 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -94,7 +94,7 @@ describe('Bitcoin-core', function () { var keyPair = bitcoin.ECPair.fromWIF(string, network) it('fromWIF imports ' + string, function () { - assert.strictEqual(keyPair.d.toHex(), hex) + assert.strictEqual(keyPair.getPrivateKey().toString('hex'), hex) assert.strictEqual(keyPair.compressed, params.isCompressed) }) diff --git a/test/ecdsa.js b/test/ecdsa.js deleted file mode 100644 index 1f20a41..0000000 --- a/test/ecdsa.js +++ /dev/null @@ -1,135 +0,0 @@ -/* global describe, it */ - -var assert = require('assert') -var bcrypto = require('../src/crypto') -var ecdsa = require('../src/ecdsa') -var hoodwink = require('hoodwink') - -var BigInteger = require('bigi') - -var curve = ecdsa.__curve - -var fixtures = require('./fixtures/ecdsa.json') - -describe('ecdsa', function () { - function fromRaw (signature) { - return { - r: new BigInteger(signature.r, 16), - s: new BigInteger(signature.s, 16) - } - } - - function toRaw (signature) { - return { - r: signature.r.toHex(), - s: signature.s.toHex() - } - } - - describe('deterministicGenerateK', function () { - function checkSig () { - return true - } - - fixtures.valid.ecdsa.forEach(function (f) { - it('for "' + f.message + '"', function () { - var x = BigInteger.fromHex(f.d).toBuffer(32) - var h1 = bcrypto.sha256(f.message) - - var k = ecdsa.deterministicGenerateK(h1, x, checkSig) - assert.strictEqual(k.toHex(), f.k) - }) - }) - - it('loops until an appropriate k value is found', hoodwink(function () { - this.mock(BigInteger, 'fromBuffer', function f (b) { - assert.strictEqual(b.length, 32) - if (f.calls === 0) return BigInteger.ZERO // < 1 - if (f.calls === 1) return curve.n // > n - 1 - if (f.calls === 2) return new BigInteger('42') // valid - }, 3) - - var x = new BigInteger('1').toBuffer(32) - var h1 = Buffer.alloc(32) - var k = ecdsa.deterministicGenerateK(h1, x, checkSig) - - assert.strictEqual(k.toString(), '42') - })) - - it('loops until a suitable signature is found', hoodwink(function () { - var checkSigStub = this.stub(function f () { - if (f.calls === 0) return false // bad signature - if (f.calls === 1) return true // good signature - }, 2) - - var x = BigInteger.ONE.toBuffer(32) - var h1 = Buffer.alloc(32) - var k = ecdsa.deterministicGenerateK(h1, x, checkSigStub) - - assert.strictEqual(k.toHex(), 'a9b1a1a84a4c2f96b6158ed7a81404c50cb74373c22e8d9e02d0411d719acae2') - })) - - fixtures.valid.rfc6979.forEach(function (f) { - it('produces the expected k values for ' + f.message + " if k wasn't suitable", function () { - var x = BigInteger.fromHex(f.d).toBuffer(32) - var h1 = bcrypto.sha256(f.message) - - var results = [] - ecdsa.deterministicGenerateK(h1, x, function (k) { - results.push(k) - - return results.length === 16 - }) - - assert.strictEqual(results[0].toHex(), f.k0) - assert.strictEqual(results[1].toHex(), f.k1) - assert.strictEqual(results[15].toHex(), f.k15) - }) - }) - }) - - describe('sign', function () { - fixtures.valid.ecdsa.forEach(function (f) { - it('produces a deterministic signature for "' + f.message + '"', function () { - var d = BigInteger.fromHex(f.d) - var hash = bcrypto.sha256(f.message) - var signature = ecdsa.sign(hash, d) - - assert.deepEqual(toRaw(signature), f.signature) - }) - }) - - it('should sign with low S value', function () { - var hash = bcrypto.sha256('Vires in numeris') - var sig = ecdsa.sign(hash, BigInteger.ONE) - - // See BIP62 for more information - var N_OVER_TWO = curve.n.shiftRight(1) - assert(sig.s.compareTo(N_OVER_TWO) <= 0) - }) - }) - - describe('verify', function () { - fixtures.valid.ecdsa.forEach(function (f) { - it('verifies a valid signature for "' + f.message + '"', function () { - var d = BigInteger.fromHex(f.d) - var H = bcrypto.sha256(f.message) - var signature = fromRaw(f.signature) - var Q = curve.G.multiply(d) - - assert(ecdsa.verify(H, signature, Q)) - }) - }) - - fixtures.invalid.verify.forEach(function (f) { - it('fails to verify with ' + f.description, function () { - var H = bcrypto.sha256(f.message) - var d = BigInteger.fromHex(f.d) - var signature = fromRaw(f.signature) - var Q = curve.G.multiply(d) - - assert.strictEqual(ecdsa.verify(H, signature, Q), false) - }) - }) - }) -}) diff --git a/test/ecpair.js b/test/ecpair.js index 52e5499..89deae2 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -1,34 +1,36 @@ /* global describe, it, beforeEach */ /* eslint-disable no-new */ -var assert = require('assert') -var ecdsa = require('../src/ecdsa') -var ecurve = require('ecurve') -var proxyquire = require('proxyquire') -var hoodwink = require('hoodwink') +let assert = require('assert') +let proxyquire = require('proxyquire') +let hoodwink = require('hoodwink') -var BigInteger = require('bigi') -var ECPair = require('../src/ecpair') +let ECPair = require('../src/ecpair') +let tinysecp = require('tiny-secp256k1') -var fixtures = require('./fixtures/ecpair.json') -var curve = ecdsa.__curve +let fixtures = require('./fixtures/ecpair.json') -var NETWORKS = require('../src/networks') -var NETWORKS_LIST = [] // Object.values(NETWORKS) -for (var networkName in NETWORKS) { +let NETWORKS = require('../src/networks') +let NETWORKS_LIST = [] // Object.values(NETWORKS) +for (let networkName in NETWORKS) { NETWORKS_LIST.push(NETWORKS[networkName]) } +let ZERO = Buffer.alloc(32, 0) +let ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') +let GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 'hex') +let GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 'hex') + describe('ECPair', function () { describe('constructor', function () { it('defaults to compressed', function () { - var keyPair = new ECPair(BigInteger.ONE) + let keyPair = ECPair.fromPrivateKey(ONE) assert.strictEqual(keyPair.compressed, true) }) it('supports the uncompressed option', function () { - var keyPair = new ECPair(BigInteger.ONE, null, { + let keyPair = ECPair.fromPrivateKey(ONE, { compressed: false }) @@ -36,7 +38,7 @@ describe('ECPair', function () { }) it('supports the network option', function () { - var keyPair = new ECPair(BigInteger.ONE, null, { + let keyPair = ECPair.fromPrivateKey(ONE, { compressed: false, network: NETWORKS.testnet }) @@ -45,51 +47,56 @@ describe('ECPair', function () { }) fixtures.valid.forEach(function (f) { - it('calculates the public point for ' + f.WIF, function () { - var d = new BigInteger(f.d) - var keyPair = new ECPair(d, null, { + it('derives public key for ' + f.WIF, function () { + let d = Buffer.from(f.d, 'hex') + console.log(d) + + let keyPair = ECPair.fromPrivateKey(d, { compressed: f.compressed }) - assert.strictEqual(keyPair.getPublicKeyBuffer().toString('hex'), f.Q) + assert.strictEqual(keyPair.getPublicKey().toString('hex'), f.Q) }) }) fixtures.invalid.constructor.forEach(function (f) { it('throws ' + f.exception, function () { - var d = f.d && new BigInteger(f.d) - var Q = f.Q && ecurve.Point.decodeFrom(curve, Buffer.from(f.Q, 'hex')) - - assert.throws(function () { - new ECPair(d, Q, f.options) - }, new RegExp(f.exception)) + if (f.d) { + let d = Buffer.from(f.d, 'hex') + assert.throws(function () { + ECPair.fromPrivateKey(d, f.options) + }, new RegExp(f.exception)) + } else { + let Q = Buffer.from(f.Q, 'hex') + assert.throws(function () { + ECPair.fromPublicKey(Q, f.options) + }, new RegExp(f.exception)) + } }) }) }) - describe('getPublicKeyBuffer', function () { - var keyPair + describe('getPublicKey', function () { + let keyPair beforeEach(function () { - keyPair = new ECPair(BigInteger.ONE) + keyPair = ECPair.fromPrivateKey(ONE) }) - it('wraps Q.getEncoded', hoodwink(function () { - this.mock(keyPair.Q, 'getEncoded', function (compressed) { - assert.strictEqual(compressed, keyPair.compressed) - }, 1) - - keyPair.getPublicKeyBuffer() + it('calls pointFromScalar lazily', hoodwink(function () { + assert.strictEqual(keyPair.__Q, null) + keyPair.getPublicKey() + assert.strictEqual(keyPair.__Q.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') })) }) describe('fromWIF', function () { fixtures.valid.forEach(function (f) { it('imports ' + f.WIF + ' (' + f.network + ')', function () { - var network = NETWORKS[f.network] - var keyPair = ECPair.fromWIF(f.WIF, network) + let network = NETWORKS[f.network] + let keyPair = ECPair.fromWIF(f.WIF, network) - assert.strictEqual(keyPair.d.toString(), f.d) + assert.strictEqual(keyPair.getPrivateKey().toString('hex'), f.d) assert.strictEqual(keyPair.compressed, f.compressed) assert.strictEqual(keyPair.network, network) }) @@ -97,9 +104,9 @@ describe('ECPair', function () { fixtures.valid.forEach(function (f) { it('imports ' + f.WIF + ' (via list of networks)', function () { - var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) - assert.strictEqual(keyPair.d.toString(), f.d) + assert.strictEqual(keyPair.getPrivateKey().toString('hex'), f.d) assert.strictEqual(keyPair.compressed, f.compressed) assert.strictEqual(keyPair.network, NETWORKS[f.network]) }) @@ -108,7 +115,7 @@ describe('ECPair', function () { fixtures.invalid.fromWIF.forEach(function (f) { it('throws on ' + f.WIF, function () { assert.throws(function () { - var networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST + let networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST ECPair.fromWIF(f.WIF, networks) }, new RegExp(f.exception)) @@ -119,30 +126,29 @@ describe('ECPair', function () { describe('toWIF', function () { fixtures.valid.forEach(function (f) { it('exports ' + f.WIF, function () { - var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) - var result = keyPair.toWIF() - + let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + let result = keyPair.toWIF() assert.strictEqual(result, f.WIF) }) }) }) describe('makeRandom', function () { - var d = Buffer.from('0404040404040404040404040404040404040404040404040404040404040404', 'hex') - var exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' + let d = Buffer.alloc(32, 4) + let exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' describe('uses randombytes RNG', function () { it('generates a ECPair', function () { - var stub = { randombytes: function () { return d } } - var ProxiedECPair = proxyquire('../src/ecpair', stub) + let stub = { randombytes: function () { return d } } + let ProxiedECPair = proxyquire('../src/ecpair', stub) - var keyPair = ProxiedECPair.makeRandom() + let keyPair = ProxiedECPair.makeRandom() assert.strictEqual(keyPair.toWIF(), exWIF) }) }) it('allows a custom RNG to be used', function () { - var keyPair = ECPair.makeRandom({ + let keyPair = ECPair.makeRandom({ rng: function (size) { return d.slice(0, size) } }) @@ -150,14 +156,14 @@ describe('ECPair', function () { }) it('retains the same defaults as ECPair constructor', function () { - var keyPair = ECPair.makeRandom() + let keyPair = ECPair.makeRandom() assert.strictEqual(keyPair.compressed, true) assert.strictEqual(keyPair.network, NETWORKS.bitcoin) }) it('supports the options parameter', function () { - var keyPair = ECPair.makeRandom({ + let keyPair = ECPair.makeRandom({ compressed: false, network: NETWORKS.testnet }) @@ -168,7 +174,7 @@ describe('ECPair', function () { it('throws if d is bad length', function () { function rng () { - return BigInteger.ZERO.toBuffer(28) + return Buffer.alloc(28) } assert.throws(function () { @@ -176,20 +182,20 @@ describe('ECPair', function () { }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/) }) - it('loops until d is within interval [1, n - 1] : 1', hoodwink(function () { - var rng = this.stub(function f () { - if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // 0 - return BigInteger.ONE.toBuffer(32) // >0 + it('loops until d is within interval [1, n) : 1', hoodwink(function () { + let rng = this.stub(function f () { + if (f.calls === 0) return ZERO // 0 + return ONE // >0 }, 2) ECPair.makeRandom({ rng: rng }) })) - it('loops until d is within interval [1, n - 1] : n - 1', hoodwink(function () { - var rng = this.stub(function f () { - if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // <1 - if (f.calls === 1) return curve.n.toBuffer(32) // >n-1 - return curve.n.subtract(BigInteger.ONE).toBuffer(32) // n-1 + it('loops until d is within interval [1, n) : n - 1', hoodwink(function () { + let rng = this.stub(function f () { + if (f.calls === 0) return ZERO // <1 + if (f.calls === 1) return GROUP_ORDER // >n-1 + return GROUP_ORDER_LESS_1 // n-1 }, 3) ECPair.makeRandom({ rng: rng }) @@ -199,35 +205,36 @@ describe('ECPair', function () { describe('getNetwork', function () { fixtures.valid.forEach(function (f) { it('returns ' + f.network + ' for ' + f.WIF, function () { - var network = NETWORKS[f.network] - var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + let network = NETWORKS[f.network] + let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) assert.strictEqual(keyPair.getNetwork(), network) }) }) }) - describe('ecdsa wrappers', function () { - var keyPair, hash + describe('tinysecp wrappers', function () { + let keyPair, hash, signature beforeEach(function () { keyPair = ECPair.makeRandom() - hash = Buffer.alloc(32) + hash = ZERO + signature = Buffer.alloc(64, 1) }) describe('signing', function () { - it('wraps ecdsa.sign', hoodwink(function () { - this.mock(ecdsa, 'sign', function (h, d) { + it('wraps tinysecp.sign', hoodwink(function () { + this.mock(tinysecp, 'sign', function (h, d) { assert.strictEqual(h, hash) - assert.strictEqual(d, keyPair.d) - return { r: BigInteger.ONE, s: BigInteger.ONE } + assert.strictEqual(d, keyPair.getPrivateKey()) + return signature }, 1) - keyPair.sign(hash) + assert.strictEqual(keyPair.sign(hash), signature) })) it('throws if no private key is found', function () { - keyPair.d = null + delete keyPair.__d assert.throws(function () { keyPair.sign(hash) @@ -236,24 +243,15 @@ describe('ECPair', function () { }) describe('verify', function () { - var signature - - beforeEach(function () { - signature = keyPair.sign(hash) - }) - - it('wraps ecdsa.verify', hoodwink(function () { - this.mock(ecdsa, 'verify', function (h, s, q) { + it('wraps tinysecp.verify', hoodwink(function () { + this.mock(tinysecp, 'verify', function (h, q, s) { assert.strictEqual(h, hash) - // assert.strictEqual(s, signature) - assert.deepEqual(s, { - r: BigInteger.fromBuffer(signature.slice(0, 32)), - s: BigInteger.fromBuffer(signature.slice(32, 64)) - }) - assert.strictEqual(q, keyPair.Q) + assert.strictEqual(q, keyPair.getPublicKey()) + assert.strictEqual(s, signature) + return true }, 1) - keyPair.verify(hash, signature) + assert.strictEqual(keyPair.verify(hash, signature), true) })) }) }) diff --git a/test/fixtures/ecpair.json b/test/fixtures/ecpair.json index 6f0a1a2..77eee08 100644 --- a/test/fixtures/ecpair.json +++ b/test/fixtures/ecpair.json @@ -1,7 +1,7 @@ { "valid": [ { - "d": "1", + "d": "0000000000000000000000000000000000000000000000000000000000000001", "Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", "compressed": true, "network": "bitcoin", @@ -9,7 +9,7 @@ "WIF": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" }, { - "d": "1", + "d": "0000000000000000000000000000000000000000000000000000000000000001", "Q": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", "compressed": false, "network": "bitcoin", @@ -17,7 +17,7 @@ "WIF": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf" }, { - "d": "19898843618908353587043383062236220484949425084007183071220218307100305431102", + "d": "2bfe58ab6d9fd575bdc3a624e4825dd2b375d64ac033fbc46ea79dbab4f69a3e", "Q": "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", "compressed": true, "network": "bitcoin", @@ -25,7 +25,7 @@ "WIF": "KxhEDBQyyEFymvfJD96q8stMbJMbZUb6D1PmXqBWZDU2WvbvVs9o" }, { - "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", + "d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b", "Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34", "compressed": true, "network": "bitcoin", @@ -33,7 +33,7 @@ "WIF": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx" }, { - "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", + "d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b", "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", "compressed": false, "network": "bitcoin", @@ -41,7 +41,7 @@ "WIF": "5JdxzLtFPHNe7CAL8EBC6krdFv9pwPoRo4e3syMZEQT9srmK8hh" }, { - "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", + "d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b", "Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34", "compressed": true, "network": "testnet", @@ -49,7 +49,7 @@ "WIF": "cRD9b1m3vQxmwmjSoJy7Mj56f4uNFXjcWMCzpQCEmHASS4edEwXv" }, { - "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", + "d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b", "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", "compressed": false, "network": "testnet", @@ -57,7 +57,7 @@ "WIF": "92Qba5hnyWSn5Ffcka56yMQauaWY6ZLd91Vzxbi4a9CCetaHtYj" }, { - "d": "115792089237316195423570985008687907852837564279074904382605163141518161494336", + "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", "Q": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", "compressed": true, "network": "bitcoin", @@ -68,36 +68,27 @@ "invalid": { "constructor": [ { - "exception": "Private key must be greater than 0", - "d": "-1" + "exception": "Private key not in range \\[1, n\\)", + "d": "0000000000000000000000000000000000000000000000000000000000000000" }, { - "exception": "Private key must be greater than 0", - "d": "0" + "exception": "Private key not in range \\[1, n\\)", + "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" }, { - "exception": "Private key must be less than the curve order", - "d": "115792089237316195423570985008687907852837564279074904382605163141518161494337" - }, - { - "exception": "Private key must be less than the curve order", - "d": "115792089237316195423570985008687907853269984665640564039457584007913129639935" + "exception": "Private key not in range \\[1, n\\)", + "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142" }, { "exception": "Expected property \"compressed\" of type \\?Boolean, got Number 2", - "d": "1", + "d": "0000000000000000000000000000000000000000000000000000000000000001", "options": { "compressed": 2 } }, - { - "exception": "Unexpected publicKey parameter", - "d": "1", - "Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" - }, { "exception": "Expected property \"network.messagePrefix\" of type Buffer|String, got undefined", - "d": "1", + "d": "0000000000000000000000000000000000000000000000000000000000000001", "options": { "network": {} } diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 0baaffa..46a0393 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -72,7 +72,7 @@ let baddress = bitcoin.address let bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), network.pubKeyHash) } function randomAddress () { diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 0f8ae52..5cd6a46 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -12,7 +12,7 @@ let baddress = bitcoin.address let bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), network.pubKeyHash) } describe('bitcoinjs-lib (addresses)', function () { @@ -26,7 +26,7 @@ describe('bitcoinjs-lib (addresses)', function () { it('can generate an address from a SHA256 hash', function () { var hash = bitcoin.crypto.sha256(Buffer.from('correct horse battery staple')) - var keyPair = bitcoin.ECPair.makeRandom({ rng: () => hash }) + var keyPair = bitcoin.ECPair.fromPrivateKey(hash) var address = getAddress(keyPair) // Generating addresses from SHA256 hashes is not secure if the input to the hash function is predictable @@ -57,7 +57,7 @@ describe('bitcoinjs-lib (addresses)', function () { it('can generate a SegWit address', function () { var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var pubKey = keyPair.getPublicKeyBuffer() + var pubKey = keyPair.getPublicKey() var scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(pubKey)) var address = bitcoin.address.fromOutputScript(scriptPubKey) @@ -67,7 +67,7 @@ describe('bitcoinjs-lib (addresses)', function () { it('can generate a SegWit address (via P2SH)', function () { var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var pubKey = keyPair.getPublicKeyBuffer() + var pubKey = keyPair.getPublicKey() var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(pubKey)) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 2b51ed7..52b9f00 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -25,11 +25,11 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { bitcoin.opcodes.OP_DROP, bitcoin.opcodes.OP_ELSE, - bQ.getPublicKeyBuffer(), + bQ.getPublicKey(), bitcoin.opcodes.OP_CHECKSIGVERIFY, bitcoin.opcodes.OP_ENDIF, - aQ.getPublicKeyBuffer(), + aQ.getPublicKey(), bitcoin.opcodes.OP_CHECKSIG ]) } diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 8566628..58ae431 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -24,7 +24,7 @@ describe('bitcoinjs-lib (crypto)', function () { assert(bitcoin.script.pubKeyHash.input.check(scriptChunks), 'Expected pubKeyHash script') var prevOutScript = bitcoin.address.toOutputScript('1ArJ9vRaQcoQ29mTWZH768AmRwzb6Zif1z') var scriptSignature = bitcoin.script.signature.decode(scriptChunks[0]) - var publicKey = bitcoin.ECPair.fromPublicKeyBuffer(scriptChunks[1]) + var publicKey = bitcoin.ECPair.fromPublicKey(scriptChunks[1]) var m = tx.hashForSignature(vin, prevOutScript, scriptSignature.hashType) assert(publicKey.verify(m, scriptSignature.signature), 'Invalid m') diff --git a/test/integration/csv.js b/test/integration/csv.js index ef9c30f..6aa3087 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -26,11 +26,11 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { bitcoin.opcodes.OP_DROP, bitcoin.opcodes.OP_ELSE, - bQ.getPublicKeyBuffer(), + bQ.getPublicKey(), bitcoin.opcodes.OP_CHECKSIGVERIFY, bitcoin.opcodes.OP_ENDIF, - aQ.getPublicKeyBuffer(), + aQ.getPublicKey(), bitcoin.opcodes.OP_CHECKSIG ]) } diff --git a/test/integration/stealth.js b/test/integration/stealth.js index b3cea41..91508e2 100644 --- a/test/integration/stealth.js +++ b/test/integration/stealth.js @@ -1,74 +1,72 @@ /* global describe, it */ let assert = require('assert') -let bigi = require('bigi') let bitcoin = require('../../') - -let ecurve = require('ecurve') -let secp256k1 = ecurve.getCurveByName('secp256k1') -let G = secp256k1.G -let n = secp256k1.n +let ecc = require('tiny-secp256k1') // TODO: remove let baddress = bitcoin.address let bcrypto = bitcoin.crypto function getAddress (node) { - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), bitcoin.networks.bitcoin.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), bitcoin.networks.bitcoin.pubKeyHash) } // vG = (dG \+ sha256(e * dG)G) function stealthSend (e, Q) { - var eQ = Q.multiply(e) // shared secret - var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) - var cG = G.multiply(c) - var vG = new bitcoin.ECPair(null, Q.add(cG)) + var eQ = ecc.pointMultiply(Q, e, true) // shared secret + var c = bitcoin.crypto.sha256(eQ) + var Qc = ecc.pointAddScalar(Q, c) + var vG = bitcoin.ECPair.fromPublicKey(Qc) return vG } // v = (d + sha256(eG * d)) function stealthReceive (d, eG) { - var eQ = eG.multiply(d) // shared secret - var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) - var v = new bitcoin.ECPair(d.add(c).mod(n)) + var eQ = ecc.pointMultiply(eG, d) // shared secret + var c = bitcoin.crypto.sha256(eQ) + var dc = ecc.privateAdd(d, c) + var v = bitcoin.ECPair.fromPrivateKey(dc) return v } // d = (v - sha256(e * dG)) function stealthRecoverLeaked (v, e, Q) { - var eQ = Q.multiply(e) // shared secret - var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) - var d = new bitcoin.ECPair(v.subtract(c).mod(n)) + var eQ = ecc.pointMultiply(Q, e) // shared secret + var c = bitcoin.crypto.sha256(eQ) + var vc = ecc.privateSub(v, c) + var d = bitcoin.ECPair.fromPrivateKey(vc) return d } // vG = (rG \+ sha256(e * dG)G) function stealthDualSend (e, R, Q) { - var eQ = Q.multiply(e) // shared secret - var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) - var cG = G.multiply(c) - var vG = new bitcoin.ECPair(null, R.add(cG)) + var eQ = ecc.pointMultiply(Q, e) // shared secret + var c = bitcoin.crypto.sha256(eQ) + var Rc = ecc.pointAddScalar(R, c) + var vG = bitcoin.ECPair.fromPublicKey(Rc) return vG } // vG = (rG \+ sha256(eG * d)G) function stealthDualScan (d, R, eG) { - var eQ = eG.multiply(d) // shared secret - var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) - var cG = G.multiply(c) - var vG = new bitcoin.ECPair(null, R.add(cG)) + var eQ = ecc.pointMultiply(eG, d) // shared secret + var c = bitcoin.crypto.sha256(eQ) + var Rc = ecc.pointAddScalar(R, c) + var vG = bitcoin.ECPair.fromPublicKey(Rc) return vG } // v = (r + sha256(eG * d)) function stealthDualReceive (d, r, eG) { - var eQ = eG.multiply(d) // shared secret - var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) - var v = new bitcoin.ECPair(r.add(c).mod(n)) + var eQ = ecc.pointMultiply(eG, d) // shared secret + var c = bitcoin.crypto.sha256(eQ) + var rc = ecc.privateAdd(r, c) + var v = bitcoin.ECPair.fromPrivateKey(rc) return v } @@ -80,12 +78,12 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender // ... recipient reveals public key (recipient.Q) to sender - var forSender = stealthSend(nonce.d, recipient.Q) + var forSender = stealthSend(nonce.getPrivateKey(), recipient.getPublicKey()) assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient - var forRecipient = stealthReceive(recipient.d, nonce.Q) + var forRecipient = stealthReceive(recipient.getPrivateKey(), nonce.getPublicKey()) assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n') @@ -98,11 +96,11 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.makeRandom() // private to sender // ... recipient reveals public key (recipient.Q) to sender - var forSender = stealthSend(nonce.d, recipient.Q) + var forSender = stealthSend(nonce.getPrivateKey(), recipient.getPublicKey()) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient - var forRecipient = stealthReceive(recipient.d, nonce.Q) + var forRecipient = stealthReceive(recipient.getPrivateKey(), nonce.getPublicKey()) assert.doesNotThrow(function () { forRecipient.toWIF() }) // sender and recipient, both derived same address @@ -114,15 +112,15 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.makeRandom() // private to sender // ... recipient reveals public key (recipient.Q) to sender - var forSender = stealthSend(nonce.d, recipient.Q) + var forSender = stealthSend(nonce.getPrivateKey(), recipient.getPublicKey()) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient - var forRecipient = stealthReceive(recipient.d, nonce.Q) + var forRecipient = stealthReceive(recipient.getPrivateKey(), nonce.getPublicKey()) assert.doesNotThrow(function () { forRecipient.toWIF() }) // ... recipient accidentally leaks forRecipient.d on the blockchain - var leaked = stealthRecoverLeaked(forRecipient.d, nonce.d, recipient.Q) + var leaked = stealthRecoverLeaked(forRecipient.getPrivateKey(), nonce.getPrivateKey(), recipient.getPublicKey()) assert.equal(leaked.toWIF(), recipient.toWIF()) }) @@ -133,15 +131,15 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - var forSender = stealthDualSend(nonce.d, recipient.Q, scan.Q) + var forSender = stealthDualSend(nonce.getPrivateKey(), recipient.getPublicKey(), scan.getPublicKey()) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to scanner - var forScanner = stealthDualScan(scan.d, recipient.Q, nonce.Q) + var forScanner = stealthDualScan(scan.getPrivateKey(), recipient.getPublicKey(), nonce.getPublicKey()) assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - var forRecipient = stealthDualReceive(scan.d, recipient.d, nonce.Q) + var forRecipient = stealthDualReceive(scan.getPrivateKey(), recipient.getPrivateKey(), nonce.getPublicKey()) assert.doesNotThrow(function () { forRecipient.toWIF() }) // scanner, sender and recipient, all derived same address @@ -155,15 +153,15 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.makeRandom() // private to sender // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - var forSender = stealthDualSend(nonce.d, recipient.Q, scan.Q) + var forSender = stealthDualSend(nonce.getPrivateKey(), recipient.getPublicKey(), scan.getPublicKey()) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to scanner - var forScanner = stealthDualScan(scan.d, recipient.Q, nonce.Q) + var forScanner = stealthDualScan(scan.getPrivateKey(), recipient.getPublicKey(), nonce.getPublicKey()) assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - var forRecipient = stealthDualReceive(scan.d, recipient.d, nonce.Q) + var forRecipient = stealthDualReceive(scan.getPrivateKey(), recipient.getPrivateKey(), nonce.getPublicKey()) assert.doesNotThrow(function () { forRecipient.toWIF() }) // scanner, sender and recipient, all derived same address diff --git a/test/integration/transactions.js b/test/integration/transactions.js index c1e4862..70d68a6 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -10,7 +10,7 @@ let baddress = bitcoin.address let bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), network.pubKeyHash) } function rng () { @@ -115,7 +115,7 @@ describe('bitcoinjs-lib (transactions)', function () { '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx9rcrL7' ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) }) - var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() }) + var pubKeys = keyPairs.map(function (x) { return x.getPublicKey() }) var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) @@ -150,7 +150,7 @@ describe('bitcoinjs-lib (transactions)', function () { this.timeout(30000) var keyPair = bitcoin.ECPair.fromWIF('cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA', regtest) - var pubKey = keyPair.getPublicKeyBuffer() + var pubKey = keyPair.getPublicKey() var pubKeyHash = bitcoin.crypto.hash160(pubKey) var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash) @@ -191,7 +191,7 @@ describe('bitcoinjs-lib (transactions)', function () { 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87KcLPVfXz', 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87L7FgDCKE' ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) }) - var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() }) + var pubKeys = keyPairs.map(function (x) { return x.getPublicKey() }) var witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) var redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) @@ -241,7 +241,7 @@ describe('bitcoinjs-lib (transactions)', function () { var ss = bitcoin.script.signature.decode(scriptSig.signature) var hash = tx.hashForSignature(i, prevOutScript, ss.hashType) - assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.getPublicKeyBuffer().toString('hex')) + assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.getPublicKey().toString('hex')) assert.strictEqual(keyPair.verify(hash, ss.signature), true) }) }) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 86f7eca..9c9cc47 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -7,7 +7,6 @@ let bscript = require('../src/script') let btemplates = require('../src/templates') let ops = require('bitcoin-ops') -let BigInteger = require('bigi') let ECPair = require('../src/ecpair') let Transaction = require('../src/transaction') let TransactionBuilder = require('../src/transaction_builder') @@ -17,7 +16,7 @@ let fixtures = require('./fixtures/transaction_builder') // TODO: remove function getAddress (node) { - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), NETWORKS.bitcoin.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), NETWORKS.bitcoin.pubKeyHash) } function construct (f, dontSign) { @@ -89,7 +88,7 @@ function construct (f, dontSign) { describe('TransactionBuilder', function () { // constants - var keyPair = new ECPair(BigInteger.ONE) + var keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) var scripts = [ '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP' diff --git a/test/types.js b/test/types.js index 832b32d..2dbc554 100644 --- a/test/types.js +++ b/test/types.js @@ -5,18 +5,6 @@ var types = require('../src/types') var typeforce = require('typeforce') describe('types', function () { - describe('BigInt/ECPoint', function () { - it('return true for duck types', function () { - assert(types.BigInt(new function BigInteger () {}())) - assert(types.ECPoint(new function Point () {}())) - }) - - it('return false for bad types', function () { - assert(!types.BigInt(new function NotABigInteger () {}())) - assert(!types.ECPoint(new function NotAPoint () {}())) - }) - }) - describe('Buffer Hash160/Hash256', function () { var buffer20byte = Buffer.alloc(20) var buffer32byte = Buffer.alloc(32) From 2fe220517fb8b68cc2993fe98c456d1930de4713 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 30 May 2018 11:19:46 +1000 Subject: [PATCH 032/568] transition ECPair to properties over getters --- src/ecpair.js | 17 +++++++---------- test/bitcoin.core.js | 2 +- test/ecpair.js | 18 ++++++++++-------- test/integration/_regtest.js | 2 +- test/integration/addresses.js | 8 +++----- test/integration/cltv.js | 4 ++-- test/integration/csv.js | 4 ++-- test/integration/stealth.js | 28 ++++++++++++++-------------- test/integration/transactions.js | 13 ++++++------- test/transaction_builder.js | 2 +- 10 files changed, 47 insertions(+), 51 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index 9467b1b..d0653be 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -24,18 +24,15 @@ function ECPair (d, Q, options) { if (Q) this.__Q = ecc.pointCompress(Q, this.compressed) } -ECPair.prototype.getNetwork = function () { - return this.network -} +Object.defineProperty(ECPair.prototype, 'privateKey', { + enumerable: false, + get: function () { return this.__d } +}) -ECPair.prototype.getPrivateKey = function () { - return this.__d -} - -ECPair.prototype.getPublicKey = function () { +Object.defineProperty(ECPair.prototype, 'publicKey', { get: function () { if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed) return this.__Q -} +}}) ECPair.prototype.toWIF = function () { if (!this.__d) throw new Error('Missing private key') @@ -48,7 +45,7 @@ ECPair.prototype.sign = function (hash) { } ECPair.prototype.verify = function (hash, signature) { - return ecc.verify(hash, this.getPublicKey(), signature) + return ecc.verify(hash, this.publicKey, signature) } function fromPrivateKey (buffer, options) { diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index 4bf6785..4d73c2a 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -94,7 +94,7 @@ describe('Bitcoin-core', function () { var keyPair = bitcoin.ECPair.fromWIF(string, network) it('fromWIF imports ' + string, function () { - assert.strictEqual(keyPair.getPrivateKey().toString('hex'), hex) + assert.strictEqual(keyPair.privateKey.toString('hex'), hex) assert.strictEqual(keyPair.compressed, params.isCompressed) }) diff --git a/test/ecpair.js b/test/ecpair.js index 89deae2..5e2ea59 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -55,7 +55,7 @@ describe('ECPair', function () { compressed: f.compressed }) - assert.strictEqual(keyPair.getPublicKey().toString('hex'), f.Q) + assert.strictEqual(keyPair.publicKey.toString('hex'), f.Q) }) }) @@ -85,7 +85,9 @@ describe('ECPair', function () { it('calls pointFromScalar lazily', hoodwink(function () { assert.strictEqual(keyPair.__Q, null) - keyPair.getPublicKey() + + // .publicKey forces the memoization + assert.strictEqual(keyPair.publicKey.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') assert.strictEqual(keyPair.__Q.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') })) }) @@ -96,7 +98,7 @@ describe('ECPair', function () { let network = NETWORKS[f.network] let keyPair = ECPair.fromWIF(f.WIF, network) - assert.strictEqual(keyPair.getPrivateKey().toString('hex'), f.d) + assert.strictEqual(keyPair.privateKey.toString('hex'), f.d) assert.strictEqual(keyPair.compressed, f.compressed) assert.strictEqual(keyPair.network, network) }) @@ -106,7 +108,7 @@ describe('ECPair', function () { it('imports ' + f.WIF + ' (via list of networks)', function () { let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) - assert.strictEqual(keyPair.getPrivateKey().toString('hex'), f.d) + assert.strictEqual(keyPair.privateKey.toString('hex'), f.d) assert.strictEqual(keyPair.compressed, f.compressed) assert.strictEqual(keyPair.network, NETWORKS[f.network]) }) @@ -202,13 +204,13 @@ describe('ECPair', function () { })) }) - describe('getNetwork', function () { + describe('.network', function () { fixtures.valid.forEach(function (f) { it('returns ' + f.network + ' for ' + f.WIF, function () { let network = NETWORKS[f.network] let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) - assert.strictEqual(keyPair.getNetwork(), network) + assert.strictEqual(keyPair.network, network) }) }) }) @@ -226,7 +228,7 @@ describe('ECPair', function () { it('wraps tinysecp.sign', hoodwink(function () { this.mock(tinysecp, 'sign', function (h, d) { assert.strictEqual(h, hash) - assert.strictEqual(d, keyPair.getPrivateKey()) + assert.strictEqual(d, keyPair.privateKey) return signature }, 1) @@ -246,7 +248,7 @@ describe('ECPair', function () { it('wraps tinysecp.verify', hoodwink(function () { this.mock(tinysecp, 'verify', function (h, q, s) { assert.strictEqual(h, hash) - assert.strictEqual(q, keyPair.getPublicKey()) + assert.strictEqual(q, keyPair.publicKey) assert.strictEqual(s, signature) return true }, 1) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 46a0393..346fa41 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -72,7 +72,7 @@ let baddress = bitcoin.address let bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), network.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) } function randomAddress () { diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 5cd6a46..9451c69 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -12,7 +12,7 @@ let baddress = bitcoin.address let bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), network.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) } describe('bitcoinjs-lib (addresses)', function () { @@ -57,9 +57,8 @@ describe('bitcoinjs-lib (addresses)', function () { it('can generate a SegWit address', function () { var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var pubKey = keyPair.getPublicKey() - var scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(pubKey)) + var scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey)) var address = bitcoin.address.fromOutputScript(scriptPubKey) assert.strictEqual(address, 'bc1qt97wqg464zrhnx23upykca5annqvwkwujjglky') @@ -67,9 +66,8 @@ describe('bitcoinjs-lib (addresses)', function () { it('can generate a SegWit address (via P2SH)', function () { var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var pubKey = keyPair.getPublicKey() - var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(pubKey)) + var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey)) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) var address = bitcoin.address.fromOutputScript(scriptPubKey) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 52b9f00..b7c6c2c 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -25,11 +25,11 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { bitcoin.opcodes.OP_DROP, bitcoin.opcodes.OP_ELSE, - bQ.getPublicKey(), + bQ.publicKey, bitcoin.opcodes.OP_CHECKSIGVERIFY, bitcoin.opcodes.OP_ENDIF, - aQ.getPublicKey(), + aQ.publicKey, bitcoin.opcodes.OP_CHECKSIG ]) } diff --git a/test/integration/csv.js b/test/integration/csv.js index 6aa3087..adb28b5 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -26,11 +26,11 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { bitcoin.opcodes.OP_DROP, bitcoin.opcodes.OP_ELSE, - bQ.getPublicKey(), + bQ.publicKey, bitcoin.opcodes.OP_CHECKSIGVERIFY, bitcoin.opcodes.OP_ENDIF, - aQ.getPublicKey(), + aQ.publicKey, bitcoin.opcodes.OP_CHECKSIG ]) } diff --git a/test/integration/stealth.js b/test/integration/stealth.js index 91508e2..90acb7b 100644 --- a/test/integration/stealth.js +++ b/test/integration/stealth.js @@ -8,7 +8,7 @@ let ecc = require('tiny-secp256k1') let baddress = bitcoin.address let bcrypto = bitcoin.crypto function getAddress (node) { - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), bitcoin.networks.bitcoin.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.publicKey), bitcoin.networks.bitcoin.pubKeyHash) } // vG = (dG \+ sha256(e * dG)G) @@ -78,12 +78,12 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender // ... recipient reveals public key (recipient.Q) to sender - var forSender = stealthSend(nonce.getPrivateKey(), recipient.getPublicKey()) + var forSender = stealthSend(nonce.privateKey, recipient.publicKey) assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient - var forRecipient = stealthReceive(recipient.getPrivateKey(), nonce.getPublicKey()) + var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n') @@ -96,11 +96,11 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.makeRandom() // private to sender // ... recipient reveals public key (recipient.Q) to sender - var forSender = stealthSend(nonce.getPrivateKey(), recipient.getPublicKey()) + var forSender = stealthSend(nonce.privateKey, recipient.publicKey) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient - var forRecipient = stealthReceive(recipient.getPrivateKey(), nonce.getPublicKey()) + var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) assert.doesNotThrow(function () { forRecipient.toWIF() }) // sender and recipient, both derived same address @@ -112,15 +112,15 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.makeRandom() // private to sender // ... recipient reveals public key (recipient.Q) to sender - var forSender = stealthSend(nonce.getPrivateKey(), recipient.getPublicKey()) + var forSender = stealthSend(nonce.privateKey, recipient.publicKey) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient - var forRecipient = stealthReceive(recipient.getPrivateKey(), nonce.getPublicKey()) + var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) assert.doesNotThrow(function () { forRecipient.toWIF() }) // ... recipient accidentally leaks forRecipient.d on the blockchain - var leaked = stealthRecoverLeaked(forRecipient.getPrivateKey(), nonce.getPrivateKey(), recipient.getPublicKey()) + var leaked = stealthRecoverLeaked(forRecipient.privateKey, nonce.privateKey, recipient.publicKey) assert.equal(leaked.toWIF(), recipient.toWIF()) }) @@ -131,15 +131,15 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - var forSender = stealthDualSend(nonce.getPrivateKey(), recipient.getPublicKey(), scan.getPublicKey()) + var forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to scanner - var forScanner = stealthDualScan(scan.getPrivateKey(), recipient.getPublicKey(), nonce.getPublicKey()) + var forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - var forRecipient = stealthDualReceive(scan.getPrivateKey(), recipient.getPrivateKey(), nonce.getPublicKey()) + var forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) assert.doesNotThrow(function () { forRecipient.toWIF() }) // scanner, sender and recipient, all derived same address @@ -153,15 +153,15 @@ describe('bitcoinjs-lib (crypto)', function () { var nonce = bitcoin.ECPair.makeRandom() // private to sender // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - var forSender = stealthDualSend(nonce.getPrivateKey(), recipient.getPublicKey(), scan.getPublicKey()) + var forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to scanner - var forScanner = stealthDualScan(scan.getPrivateKey(), recipient.getPublicKey(), nonce.getPublicKey()) + var forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - var forRecipient = stealthDualReceive(scan.getPrivateKey(), recipient.getPrivateKey(), nonce.getPublicKey()) + var forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) assert.doesNotThrow(function () { forRecipient.toWIF() }) // scanner, sender and recipient, all derived same address diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 70d68a6..7f78ee8 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -10,7 +10,7 @@ let baddress = bitcoin.address let bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), network.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) } function rng () { @@ -115,7 +115,7 @@ describe('bitcoinjs-lib (transactions)', function () { '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx9rcrL7' ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) }) - var pubKeys = keyPairs.map(function (x) { return x.getPublicKey() }) + var pubKeys = keyPairs.map(function (x) { return x.publicKey }) var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) @@ -150,8 +150,7 @@ describe('bitcoinjs-lib (transactions)', function () { this.timeout(30000) var keyPair = bitcoin.ECPair.fromWIF('cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA', regtest) - var pubKey = keyPair.getPublicKey() - var pubKeyHash = bitcoin.crypto.hash160(pubKey) + var pubKeyHash = bitcoin.crypto.hash160(keyPair.publicKey) var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash) var redeemScriptHash = bitcoin.crypto.hash160(redeemScript) @@ -191,7 +190,7 @@ describe('bitcoinjs-lib (transactions)', function () { 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87KcLPVfXz', 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87L7FgDCKE' ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) }) - var pubKeys = keyPairs.map(function (x) { return x.getPublicKey() }) + var pubKeys = keyPairs.map(function (x) { return x.publicKey }) var witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) var redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) @@ -230,7 +229,7 @@ describe('bitcoinjs-lib (transactions)', function () { '032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d', '0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a', '039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f' - ].map(function (q) { return bitcoin.ECPair.fromPublicKeyBuffer(Buffer.from(q, 'hex')) }) + ].map(function (q) { return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')) }) var tx = bitcoin.Transaction.fromHex(txHex) @@ -241,7 +240,7 @@ describe('bitcoinjs-lib (transactions)', function () { var ss = bitcoin.script.signature.decode(scriptSig.signature) var hash = tx.hashForSignature(i, prevOutScript, ss.hashType) - assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.getPublicKey().toString('hex')) + assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.publicKey.toString('hex')) assert.strictEqual(keyPair.verify(hash, ss.signature), true) }) }) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 9c9cc47..c5c7c1e 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -16,7 +16,7 @@ let fixtures = require('./fixtures/transaction_builder') // TODO: remove function getAddress (node) { - return baddress.toBase58Check(bcrypto.hash160(node.getPublicKey()), NETWORKS.bitcoin.pubKeyHash) + return baddress.toBase58Check(bcrypto.hash160(node.publicKey), NETWORKS.bitcoin.pubKeyHash) } function construct (f, dontSign) { From 726e4c53b66a68bc52d57e49a168615dd013cbfb Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 5 Jun 2018 17:15:53 +1000 Subject: [PATCH 033/568] script: use ecc.isPoint for canonical public keys --- src/script.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/script.js b/src/script.js index 2860d66..6094589 100644 --- a/src/script.js +++ b/src/script.js @@ -1,5 +1,6 @@ var Buffer = require('safe-buffer').Buffer var bip66 = require('bip66') +let ecc = require('tiny-secp256k1') var pushdata = require('pushdata-bitcoin') var typeforce = require('typeforce') var types = require('./types') @@ -170,18 +171,7 @@ function toStack (chunks) { } function isCanonicalPubKey (buffer) { - if (!Buffer.isBuffer(buffer)) return false - if (buffer.length < 33) return false - - switch (buffer[0]) { - case 0x02: - case 0x03: - return buffer.length === 33 - case 0x04: - return buffer.length === 65 - } - - return false + return ecc.isPoint(buffer) } function isDefinedHashType (hashType) { From d934e543d8e16624fbe722af702064237c9a47ab Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 5 Jun 2018 17:21:43 +1000 Subject: [PATCH 034/568] rm Litecoin --- src/networks.js | 10 ---------- test/address.js | 27 +++++++++++++++++++-------- test/integration/addresses.js | 16 +++++++++++++--- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/networks.js b/src/networks.js index 43cd5fc..b168f7a 100644 --- a/src/networks.js +++ b/src/networks.js @@ -23,15 +23,5 @@ module.exports = { pubKeyHash: 0x6f, scriptHash: 0xc4, wif: 0xef - }, - litecoin: { - messagePrefix: '\x19Litecoin Signed Message:\n', - bip32: { - public: 0x019da462, - private: 0x019d9cfe - }, - pubKeyHash: 0x30, - scriptHash: 0x32, - wif: 0xb0 } } diff --git a/test/address.js b/test/address.js index e9c8a80..a9d7f9e 100644 --- a/test/address.js +++ b/test/address.js @@ -1,10 +1,21 @@ /* global describe, it */ -var assert = require('assert') -var baddress = require('../src/address') -var networks = require('../src/networks') -var bscript = require('../src/script') -var fixtures = require('./fixtures/address.json') +let assert = require('assert') +let baddress = require('../src/address') +let bscript = require('../src/script') +let fixtures = require('./fixtures/address.json') +let NETWORKS = Object.assign({ + litecoin: { + messagePrefix: '\x19Litecoin Signed Message:\n', + bip32: { + public: 0x019da462, + private: 0x019d9cfe + }, + pubKeyHash: 0x30, + scriptHash: 0x32, + wif: 0xb0 + } +}, require('../src/networks')) describe('address', function () { describe('fromBase58Check', function () { @@ -36,7 +47,7 @@ describe('address', function () { var actual = baddress.fromBech32(f.bech32) assert.strictEqual(actual.version, f.version) - assert.strictEqual(actual.prefix, networks[f.network].bech32) + assert.strictEqual(actual.prefix, NETWORKS[f.network].bech32) assert.strictEqual(actual.data.toString('hex'), f.data) }) }) @@ -54,7 +65,7 @@ describe('address', function () { fixtures.standard.forEach(function (f) { it('encodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', function () { var script = bscript.fromASM(f.script) - var address = baddress.fromOutputScript(script, networks[f.network]) + var address = baddress.fromOutputScript(script, NETWORKS[f.network]) assert.strictEqual(address, f.base58check || f.bech32.toLowerCase()) }) @@ -107,7 +118,7 @@ describe('address', function () { describe('toOutputScript', function () { fixtures.standard.forEach(function (f) { it('decodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', function () { - var script = baddress.toOutputScript(f.base58check || f.bech32, networks[f.network]) + var script = baddress.toOutputScript(f.base58check || f.bech32, NETWORKS[f.network]) assert.strictEqual(bscript.toASM(script), f.script) }) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 9451c69..af2e06c 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -4,6 +4,17 @@ let assert = require('assert') let bitcoin = require('../../') let dhttp = require('dhttp/200') +let LITECOIN = { + messagePrefix: '\x19Litecoin Signed Message:\n', + bip32: { + public: 0x019da462, + private: 0x019d9cfe + }, + pubKeyHash: 0x30, + scriptHash: 0x32, + wif: 0xb0 +} + // deterministic RNG for testing only function rng () { return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } @@ -135,10 +146,9 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can generate a Litecoin address', function () { - let litecoin = bitcoin.networks.litecoin - let keyPair = bitcoin.ECPair.makeRandom({ network: litecoin, rng: rng }) + let keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: rng }) let wif = keyPair.toWIF() - let address = getAddress(keyPair, litecoin) + let address = getAddress(keyPair, LITECOIN) assert.strictEqual(address, 'LZJSxZbjqJ2XVEquqfqHg1RQTDdfST5PTn') assert.strictEqual(wif, 'T7A4PUSgTDHecBxW1ZiYFrDNRih2o7M8Gf9xpoCgudPF9gDiNvuS') From 7592a6bcc2c21d7c430224670d010c0f4c500789 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 5 Jun 2018 17:17:40 +1000 Subject: [PATCH 035/568] script: rename isCanonicalSignature to isCanonicalScriptSignature --- src/script.js | 4 ++-- src/templates/multisig/input.js | 4 ++-- src/templates/pubkey/input.js | 4 ++-- src/templates/pubkeyhash/input.js | 4 ++-- src/templates/witnesspubkeyhash/input.js | 4 ++-- test/script.js | 3 ++- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/script.js b/src/script.js index 6094589..290ba00 100644 --- a/src/script.js +++ b/src/script.js @@ -181,7 +181,7 @@ function isDefinedHashType (hashType) { return hashTypeMod > 0x00 && hashTypeMod < 0x04 } -function isCanonicalSignature (buffer) { +function isCanonicalScriptSignature (buffer) { if (!Buffer.isBuffer(buffer)) return false if (!isDefinedHashType(buffer[buffer.length - 1])) return false @@ -199,7 +199,7 @@ module.exports = { signature: require('./script_signature'), isCanonicalPubKey: isCanonicalPubKey, - isCanonicalSignature: isCanonicalSignature, + isCanonicalScriptSignature: isCanonicalScriptSignature, isPushOnly: isPushOnly, isDefinedHashType: isDefinedHashType } diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js index a0b2eee..0ba0a06 100644 --- a/src/templates/multisig/input.js +++ b/src/templates/multisig/input.js @@ -7,7 +7,7 @@ var typeforce = require('typeforce') var OPS = require('bitcoin-ops') function partialSignature (value) { - return value === OPS.OP_0 || bscript.isCanonicalSignature(value) + return value === OPS.OP_0 || bscript.isCanonicalScriptSignature(value) } function check (script, allowIncomplete) { @@ -19,7 +19,7 @@ function check (script, allowIncomplete) { return chunks.slice(1).every(partialSignature) } - return chunks.slice(1).every(bscript.isCanonicalSignature) + return chunks.slice(1).every(bscript.isCanonicalScriptSignature) } check.toJSON = function () { return 'multisig input' } diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js index 4c61664..9149289 100644 --- a/src/templates/pubkey/input.js +++ b/src/templates/pubkey/input.js @@ -7,12 +7,12 @@ function check (script) { var chunks = bscript.decompile(script) return chunks.length === 1 && - bscript.isCanonicalSignature(chunks[0]) + bscript.isCanonicalScriptSignature(chunks[0]) } check.toJSON = function () { return 'pubKey input' } function encodeStack (signature) { - typeforce(bscript.isCanonicalSignature, signature) + typeforce(bscript.isCanonicalScriptSignature, signature) return [signature] } diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js index ae1d758..81e1f80 100644 --- a/src/templates/pubkeyhash/input.js +++ b/src/templates/pubkeyhash/input.js @@ -7,14 +7,14 @@ function check (script) { var chunks = bscript.decompile(script) return chunks.length === 2 && - bscript.isCanonicalSignature(chunks[0]) && + bscript.isCanonicalScriptSignature(chunks[0]) && bscript.isCanonicalPubKey(chunks[1]) } check.toJSON = function () { return 'pubKeyHash input' } function encodeStack (signature, pubKey) { typeforce({ - signature: bscript.isCanonicalSignature, + signature: bscript.isCanonicalScriptSignature, pubKey: bscript.isCanonicalPubKey }, { signature: signature, diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.js index d61c426..089c8d8 100644 --- a/src/templates/witnesspubkeyhash/input.js +++ b/src/templates/witnesspubkeyhash/input.js @@ -11,14 +11,14 @@ function check (script) { var chunks = bscript.decompile(script) return chunks.length === 2 && - bscript.isCanonicalSignature(chunks[0]) && + bscript.isCanonicalScriptSignature(chunks[0]) && isCompressedCanonicalPubKey(chunks[1]) } check.toJSON = function () { return 'witnessPubKeyHash input' } function encodeStack (signature, pubKey) { typeforce({ - signature: bscript.isCanonicalSignature, + signature: bscript.isCanonicalScriptSignature, pubKey: isCompressedCanonicalPubKey }, { signature: signature, diff --git a/test/script.js b/test/script.js index 5a51dbd..537e75a 100644 --- a/test/script.js +++ b/test/script.js @@ -19,7 +19,8 @@ describe('script', function () { } }) }) - describe.skip('isCanonicalSignature', function () {}) + describe.skip('isCanonicalScriptSignature', function () { + }) describe('fromASM/toASM', function () { fixtures.valid.forEach(function (f) { From 58f50e9f1cf2164e7557ac5f05c38742957bcbf4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 5 Jun 2018 18:27:33 +1000 Subject: [PATCH 036/568] tests: fix invalid pubKey used in testing --- test/transaction_builder.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index c5c7c1e..499db0c 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -303,7 +303,7 @@ describe('TransactionBuilder', function () { describe('sign', function () { it('supports the alternative abstract interface { publicKey, sign }', function () { var keyPair = { - publicKey: Buffer.alloc(33, 0x03), + publicKey: ECPair.makeRandom({ rng: function () { return Buffer.alloc(32, 1) } }).publicKey, sign: function (hash) { return Buffer.alloc(64, 0x5f) } } @@ -312,7 +312,7 @@ describe('TransactionBuilder', function () { txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) txb.addOutput('1111111111111111111114oLvT2', 100000) txb.sign(0, keyPair) - assert.equal(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121030303030303030303030303030303030303030303030303030303030303030303ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + assert.equal(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) fixtures.invalid.sign.forEach(function (f) { From bc7ca710edd655e2c66d5f9bfbcbbc9da5456559 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 21 Jun 2018 13:31:40 +1000 Subject: [PATCH 037/568] use blockchain.info for 3PBP address example --- test/integration/addresses.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index af2e06c..0d7f91b 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -115,21 +115,19 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can support the retrieval of transactions for an address (via 3PBP)', function (done) { - var keyPair = bitcoin.ECPair.makeRandom() - var address = getAddress(keyPair) + const keyPair = bitcoin.ECPair.makeRandom() + const address = getAddress(keyPair) dhttp({ - method: 'POST', - url: 'https://api.ei8ht.com.au/3/addrtxs', - body: { - addrs: [address], - height: 0 - } - }, function (err, transactions) { + method: 'GET', + url: 'https://blockchain.info/rawaddr/' + address + }, function (err, result) { if (err) return done(err) - // random private keys [probably] have no transactions - assert.strictEqual(Object.keys(transactions).length, 0) + // random private keys [probably!] have no transactions + assert.strictEqual(result.n_tx, 0) + assert.strictEqual(result.total_received, 0) + assert.strictEqual(result.total_sent, 0) done() }) }) From 4f089fceb4b8caacf7e778182dc618504328e8f1 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 21 Jun 2018 13:57:44 +1000 Subject: [PATCH 038/568] tests: use random keys to prevent db limitations --- test/integration/transactions.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 7f78ee8..55356f7 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -109,13 +109,13 @@ describe('bitcoinjs-lib (transactions)', function () { it('can create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input', function (done) { this.timeout(30000) - var keyPairs = [ - '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx', - '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT', - '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', - '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx9rcrL7' - ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) }) - var pubKeys = keyPairs.map(function (x) { return x.publicKey }) + const keyPairs = [ + bitcoin.ECPair.makeRandom({ network: regtest }), + bitcoin.ECPair.makeRandom({ network: regtest }), + bitcoin.ECPair.makeRandom({ network: regtest }), + bitcoin.ECPair.makeRandom({ network: regtest }) + ] + const pubKeys = keyPairs.map(function (x) { return x.publicKey }) var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) @@ -149,8 +149,8 @@ describe('bitcoinjs-lib (transactions)', function () { it('can create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input', function (done) { this.timeout(30000) - var keyPair = bitcoin.ECPair.fromWIF('cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA', regtest) - var pubKeyHash = bitcoin.crypto.hash160(keyPair.publicKey) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) + const pubKeyHash = bitcoin.crypto.hash160(keyPair.publicKey) var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash) var redeemScriptHash = bitcoin.crypto.hash160(redeemScript) @@ -184,13 +184,13 @@ describe('bitcoinjs-lib (transactions)', function () { it('can create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input', function (done) { this.timeout(50000) - var keyPairs = [ - 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA', - 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87K7XCyj5v', - 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87KcLPVfXz', - 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87L7FgDCKE' - ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) }) - var pubKeys = keyPairs.map(function (x) { return x.publicKey }) + const keyPairs = [ + bitcoin.ECPair.makeRandom({ network: regtest }), + bitcoin.ECPair.makeRandom({ network: regtest }), + bitcoin.ECPair.makeRandom({ network: regtest }), + bitcoin.ECPair.makeRandom({ network: regtest }) + ] + const pubKeys = keyPairs.map(function (x) { return x.publicKey }) var witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) var redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) From d23e6307372eba8442eb5b13519c74231de1f64e Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 16 Apr 2018 11:25:13 +1000 Subject: [PATCH 039/568] add CONTRIBUTING.md --- CONTRIBUTING.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 4 +-- 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8fc283f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# Contributing to bitcoinjs-lib + +Firstly in terms of structure, there is no particular concept of "bitcoinjs developers" in the sense of privileged people. +Open source often naturally revolves around meritocracy where longer term contributors gain more trust from the developer community. + +However, for practical purpose, there are repository "maintainers" who are responsible for merging pull requests. + +We are always accepting of pull requests, but we do adhere to specific standards in regards to coding style, test driven development and commit messages. + + +## Communication Channels +GitHub is the preferred method of communication between members. + +Otherwise, in order of preference: +* bitcoinjs.slack.com +* #bitcoinjs-dev on Freenode IRC + + +## Workflow +The codebase is maintained using the "contributor workflow" where everyone without exception contributes patch proposals using "pull requests". +This facilitates social contribution, easy testing and peer review. + +To contribute a patch, the workflow is as follows: + + 1. Fork repository + 1. Create topic branch + 1. Commit patches + 1. Push changes to your fork + 1. Submit a pull request to https://github.com/bitcoinjs/bitcoinjs-lib + +[Commits should be atomic](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) and diffs easy to read. + +If your pull request is accepted for merging, you may be asked by a maintainer to squash and or [rebase](https://git-scm.com/docs/git-rebase) your commits before it will be merged. + +Please refrain from creating several pull requests for the same change. + +Patchsets should be focused: + + * Adding a feature, or + * Fixing a bug, or + * Refactoring code. + +If you combine these, the PR may be rejected or asked to be split up. + +The length of time required for peer review is unpredictable and will vary from pull request to pull request. + +Refer to the [Git manual](https://git-scm.com/doc) for any information about `git`. + +## We adhere to Bitcoin-Core policy +- `bitcoinjs.template.*` functions should follow the script "templates" typically used in the community (and consequently, the bitcoin blockchain). + +This is a matter of community consensus, but is probably going to always be closely related to what bitcoin core does by default. +They are quite plainly just pattern matching functions, and should be treated as such. + +`bitcoinjs.script.decompile` is consensus bound only, it does not reject based on policy. +`bitcoinjs.script.compile` will adhere to bitcoin-core `IsStandard` policies rules. (eg. minimalpush in https://github.com/bitcoinjs/bitcoinjs-lib/pull/638) + +ECDSA `sign` operations will adhere to `IsStandard` policies such as `LOW_S`, but `verify` will not reject them. + +For rejecting non-standard `decoding`, you should use external modules to this library. + +**TLDR:** +Where "standards compliant" refers to the default policies of bitcoin-core, we adhere to the following: +- Any "creation" event must create standards-compliant data (standards bound) +- Any "validation" event must allow for non-standards compliant data (consensus bound) + +For stricter validation, use an external module which we [might have] provided. + + +## Release Policy +TODO diff --git a/README.md b/README.md index 14faf3d..77b4455 100644 --- a/README.md +++ b/README.md @@ -163,9 +163,7 @@ If you have a use case that you feel could be listed here, please [ask for it](h ## Contributing -We are always accepting of pull requests, but we do adhere to specific standards in regards to coding style, test driven development and commit messages. - -Please make your best effort to adhere to these when contributing to save on trivial corrections. +See [CONTRIBUTING.md](CONTRIBUTING.md). ### Running the test suite From 616c876c1c959baf29c242bb38feb3bbb8a20a69 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 21 Jun 2018 12:38:10 +1000 Subject: [PATCH 040/568] CONTRIB: add bitcoin/bitcoin text attribution and cleanup --- CONTRIBUTING.md | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8fc283f..efc9cae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,11 @@ + +[//]: # (This is partially derived from https://github.com/bitcoin/bitcoin/blob/6579d80572d2d33aceabbd3db45a6a9f809aa5e3/CONTRIBUTING.md) + # Contributing to bitcoinjs-lib +Firstly in terms of structure, there is no particular concept of "bitcoinjs developers" in a sense of privileged people. +Open source revolves around a meritocracy where contributors who help gain trust from the community. -Firstly in terms of structure, there is no particular concept of "bitcoinjs developers" in the sense of privileged people. -Open source often naturally revolves around meritocracy where longer term contributors gain more trust from the developer community. - -However, for practical purpose, there are repository "maintainers" who are responsible for merging pull requests. +For practical purpose, there are repository "maintainers" who are responsible for merging pull requests. We are always accepting of pull requests, but we do adhere to specific standards in regards to coding style, test driven development and commit messages. @@ -30,7 +32,7 @@ To contribute a patch, the workflow is as follows: [Commits should be atomic](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) and diffs easy to read. -If your pull request is accepted for merging, you may be asked by a maintainer to squash and or [rebase](https://git-scm.com/docs/git-rebase) your commits before it will be merged. +If your pull request is accepted for merging, you may be asked by a maintainer to squash and or [rebase](https://git-scm.com/docs/git-rebase) your commits before it is merged. Please refrain from creating several pull requests for the same change. @@ -46,26 +48,20 @@ The length of time required for peer review is unpredictable and will vary from Refer to the [Git manual](https://git-scm.com/doc) for any information about `git`. + ## We adhere to Bitcoin-Core policy -- `bitcoinjs.template.*` functions should follow the script "templates" typically used in the community (and consequently, the bitcoin blockchain). +Bitcoin script payment/script templates are based on community consensus, but typically adhere to bitcoin-core node policy by default. -This is a matter of community consensus, but is probably going to always be closely related to what bitcoin core does by default. -They are quite plainly just pattern matching functions, and should be treated as such. +- `bitcoinjs.script.decompile` is consensus bound only, it does not reject based on policy. +- `bitcoinjs.script.compile` will try to adhere to bitcoin-core `IsStandard` policies rules. (eg. minimalpush in https://github.com/bitcoinjs/bitcoinjs-lib/pull/638) -`bitcoinjs.script.decompile` is consensus bound only, it does not reject based on policy. -`bitcoinjs.script.compile` will adhere to bitcoin-core `IsStandard` policies rules. (eg. minimalpush in https://github.com/bitcoinjs/bitcoinjs-lib/pull/638) +Any elliptic curve `sign` operations should adhere to `IsStandard` policies, like `LOW_S`, but `verify` should not reject them [by default]. -ECDSA `sign` operations will adhere to `IsStandard` policies such as `LOW_S`, but `verify` will not reject them. +If you need non-standard rejecting `decoding`, you should use an external module, not this library. -For rejecting non-standard `decoding`, you should use external modules to this library. - -**TLDR:** +#### TLDR Where "standards compliant" refers to the default policies of bitcoin-core, we adhere to the following: - Any "creation" event must create standards-compliant data (standards bound) - Any "validation" event must allow for non-standards compliant data (consensus bound) -For stricter validation, use an external module which we [might have] provided. - - -## Release Policy -TODO +For stricter validation, use an external module which we [may have] provided. From b8a519224e9c4f67611869a926354a1a0f56cbef Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 21 Jun 2018 11:54:44 +1000 Subject: [PATCH 041/568] README: re-word top pre-amble --- README.md | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 77b4455..606ded7 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,32 @@ # BitcoinJS (bitcoinjs-lib) [](https://travis-ci.org/bitcoinjs/bitcoinjs-lib) [](https://www.npmjs.org/package/bitcoinjs-lib) -[](http://tip4commit.com/projects/735) [](https://github.com/feross/standard) -The pure JavaScript Bitcoin library for node.js and browsers. -Estimated to be in use by over 15 million wallet users and is the backbone for almost all Bitcoin web wallets in production today. - - -## Features -- Clean: Pure JavaScript, concise code, easy to read. -- Tested: Coverage > 90%, third-party integration tests. -- Careful: Two person approval process for small, focused pull requests. -- Compatible: Works on Node.js and all modern browsers. -- Powerful: Support for advanced features, such as multi-sig, HD Wallets. -- Secure: Strong random number generation, PGP signed releases, trusted developers. -- Principled: No support for browsers with crap RNG (IE < 11) -- Standardized: Node community coding style, Browserify, Node's stdlib and Buffers. -- Fast: Optimized code, uses typed arrays instead of byte arrays for performance. -- Experiment-friendly: Bitcoin Mainnet and Testnet support. -- Altcoin-ready: Capable of working with bitcoin-derived cryptocurrencies (such as Dogecoin). +A javascript Bitcoin library for node.js and browsers. +Released under the terms of the [MIT LICENSE](LICENSE). ## Should I use this in production? -If you are thinking of using the master branch of this library in production, **stop**. +If you are thinking of using the *master* branch of this library in production, **stop**. Master is not stable; it is our development branch, and [only tagged releases may be classified as stable](https://github.com/bitcoinjs/bitcoinjs-lib/tags). +## Can I trust this code? +> Don't trust. Verify. + +We recommend every user of this library and the [bitcoinjs](https://github.com/bitcoinjs) ecosystem to audit and verify any underlying code for its validity and suitability. + +Mistakes and bugs happen, but with your help in resolving and reporting [issues](https://github.com/bitcoinjs/bitcoinjs-lib/issues), together we can produce open source software library that is: + +- Easy to audit and verify, +- Tested, with test coverage >90%, +- Advanced and feature rich, +- Standardized, using [standard](http://github.com/standard/standard) and Node `Buffer`'s throughout, and +- Friendly, with a strong and helpful community, ready to answer questions. + + ## Installation ``` bash npm install bitcoinjs-lib From a85bea5a895f7cfabcd6c45559bd1ade1e903b39 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 21 Jun 2018 12:06:14 +1000 Subject: [PATCH 042/568] README: add Node LTS version hint, and warning about npm --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 606ded7..dc8fa0a 100644 --- a/README.md +++ b/README.md @@ -18,24 +18,29 @@ Master is not stable; it is our development branch, and [only tagged releases ma We recommend every user of this library and the [bitcoinjs](https://github.com/bitcoinjs) ecosystem to audit and verify any underlying code for its validity and suitability. -Mistakes and bugs happen, but with your help in resolving and reporting [issues](https://github.com/bitcoinjs/bitcoinjs-lib/issues), together we can produce open source software library that is: +Mistakes and bugs happen, but with your help in resolving and reporting [issues](https://github.com/bitcoinjs/bitcoinjs-lib/issues), together we can produce open source software that is: - Easy to audit and verify, -- Tested, with test coverage >90%, +- Tested, with test coverage >95%, - Advanced and feature rich, - Standardized, using [standard](http://github.com/standard/standard) and Node `Buffer`'s throughout, and - Friendly, with a strong and helpful community, ready to answer questions. - ## Installation ``` bash npm install bitcoinjs-lib ``` +Typically we support the [Node Maintenance LTS version](https://github.com/nodejs/Release). +If in doubt, see the [.travis.yml](.travis.yml) for what versions are used by our continuous integration tests. + +**WARNING**: We presently don't provide any tooling to verify that the release on `npm` matches GitHub. As such, you should verify anything downloaded by `npm` against your own verified copy. + + ## Setup ### Node.js ``` javascript -var bitcoin = require('bitcoinjs-lib') +const bitcoin = require('bitcoinjs-lib') ``` ### Browser From 4838b6b3c26fa25c1c439505e5469290fdd469aa Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 21 Jun 2018 12:14:21 +1000 Subject: [PATCH 043/568] README: rm browserify tutorial, add typescript clarifications --- README.md | 58 +++++++++++-------------------------------------------- 1 file changed, 11 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index dc8fa0a..fba551c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Master is not stable; it is our development branch, and [only tagged releases ma We recommend every user of this library and the [bitcoinjs](https://github.com/bitcoinjs) ecosystem to audit and verify any underlying code for its validity and suitability. -Mistakes and bugs happen, but with your help in resolving and reporting [issues](https://github.com/bitcoinjs/bitcoinjs-lib/issues), together we can produce open source software that is: +Mistakes and bugs happen, but with your help in resolving and reporting [issues](https://github.com/bitcoinjs/bitcoinjs-lib/issues), together we can produce open source software that is: - Easy to audit and verify, - Tested, with test coverage >95%, @@ -32,68 +32,32 @@ npm install bitcoinjs-lib ``` Typically we support the [Node Maintenance LTS version](https://github.com/nodejs/Release). -If in doubt, see the [.travis.yml](.travis.yml) for what versions are used by our continuous integration tests. +If in doubt, see the [.travis.yml](.travis.yml) for what versions are used by our continuous integration tests. **WARNING**: We presently don't provide any tooling to verify that the release on `npm` matches GitHub. As such, you should verify anything downloaded by `npm` against your own verified copy. -## Setup -### Node.js -``` javascript -const bitcoin = require('bitcoinjs-lib') -``` +## Usage ### Browser -If you're familiar with how to use browserify, ignore this and proceed normally. -These steps are advisory only, and may not be suitable for your application. +The recommended method of using `bitcoinjs-lib` in your browser is through [Browserify](https://github.com/substack/node-browserify). +If you're familiar with how to use browserify, ignore this and carry on, otherwise, it is recommended to read the tutorial at http://browserify.org/. -[Browserify](https://github.com/substack/node-browserify) is assumed to be installed for these steps. - -For your project, create an `index.js` file -``` javascript -let bitcoin = require('bitcoinjs-lib') - -// your code here -function myFunction () { - return bitcoin.ECPair.makeRandom().toWIF() -} - -module.exports = { - myFunction -} -``` - -Now, to compile for the browser: -``` bash -browserify index.js --standalone foo > app.js -``` - -You can now put `<script src="app.js" />` in your web page, using `foo.myFunction` to create a new Bitcoin private key. - -**NOTE**: If you uglify the javascript, you must exclude the following variable names from being mangled: `BigInteger`, `ECPair`, `Point`. -This is because of the function-name-duck-typing used in [typeforce](https://github.com/dcousens/typeforce). - -Example: -``` bash -uglifyjs ... --mangle reserved=['BigInteger','ECPair','Point'] -``` - -**NOTE**: This library tracks Node LTS features, if you need strict ES5, use [`--transform babelify`](https://github.com/babel/babelify) in conjunction with your `browserify` step (using an [`es2015`](http://babeljs.io/docs/plugins/preset-es2015/) preset). +**NOTE**: We use Node Maintenance LTS features, if you need strict ES5, use [`--transform babelify`](https://github.com/babel/babelify) in conjunction with your `browserify` step (using an [`es2015`](http://babeljs.io/docs/plugins/preset-es2015/) preset). **NOTE**: If you expect this library to run on an iOS 10 device, ensure that you are using [buffer@5.0.5](https://github.com/feross/buffer/pull/155) or greater. - ### Typescript or VSCode users -Type declarations for Typescript are available for version `^3.0.0` of the library. +Type declarations for Typescript [are available](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/0897921174860ec3d5318992d2323b3ae8100a68/types/bitcoinjs-lib) for version `^3.0.0` of the library. + ``` bash npm install @types/bitcoinjs-lib ``` -You can now use `bitcoinjs-lib` as a typescript compliant library. +For VSCode (and other editors), it is advised to install the type declarations, as Intellisense uses that information to help you code (autocompletion, static analysis). -For VSCode (and other editors), users are advised to install the type declarations, as Intellisense uses that information to help you code (autocompletion, static analysis). - -Report any typescript related bugs at [@dlebrecht DefinitelyTyped fork](https://github.com/dlebrecht/DefinitelyTyped), submit PRs to [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) +**WARNING**: These Typescript definitions are not maintained by the maintainers of this repository, and are instead maintained at [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). +Please report any issues or problems there. ### Flow From 64dd2be0ab4177e369ed56d71a777039749ce046 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 21 Jun 2018 12:18:52 +1000 Subject: [PATCH 044/568] README: update flow-type section --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fba551c..7ed1b62 100644 --- a/README.md +++ b/README.md @@ -61,14 +61,17 @@ Please report any issues or problems there. ### Flow -Definitions for [Flow typechecker](https://flowtype.org/) are available in flow-typed repository. +[Flow-type](https://flowtype.org/) definitions for are available in the [flow-*typed* repository](https://github.com/flowtype/flow-typed/tree/master/definitions/npm/bitcoinjs-lib_v2.x.x) for version `^2.0.0` of the library. -[You can either download them directly](https://github.com/flowtype/flow-typed/blob/master/definitions/npm/bitcoinjs-lib_v2.x.x/flow_v0.17.x-/bitcoinjs-lib_v2.x.x.js) from the repo, or with the flow-typed CLI +You can [download them directly](https://github.com/flowtype/flow-typed/blob/master/definitions/npm/bitcoinjs-lib_v2.x.x/flow_v0.17.x-/bitcoinjs-lib_v2.x.x.js), or using the flow-typed CLI: - # npm install -g flow-typed - $ flow-typed install -f 0.27 bitcoinjs-lib@2.2.0 # 0.27 for flow version, 2.2.0 for bitcoinjs-lib version +``` bash +npm install -g flow-typed +flow-typed install -f 0.27 bitcoinjs-lib@2.2.0 +``` + +These definitions are maintained by [@runn1ng](https://github.com/runn1ng). -The definitions are complete and up to date with version 2.2.0. The definitions are maintained by [@runn1ng](https://github.com/runn1ng). ## Examples The below examples are implemented as integration tests, they should be very easy to understand. From 67f59c4525c1ea1ec1ba2b67670130b01572f4f2 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 21 Jun 2018 12:19:39 +1000 Subject: [PATCH 045/568] README: rm projects utilizing, alternatives, and some grammar fixes --- README.md | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 7ed1b62..d2d500e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Master is not stable; it is our development branch, and [only tagged releases ma ## Can I trust this code? > Don't trust. Verify. -We recommend every user of this library and the [bitcoinjs](https://github.com/bitcoinjs) ecosystem to audit and verify any underlying code for its validity and suitability. +We recommend every user of this library and the [bitcoinjs](https://github.com/bitcoinjs) ecosystem audit and verify any underlying code for its validity and suitability. Mistakes and bugs happen, but with your help in resolving and reporting [issues](https://github.com/bitcoinjs/bitcoinjs-lib/issues), together we can produce open source software that is: @@ -26,6 +26,7 @@ Mistakes and bugs happen, but with your help in resolving and reporting [issues] - Standardized, using [standard](http://github.com/standard/standard) and Node `Buffer`'s throughout, and - Friendly, with a strong and helpful community, ready to answer questions. + ## Installation ``` bash npm install bitcoinjs-lib @@ -117,22 +118,6 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). If you have a use case that you feel could be listed here, please [ask for it](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new)! -## Projects utilizing BitcoinJS -- [BitAddress](https://www.bitaddress.org) -- [Blockchain.info](https://blockchain.info/wallet) -- [Blocktrail](https://www.blocktrail.com/) -- [Dark Wallet](https://www.darkwallet.is/) -- [DecentralBank](http://decentralbank.com/) -- [Dogechain Wallet](https://dogechain.info) -- [EI8HT Wallet](http://ei8.ht/) -- [GreenAddress](https://greenaddress.it) -- [Helperbit](https://helperbit.com) -- [Melis Wallet](https://melis.io) -- [Robocoin](https://wallet.robocoin.com) -- [Skyhook ATM](http://projectskyhook.com) -- [Coinbase Multisig tool](https://github.com/coinbase/multisig-tool) - - ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md). From 93b1ae430361185d66142fa21b93d740979ead4a Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 25 Jun 2018 16:24:37 +1000 Subject: [PATCH 046/568] sed -i 's/^let /const /' --- src/ecpair.js | 14 +++++++------- src/index.js | 4 ++-- src/script.js | 2 +- src/script_signature.js | 10 +++++----- test/address.js | 10 +++++----- test/ecpair.js | 24 ++++++++++++------------ test/integration/_regtest.js | 4 ++-- test/integration/addresses.js | 12 ++++++------ test/integration/bip32.js | 12 ++++++------ test/integration/csv.js | 14 +++++++------- test/integration/stealth.js | 10 +++++----- test/integration/transactions.js | 12 ++++++------ test/transaction_builder.js | 22 +++++++++++----------- 13 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index d0653be..2b62545 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -1,14 +1,14 @@ -let ecc = require('tiny-secp256k1') -let randomBytes = require('randombytes') -let typeforce = require('typeforce') -let types = require('./types') -let wif = require('wif') +const ecc = require('tiny-secp256k1') +const randomBytes = require('randombytes') +const typeforce = require('typeforce') +const types = require('./types') +const wif = require('wif') -let NETWORKS = require('./networks') +const NETWORKS = require('./networks') // TODO: why is the function name toJSON weird? function isPoint (x) { return ecc.isPoint(x) } -let isOptions = typeforce.maybe(typeforce.compile({ +const isOptions = typeforce.maybe(typeforce.compile({ compressed: types.maybe(types.Boolean), network: types.maybe(types.Network) })) diff --git a/src/index.js b/src/index.js index e1876f0..3630bb8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ -let script = require('./script') -let templates = require('./templates') +const script = require('./script') +const templates = require('./templates') for (let key in templates) { script[key] = templates[key] } diff --git a/src/script.js b/src/script.js index 290ba00..612ce9d 100644 --- a/src/script.js +++ b/src/script.js @@ -1,6 +1,6 @@ var Buffer = require('safe-buffer').Buffer var bip66 = require('bip66') -let ecc = require('tiny-secp256k1') +const ecc = require('tiny-secp256k1') var pushdata = require('pushdata-bitcoin') var typeforce = require('typeforce') var types = require('./types') diff --git a/src/script_signature.js b/src/script_signature.js index 2d915d4..56f6380 100644 --- a/src/script_signature.js +++ b/src/script_signature.js @@ -1,9 +1,9 @@ -let bip66 = require('bip66') -let Buffer = require('safe-buffer').Buffer -let typeforce = require('typeforce') -let types = require('./types') +const bip66 = require('bip66') +const Buffer = require('safe-buffer').Buffer +const typeforce = require('typeforce') +const types = require('./types') -let ZERO = Buffer.alloc(1, 0) +const ZERO = Buffer.alloc(1, 0) function toDER (x) { let i = 0 while (x[i] === 0) ++i diff --git a/test/address.js b/test/address.js index a9d7f9e..7bc87f0 100644 --- a/test/address.js +++ b/test/address.js @@ -1,10 +1,10 @@ /* global describe, it */ -let assert = require('assert') -let baddress = require('../src/address') -let bscript = require('../src/script') -let fixtures = require('./fixtures/address.json') -let NETWORKS = Object.assign({ +const assert = require('assert') +const baddress = require('../src/address') +const bscript = require('../src/script') +const fixtures = require('./fixtures/address.json') +const NETWORKS = Object.assign({ litecoin: { messagePrefix: '\x19Litecoin Signed Message:\n', bip32: { diff --git a/test/ecpair.js b/test/ecpair.js index 5e2ea59..f873651 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -1,25 +1,25 @@ /* global describe, it, beforeEach */ /* eslint-disable no-new */ -let assert = require('assert') -let proxyquire = require('proxyquire') -let hoodwink = require('hoodwink') +const assert = require('assert') +const proxyquire = require('proxyquire') +const hoodwink = require('hoodwink') -let ECPair = require('../src/ecpair') -let tinysecp = require('tiny-secp256k1') +const ECPair = require('../src/ecpair') +const tinysecp = require('tiny-secp256k1') -let fixtures = require('./fixtures/ecpair.json') +const fixtures = require('./fixtures/ecpair.json') -let NETWORKS = require('../src/networks') -let NETWORKS_LIST = [] // Object.values(NETWORKS) +const NETWORKS = require('../src/networks') +const NETWORKS_LIST = [] // Object.values(NETWORKS) for (let networkName in NETWORKS) { NETWORKS_LIST.push(NETWORKS[networkName]) } -let ZERO = Buffer.alloc(32, 0) -let ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') -let GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 'hex') -let GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 'hex') +const ZERO = Buffer.alloc(32, 0) +const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') +const GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 'hex') +const GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 'hex') describe('ECPair', function () { describe('constructor', function () { diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 346fa41..409f908 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -68,8 +68,8 @@ function verify (txo, callback) { } // TODO: remove -let baddress = bitcoin.address -let bcrypto = bitcoin.crypto +const baddress = bitcoin.address +const bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 0d7f91b..a38b5cf 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -1,10 +1,10 @@ /* global describe, it */ -let assert = require('assert') -let bitcoin = require('../../') -let dhttp = require('dhttp/200') +const assert = require('assert') +const bitcoin = require('../../') +const dhttp = require('dhttp/200') -let LITECOIN = { +const LITECOIN = { messagePrefix: '\x19Litecoin Signed Message:\n', bip32: { public: 0x019da462, @@ -19,8 +19,8 @@ let LITECOIN = { function rng () { return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } // TODO: remove -let baddress = bitcoin.address -let bcrypto = bitcoin.crypto +const baddress = bitcoin.address +const bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) diff --git a/test/integration/bip32.js b/test/integration/bip32.js index effdec1..dc3591a 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -1,13 +1,13 @@ /* global describe, it */ -let assert = require('assert') -let bip32 = require('bip32') -let bip39 = require('bip39') -let bitcoin = require('../../') +const assert = require('assert') +const bip32 = require('bip32') +const bip39 = require('bip39') +const bitcoin = require('../../') // TODO: remove -let baddress = bitcoin.address -let bcrypto = bitcoin.crypto +const baddress = bitcoin.address +const bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) diff --git a/test/integration/csv.js b/test/integration/csv.js index adb28b5..8413959 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -1,13 +1,13 @@ /* global describe, it, before */ -let assert = require('assert') -let bitcoin = require('../../') -let regtestUtils = require('./_regtest') -let regtest = regtestUtils.network -let bip68 = require('bip68') +const assert = require('assert') +const bitcoin = require('../../') +const regtestUtils = require('./_regtest') +const regtest = regtestUtils.network +const bip68 = require('bip68') -let alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) -let bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) +const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) +const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) describe('bitcoinjs-lib (transactions w/ CSV)', function () { // force update MTP diff --git a/test/integration/stealth.js b/test/integration/stealth.js index 90acb7b..f6d81e6 100644 --- a/test/integration/stealth.js +++ b/test/integration/stealth.js @@ -1,12 +1,12 @@ /* global describe, it */ -let assert = require('assert') -let bitcoin = require('../../') -let ecc = require('tiny-secp256k1') +const assert = require('assert') +const bitcoin = require('../../') +const ecc = require('tiny-secp256k1') // TODO: remove -let baddress = bitcoin.address -let bcrypto = bitcoin.crypto +const baddress = bitcoin.address +const bcrypto = bitcoin.crypto function getAddress (node) { return baddress.toBase58Check(bcrypto.hash160(node.publicKey), bitcoin.networks.bitcoin.pubKeyHash) } diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 55356f7..1e796a5 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -1,13 +1,13 @@ /* global describe, it */ -let assert = require('assert') -let bitcoin = require('../../') -let regtestUtils = require('./_regtest') -let regtest = regtestUtils.network +const assert = require('assert') +const bitcoin = require('../../') +const regtestUtils = require('./_regtest') +const regtest = regtestUtils.network // TODO: remove -let baddress = bitcoin.address -let bcrypto = bitcoin.crypto +const baddress = bitcoin.address +const bcrypto = bitcoin.crypto function getAddress (node, network) { network = network || bitcoin.networks.bitcoin return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 499db0c..cfb6671 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -1,18 +1,18 @@ /* global describe, it, beforeEach */ -let assert = require('assert') -let baddress = require('../src/address') -let bcrypto = require('../src/crypto') -let bscript = require('../src/script') -let btemplates = require('../src/templates') -let ops = require('bitcoin-ops') +const assert = require('assert') +const baddress = require('../src/address') +const bcrypto = require('../src/crypto') +const bscript = require('../src/script') +const btemplates = require('../src/templates') +const ops = require('bitcoin-ops') -let ECPair = require('../src/ecpair') -let Transaction = require('../src/transaction') -let TransactionBuilder = require('../src/transaction_builder') -let NETWORKS = require('../src/networks') +const ECPair = require('../src/ecpair') +const Transaction = require('../src/transaction') +const TransactionBuilder = require('../src/transaction_builder') +const NETWORKS = require('../src/networks') -let fixtures = require('./fixtures/transaction_builder') +const fixtures = require('./fixtures/transaction_builder') // TODO: remove function getAddress (node) { From 91b8823aa89ee9afa9c3f4b0dfe60a7ec055f599 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 25 Jun 2018 16:25:12 +1000 Subject: [PATCH 047/568] sed -i 's/^var /const /' --- src/address.js | 16 ++++++------- src/block.js | 14 ++++++------ src/crypto.js | 2 +- src/script.js | 18 +++++++-------- src/script_number.js | 2 +- src/templates/index.js | 20 ++++++++-------- src/templates/multisig/input.js | 12 +++++----- src/templates/multisig/output.js | 10 ++++---- src/templates/nulldata.js | 8 +++---- src/templates/pubkey/input.js | 4 ++-- src/templates/pubkey/output.js | 6 ++--- src/templates/pubkeyhash/input.js | 4 ++-- src/templates/pubkeyhash/output.js | 8 +++---- src/templates/scripthash/input.js | 16 ++++++------- src/templates/scripthash/output.js | 8 +++---- src/templates/witnesscommitment/output.js | 12 +++++----- src/templates/witnesspubkeyhash/input.js | 4 ++-- src/templates/witnesspubkeyhash/output.js | 8 +++---- src/templates/witnessscripthash/input.js | 12 +++++----- src/templates/witnessscripthash/output.js | 8 +++---- src/transaction.js | 28 +++++++++++------------ src/transaction_builder.js | 28 +++++++++++------------ src/types.js | 12 +++++----- test/bitcoin.core.js | 22 +++++++++--------- test/block.js | 6 ++--- test/bufferutils.js | 6 ++--- test/crypto.js | 6 ++--- test/integration/_regtest.js | 10 ++++---- test/integration/blocks.js | 4 ++-- test/integration/cltv.js | 14 ++++++------ test/integration/crypto.js | 16 ++++++------- test/script.js | 10 ++++---- test/script_number.js | 6 ++--- test/script_signature.js | 8 +++---- test/templates.js | 12 +++++----- test/transaction.js | 8 +++---- test/types.js | 6 ++--- 37 files changed, 197 insertions(+), 197 deletions(-) diff --git a/src/address.js b/src/address.js index 221f85a..949e389 100644 --- a/src/address.js +++ b/src/address.js @@ -1,11 +1,11 @@ -var Buffer = require('safe-buffer').Buffer -var bech32 = require('bech32') -var bs58check = require('bs58check') -var bscript = require('./script') -var btemplates = require('./templates') -var networks = require('./networks') -var typeforce = require('typeforce') -var types = require('./types') +const Buffer = require('safe-buffer').Buffer +const bech32 = require('bech32') +const bs58check = require('bs58check') +const bscript = require('./script') +const btemplates = require('./templates') +const networks = require('./networks') +const typeforce = require('typeforce') +const types = require('./types') function fromBase58Check (address) { var payload = bs58check.decode(address) diff --git a/src/block.js b/src/block.js index c545996..0d4a585 100644 --- a/src/block.js +++ b/src/block.js @@ -1,11 +1,11 @@ -var Buffer = require('safe-buffer').Buffer -var bcrypto = require('./crypto') -var fastMerkleRoot = require('merkle-lib/fastRoot') -var typeforce = require('typeforce') -var types = require('./types') -var varuint = require('varuint-bitcoin') +const Buffer = require('safe-buffer').Buffer +const bcrypto = require('./crypto') +const fastMerkleRoot = require('merkle-lib/fastRoot') +const typeforce = require('typeforce') +const types = require('./types') +const varuint = require('varuint-bitcoin') -var Transaction = require('./transaction') +const Transaction = require('./transaction') function Block () { this.version = 1 diff --git a/src/crypto.js b/src/crypto.js index 1bb39f1..a11d061 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -1,4 +1,4 @@ -var createHash = require('create-hash') +const createHash = require('create-hash') function ripemd160 (buffer) { return createHash('rmd160').update(buffer).digest() diff --git a/src/script.js b/src/script.js index 612ce9d..63bf75d 100644 --- a/src/script.js +++ b/src/script.js @@ -1,14 +1,14 @@ -var Buffer = require('safe-buffer').Buffer -var bip66 = require('bip66') +const Buffer = require('safe-buffer').Buffer +const bip66 = require('bip66') const ecc = require('tiny-secp256k1') -var pushdata = require('pushdata-bitcoin') -var typeforce = require('typeforce') -var types = require('./types') -var scriptNumber = require('./script_number') +const pushdata = require('pushdata-bitcoin') +const typeforce = require('typeforce') +const types = require('./types') +const scriptNumber = require('./script_number') -var OPS = require('bitcoin-ops') -var REVERSE_OPS = require('bitcoin-ops/map') -var OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 +const OPS = require('bitcoin-ops') +const REVERSE_OPS = require('bitcoin-ops/map') +const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 function isOPInt (value) { return types.Number(value) && diff --git a/src/script_number.js b/src/script_number.js index 3fd6326..6257690 100644 --- a/src/script_number.js +++ b/src/script_number.js @@ -1,4 +1,4 @@ -var Buffer = require('safe-buffer').Buffer +const Buffer = require('safe-buffer').Buffer function decode (buffer, maxLength, minimal) { maxLength = maxLength || 4 diff --git a/src/templates/index.js b/src/templates/index.js index af09112..4d7ce69 100644 --- a/src/templates/index.js +++ b/src/templates/index.js @@ -1,14 +1,14 @@ -var decompile = require('../script').decompile -var multisig = require('./multisig') -var nullData = require('./nulldata') -var pubKey = require('./pubkey') -var pubKeyHash = require('./pubkeyhash') -var scriptHash = require('./scripthash') -var witnessPubKeyHash = require('./witnesspubkeyhash') -var witnessScriptHash = require('./witnessscripthash') -var witnessCommitment = require('./witnesscommitment') +const decompile = require('../script').decompile +const multisig = require('./multisig') +const nullData = require('./nulldata') +const pubKey = require('./pubkey') +const pubKeyHash = require('./pubkeyhash') +const scriptHash = require('./scripthash') +const witnessPubKeyHash = require('./witnesspubkeyhash') +const witnessScriptHash = require('./witnessscripthash') +const witnessCommitment = require('./witnesscommitment') -var types = { +const types = { MULTISIG: 'multisig', NONSTANDARD: 'nonstandard', NULLDATA: 'nulldata', diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js index 0ba0a06..54f2a9f 100644 --- a/src/templates/multisig/input.js +++ b/src/templates/multisig/input.js @@ -1,10 +1,10 @@ // OP_0 [signatures ...] -var Buffer = require('safe-buffer').Buffer -var bscript = require('../../script') -var p2mso = require('./output') -var typeforce = require('typeforce') -var OPS = require('bitcoin-ops') +const Buffer = require('safe-buffer').Buffer +const bscript = require('../../script') +const p2mso = require('./output') +const typeforce = require('typeforce') +const OPS = require('bitcoin-ops') function partialSignature (value) { return value === OPS.OP_0 || bscript.isCanonicalScriptSignature(value) @@ -23,7 +23,7 @@ function check (script, allowIncomplete) { } check.toJSON = function () { return 'multisig input' } -var EMPTY_BUFFER = Buffer.allocUnsafe(0) +const EMPTY_BUFFER = Buffer.allocUnsafe(0) function encodeStack (signatures, scriptPubKey) { typeforce([partialSignature], signatures) diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.js index 70e8488..baef7e7 100644 --- a/src/templates/multisig/output.js +++ b/src/templates/multisig/output.js @@ -1,10 +1,10 @@ // m [pubKeys ...] n OP_CHECKMULTISIG -var bscript = require('../../script') -var types = require('../../types') -var typeforce = require('typeforce') -var OPS = require('bitcoin-ops') -var OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 +const bscript = require('../../script') +const types = require('../../types') +const typeforce = require('typeforce') +const OPS = require('bitcoin-ops') +const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 function check (script, allowIncomplete) { var chunks = bscript.decompile(script) diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js index b7d53f8..1b9c404 100644 --- a/src/templates/nulldata.js +++ b/src/templates/nulldata.js @@ -1,9 +1,9 @@ // OP_RETURN {data} -var bscript = require('../script') -var types = require('../types') -var typeforce = require('typeforce') -var OPS = require('bitcoin-ops') +const bscript = require('../script') +const types = require('../types') +const typeforce = require('typeforce') +const OPS = require('bitcoin-ops') function check (script) { var buffer = bscript.compile(script) diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js index 9149289..e4cb931 100644 --- a/src/templates/pubkey/input.js +++ b/src/templates/pubkey/input.js @@ -1,7 +1,7 @@ // {signature} -var bscript = require('../../script') -var typeforce = require('typeforce') +const bscript = require('../../script') +const typeforce = require('typeforce') function check (script) { var chunks = bscript.decompile(script) diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.js index 69a747f..f8bff2f 100644 --- a/src/templates/pubkey/output.js +++ b/src/templates/pubkey/output.js @@ -1,8 +1,8 @@ // {pubKey} OP_CHECKSIG -var bscript = require('../../script') -var typeforce = require('typeforce') -var OPS = require('bitcoin-ops') +const bscript = require('../../script') +const typeforce = require('typeforce') +const OPS = require('bitcoin-ops') function check (script) { var chunks = bscript.decompile(script) diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js index 81e1f80..0d5497b 100644 --- a/src/templates/pubkeyhash/input.js +++ b/src/templates/pubkeyhash/input.js @@ -1,7 +1,7 @@ // {signature} {pubKey} -var bscript = require('../../script') -var typeforce = require('typeforce') +const bscript = require('../../script') +const typeforce = require('typeforce') function check (script) { var chunks = bscript.decompile(script) diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.js index 2a7c4ab..c89685b 100644 --- a/src/templates/pubkeyhash/output.js +++ b/src/templates/pubkeyhash/output.js @@ -1,9 +1,9 @@ // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG -var bscript = require('../../script') -var types = require('../../types') -var typeforce = require('typeforce') -var OPS = require('bitcoin-ops') +const bscript = require('../../script') +const types = require('../../types') +const typeforce = require('typeforce') +const OPS = require('bitcoin-ops') function check (script) { var buffer = bscript.compile(script) diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js index 2ce55e3..a2d0693 100644 --- a/src/templates/scripthash/input.js +++ b/src/templates/scripthash/input.js @@ -1,14 +1,14 @@ // <scriptSig> {serialized scriptPubKey script} -var Buffer = require('safe-buffer').Buffer -var bscript = require('../../script') -var typeforce = require('typeforce') +const Buffer = require('safe-buffer').Buffer +const bscript = require('../../script') +const typeforce = require('typeforce') -var p2ms = require('../multisig/') -var p2pk = require('../pubkey/') -var p2pkh = require('../pubkeyhash/') -var p2wpkho = require('../witnesspubkeyhash/output') -var p2wsho = require('../witnessscripthash/output') +const p2ms = require('../multisig/') +const p2pk = require('../pubkey/') +const p2pkh = require('../pubkeyhash/') +const p2wpkho = require('../witnesspubkeyhash/output') +const p2wsho = require('../witnessscripthash/output') function check (script, allowIncomplete) { var chunks = bscript.decompile(script) diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.js index 83d2501..d9776da 100644 --- a/src/templates/scripthash/output.js +++ b/src/templates/scripthash/output.js @@ -1,9 +1,9 @@ // OP_HASH160 {scriptHash} OP_EQUAL -var bscript = require('../../script') -var types = require('../../types') -var typeforce = require('typeforce') -var OPS = require('bitcoin-ops') +const bscript = require('../../script') +const types = require('../../types') +const typeforce = require('typeforce') +const OPS = require('bitcoin-ops') function check (script) { var buffer = bscript.compile(script) diff --git a/src/templates/witnesscommitment/output.js b/src/templates/witnesscommitment/output.js index 3984ea5..5f97d69 100644 --- a/src/templates/witnesscommitment/output.js +++ b/src/templates/witnesscommitment/output.js @@ -1,12 +1,12 @@ // OP_RETURN {aa21a9ed} {commitment} -var Buffer = require('safe-buffer').Buffer -var bscript = require('../../script') -var types = require('../../types') -var typeforce = require('typeforce') -var OPS = require('bitcoin-ops') +const Buffer = require('safe-buffer').Buffer +const bscript = require('../../script') +const types = require('../../types') +const typeforce = require('typeforce') +const OPS = require('bitcoin-ops') -var HEADER = Buffer.from('aa21a9ed', 'hex') +const HEADER = Buffer.from('aa21a9ed', 'hex') function check (script) { var buffer = bscript.compile(script) diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.js index 089c8d8..a0549ff 100644 --- a/src/templates/witnesspubkeyhash/input.js +++ b/src/templates/witnesspubkeyhash/input.js @@ -1,7 +1,7 @@ // {signature} {pubKey} -var bscript = require('../../script') -var typeforce = require('typeforce') +const bscript = require('../../script') +const typeforce = require('typeforce') function isCompressedCanonicalPubKey (pubKey) { return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33 diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.js index ebcb185..d2b091b 100644 --- a/src/templates/witnesspubkeyhash/output.js +++ b/src/templates/witnesspubkeyhash/output.js @@ -1,9 +1,9 @@ // OP_0 {pubKeyHash} -var bscript = require('../../script') -var types = require('../../types') -var typeforce = require('typeforce') -var OPS = require('bitcoin-ops') +const bscript = require('../../script') +const types = require('../../types') +const typeforce = require('typeforce') +const OPS = require('bitcoin-ops') function check (script) { var buffer = bscript.compile(script) diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js index d6e85f5..c21ee1e 100644 --- a/src/templates/witnessscripthash/input.js +++ b/src/templates/witnessscripthash/input.js @@ -1,12 +1,12 @@ // <scriptSig> {serialized scriptPubKey script} -var bscript = require('../../script') -var types = require('../../types') -var typeforce = require('typeforce') +const bscript = require('../../script') +const types = require('../../types') +const typeforce = require('typeforce') -var p2ms = require('../multisig/') -var p2pk = require('../pubkey/') -var p2pkh = require('../pubkeyhash/') +const p2ms = require('../multisig/') +const p2pk = require('../pubkey/') +const p2pkh = require('../pubkeyhash/') function check (chunks, allowIncomplete) { typeforce(types.Array, chunks) diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.js index f1656b5..d800fda 100644 --- a/src/templates/witnessscripthash/output.js +++ b/src/templates/witnessscripthash/output.js @@ -1,9 +1,9 @@ // OP_0 {scriptHash} -var bscript = require('../../script') -var types = require('../../types') -var typeforce = require('typeforce') -var OPS = require('bitcoin-ops') +const bscript = require('../../script') +const types = require('../../types') +const typeforce = require('typeforce') +const OPS = require('bitcoin-ops') function check (script) { var buffer = bscript.compile(script) diff --git a/src/transaction.js b/src/transaction.js index 86cccea..8187c73 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,11 +1,11 @@ -var Buffer = require('safe-buffer').Buffer -var bcrypto = require('./crypto') -var bscript = require('./script') -var bufferutils = require('./bufferutils') -var opcodes = require('bitcoin-ops') -var typeforce = require('typeforce') -var types = require('./types') -var varuint = require('varuint-bitcoin') +const Buffer = require('safe-buffer').Buffer +const bcrypto = require('./crypto') +const bscript = require('./script') +const bufferutils = require('./bufferutils') +const opcodes = require('bitcoin-ops') +const typeforce = require('typeforce') +const types = require('./types') +const varuint = require('varuint-bitcoin') function varSliceSize (someScript) { var length = someScript.length @@ -36,12 +36,12 @@ Transaction.SIGHASH_ANYONECANPAY = 0x80 Transaction.ADVANCED_TRANSACTION_MARKER = 0x00 Transaction.ADVANCED_TRANSACTION_FLAG = 0x01 -var EMPTY_SCRIPT = Buffer.allocUnsafe(0) -var EMPTY_WITNESS = [] -var ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') -var ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') -var VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex') -var BLANK_OUTPUT = { +const EMPTY_SCRIPT = Buffer.allocUnsafe(0) +const EMPTY_WITNESS = [] +const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') +const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') +const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex') +const BLANK_OUTPUT = { script: EMPTY_SCRIPT, valueBuffer: VALUE_UINT64_MAX } diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 4f731dc..d5ad34c 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -1,18 +1,18 @@ -var Buffer = require('safe-buffer').Buffer -var baddress = require('./address') -var bcrypto = require('./crypto') -var bscript = require('./script') -var btemplates = require('./templates') -var networks = require('./networks') -var ops = require('bitcoin-ops') -var typeforce = require('typeforce') -var types = require('./types') -var scriptTypes = btemplates.types -var SIGNABLE = [btemplates.types.P2PKH, btemplates.types.P2PK, btemplates.types.MULTISIG] -var P2SH = SIGNABLE.concat([btemplates.types.P2WPKH, btemplates.types.P2WSH]) +const Buffer = require('safe-buffer').Buffer +const baddress = require('./address') +const bcrypto = require('./crypto') +const bscript = require('./script') +const btemplates = require('./templates') +const networks = require('./networks') +const ops = require('bitcoin-ops') +const typeforce = require('typeforce') +const types = require('./types') +const scriptTypes = btemplates.types +const SIGNABLE = [btemplates.types.P2PKH, btemplates.types.P2PK, btemplates.types.MULTISIG] +const P2SH = SIGNABLE.concat([btemplates.types.P2WPKH, btemplates.types.P2WSH]) -var ECPair = require('./ecpair') -var Transaction = require('./transaction') +const ECPair = require('./ecpair') +const Transaction = require('./transaction') function supportedType (type) { return SIGNABLE.indexOf(type) !== -1 diff --git a/src/types.js b/src/types.js index 302043f..2d1ec6f 100644 --- a/src/types.js +++ b/src/types.js @@ -1,6 +1,6 @@ -var typeforce = require('typeforce') +const typeforce = require('typeforce') -var UINT31_MAX = Math.pow(2, 31) - 1 +const UINT31_MAX = Math.pow(2, 31) - 1 function UInt31 (value) { return typeforce.UInt32(value) && value <= UINT31_MAX } @@ -10,16 +10,16 @@ function BIP32Path (value) { } BIP32Path.toJSON = function () { return 'BIP32 derivation path' } -var SATOSHI_MAX = 21 * 1e14 +const SATOSHI_MAX = 21 * 1e14 function Satoshi (value) { return typeforce.UInt53(value) && value <= SATOSHI_MAX } // external dependent types -var ECPoint = typeforce.quacksLike('Point') +const ECPoint = typeforce.quacksLike('Point') // exposed, external API -var Network = typeforce.compile({ +const Network = typeforce.compile({ messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), bip32: { public: typeforce.UInt32, @@ -31,7 +31,7 @@ var Network = typeforce.compile({ }) // extend typeforce types with ours -var types = { +const types = { BIP32Path: BIP32Path, Buffer256bit: typeforce.BufferN(32), ECPoint: ECPoint, diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index 4d73c2a..bda6899 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -1,17 +1,17 @@ /* global describe, it */ -var assert = require('assert') -var base58 = require('bs58') -var bitcoin = require('../') +const assert = require('assert') +const base58 = require('bs58') +const bitcoin = require('../') -var base58EncodeDecode = require('./fixtures/core/base58_encode_decode.json') -var base58KeysInvalid = require('./fixtures/core/base58_keys_invalid.json') -var base58KeysValid = require('./fixtures/core/base58_keys_valid.json') -var blocksValid = require('./fixtures/core/blocks.json') -var sigCanonical = require('./fixtures/core/sig_canonical.json') -var sigHash = require('./fixtures/core/sighash.json') -var sigNoncanonical = require('./fixtures/core/sig_noncanonical.json') -var txValid = require('./fixtures/core/tx_valid.json') +const base58EncodeDecode = require('./fixtures/core/base58_encode_decode.json') +const base58KeysInvalid = require('./fixtures/core/base58_keys_invalid.json') +const base58KeysValid = require('./fixtures/core/base58_keys_valid.json') +const blocksValid = require('./fixtures/core/blocks.json') +const sigCanonical = require('./fixtures/core/sig_canonical.json') +const sigHash = require('./fixtures/core/sighash.json') +const sigNoncanonical = require('./fixtures/core/sig_noncanonical.json') +const txValid = require('./fixtures/core/tx_valid.json') describe('Bitcoin-core', function () { // base58EncodeDecode diff --git a/test/block.js b/test/block.js index 19866e5..5794d66 100644 --- a/test/block.js +++ b/test/block.js @@ -1,9 +1,9 @@ /* global describe, it, beforeEach */ -var assert = require('assert') -var Block = require('../src/block') +const assert = require('assert') +const Block = require('../src/block') -var fixtures = require('./fixtures/block') +const fixtures = require('./fixtures/block') describe('Block', function () { describe('version', function () { diff --git a/test/bufferutils.js b/test/bufferutils.js index e172ba1..da98828 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -1,9 +1,9 @@ /* global describe, it */ -var assert = require('assert') -var bufferutils = require('../src/bufferutils') +const assert = require('assert') +const bufferutils = require('../src/bufferutils') -var fixtures = require('./fixtures/bufferutils.json') +const fixtures = require('./fixtures/bufferutils.json') describe('bufferutils', function () { describe('readUInt64LE', function () { diff --git a/test/crypto.js b/test/crypto.js index 0fcc24b..91bc674 100644 --- a/test/crypto.js +++ b/test/crypto.js @@ -1,9 +1,9 @@ /* global describe, it */ -var assert = require('assert') -var bcrypto = require('../src/crypto') +const assert = require('assert') +const bcrypto = require('../src/crypto') -var fixtures = require('./fixtures/crypto') +const fixtures = require('./fixtures/crypto') describe('crypto', function () { ['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(function (algorithm) { diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 409f908..91b25d5 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -1,9 +1,9 @@ -var assert = require('assert') -var bitcoin = require('../../') -var dhttp = require('dhttp/200') +const assert = require('assert') +const bitcoin = require('../../') +const dhttp = require('dhttp/200') -var APIPASS = process.env.APIPASS || 'satoshi' -var APIURL = 'https://api.dcousens.cloud/1' +const APIPASS = process.env.APIPASS || 'satoshi' +const APIURL = 'https://api.dcousens.cloud/1' function broadcast (txHex, callback) { dhttp({ diff --git a/test/integration/blocks.js b/test/integration/blocks.js index 56eca68..d4a7ec4 100644 --- a/test/integration/blocks.js +++ b/test/integration/blocks.js @@ -1,8 +1,8 @@ /* global describe, it */ 'use strict' -var assert = require('assert') -var bitcoin = require('../../') +const assert = require('assert') +const bitcoin = require('../../') describe('bitcoinjs-lib (blocks)', function () { it('can extract a height from a CoinBase transaction', function () { diff --git a/test/integration/cltv.js b/test/integration/cltv.js index b7c6c2c..90e78a9 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -1,13 +1,13 @@ /* global describe, it, before */ -var assert = require('assert') -var bitcoin = require('../../') -var regtestUtils = require('./_regtest') -var regtest = regtestUtils.network -var bip65 = require('bip65') +const assert = require('assert') +const bitcoin = require('../../') +const regtestUtils = require('./_regtest') +const regtest = regtestUtils.network +const bip65 = require('bip65') -var alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) -var bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) +const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) +const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // force update MTP diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 58ae431..deddf67 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -1,14 +1,14 @@ /* global describe, it */ -var assert = require('assert') -var bigi = require('bigi') -var bitcoin = require('../../') -var bip32 = require('bip32') -var crypto = require('crypto') -var tinysecp = require('tiny-secp256k1') +const assert = require('assert') +const bigi = require('bigi') +const bitcoin = require('../../') +const bip32 = require('bip32') +const crypto = require('crypto') +const tinysecp = require('tiny-secp256k1') -var ecurve = require('ecurve') -var secp256k1 = ecurve.getCurveByName('secp256k1') +const ecurve = require('ecurve') +const secp256k1 = ecurve.getCurveByName('secp256k1') describe('bitcoinjs-lib (crypto)', function () { it('can recover a private key from duplicate R values', function () { diff --git a/test/script.js b/test/script.js index 537e75a..71c9d72 100644 --- a/test/script.js +++ b/test/script.js @@ -1,11 +1,11 @@ /* global describe, it */ -var assert = require('assert') -var bscript = require('../src/script') -var minimalData = require('minimaldata') +const assert = require('assert') +const bscript = require('../src/script') +const minimalData = require('minimaldata') -var fixtures = require('./fixtures/script.json') -var fixtures2 = require('./fixtures/templates.json') +const fixtures = require('./fixtures/script.json') +const fixtures2 = require('./fixtures/templates.json') describe('script', function () { // TODO diff --git a/test/script_number.js b/test/script_number.js index 2aa33a7..9eb0820 100644 --- a/test/script_number.js +++ b/test/script_number.js @@ -1,8 +1,8 @@ /* global describe, it */ -var assert = require('assert') -var scriptNumber = require('../src/script_number') -var fixtures = require('./fixtures/script_number.json') +const assert = require('assert') +const scriptNumber = require('../src/script_number') +const fixtures = require('./fixtures/script_number.json') describe('script-number', function () { describe('decode', function () { diff --git a/test/script_signature.js b/test/script_signature.js index f53bc52..2c22540 100644 --- a/test/script_signature.js +++ b/test/script_signature.js @@ -1,9 +1,9 @@ /* global describe, it */ -var assert = require('assert') -var bscriptSig = require('../src/script').signature -var Buffer = require('safe-buffer').Buffer -var fixtures = require('./fixtures/signature.json') +const assert = require('assert') +const bscriptSig = require('../src/script').signature +const Buffer = require('safe-buffer').Buffer +const fixtures = require('./fixtures/signature.json') describe('Script Signatures', function () { function fromRaw (signature) { diff --git a/test/templates.js b/test/templates.js index c89bd52..a850564 100644 --- a/test/templates.js +++ b/test/templates.js @@ -1,12 +1,12 @@ /* global describe, it */ -var assert = require('assert') -var bcrypto = require('../src/crypto') -var bscript = require('../src/script') -var btemplates = require('../src/templates') -var ops = require('bitcoin-ops') +const assert = require('assert') +const bcrypto = require('../src/crypto') +const bscript = require('../src/script') +const btemplates = require('../src/templates') +const ops = require('bitcoin-ops') -var fixtures = require('./fixtures/templates.json') +const fixtures = require('./fixtures/templates.json') function fromHex (x) { return Buffer.from(x, 'hex') } function toHex (x) { return x.toString('hex') } diff --git a/test/transaction.js b/test/transaction.js index 62c24d0..fa69640 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -1,9 +1,9 @@ /* global describe, it, beforeEach */ -var assert = require('assert') -var bscript = require('../src/script') -var fixtures = require('./fixtures/transaction') -var Transaction = require('../src/transaction') +const assert = require('assert') +const bscript = require('../src/script') +const fixtures = require('./fixtures/transaction') +const Transaction = require('../src/transaction') describe('Transaction', function () { function fromRaw (raw, noWitness) { diff --git a/test/types.js b/test/types.js index 2dbc554..ab2110d 100644 --- a/test/types.js +++ b/test/types.js @@ -1,8 +1,8 @@ /* global describe, it */ -var assert = require('assert') -var types = require('../src/types') -var typeforce = require('typeforce') +const assert = require('assert') +const types = require('../src/types') +const typeforce = require('typeforce') describe('types', function () { describe('Buffer Hash160/Hash256', function () { From a5db0a4e44f3a075e2d129d015ddb716ecd7bc7f Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 25 Jun 2018 16:37:45 +1000 Subject: [PATCH 048/568] sed -i 's/ var / const /', with const->let fixes --- src/address.js | 16 +- src/block.js | 38 ++--- src/bufferutils.js | 4 +- src/ecpair.js | 6 +- src/script.js | 24 +-- src/script_number.js | 17 +- src/script_signature.js | 22 +-- src/templates/index.js | 6 +- src/templates/multisig/input.js | 6 +- src/templates/multisig/output.js | 12 +- src/templates/nulldata.js | 2 +- src/templates/pubkey/input.js | 4 +- src/templates/pubkey/output.js | 4 +- src/templates/pubkeyhash/input.js | 4 +- src/templates/pubkeyhash/output.js | 2 +- src/templates/scripthash/input.js | 16 +- src/templates/scripthash/output.js | 2 +- src/templates/witnesscommitment/output.js | 4 +- src/templates/witnesspubkeyhash/input.js | 2 +- src/templates/witnesspubkeyhash/output.js | 2 +- src/templates/witnessscripthash/input.js | 6 +- src/templates/witnessscripthash/output.js | 2 +- src/transaction.js | 62 +++---- src/transaction_builder.js | 154 ++++++++--------- test/address.js | 16 +- test/bitcoin.core.js | 92 +++++------ test/block.js | 22 +-- test/bufferutils.js | 10 +- test/crypto.js | 8 +- test/ecpair.js | 54 +++--- test/integration/_regtest.js | 2 +- test/integration/addresses.js | 68 ++++---- test/integration/bip32.js | 62 +++---- test/integration/blocks.js | 10 +- test/integration/cltv.js | 66 ++++---- test/integration/crypto.js | 70 ++++---- test/integration/csv.js | 34 ++-- test/integration/stealth.js | 98 +++++------ test/integration/transactions.js | 76 ++++----- test/script.js | 32 ++-- test/script_number.js | 4 +- test/script_signature.js | 8 +- test/templates.js | 126 +++++++------- test/transaction.js | 75 ++++----- test/transaction_builder.js | 191 +++++++++++----------- test/types.js | 4 +- 46 files changed, 776 insertions(+), 769 deletions(-) diff --git a/src/address.js b/src/address.js index 949e389..13fa69c 100644 --- a/src/address.js +++ b/src/address.js @@ -8,21 +8,21 @@ const typeforce = require('typeforce') const types = require('./types') function fromBase58Check (address) { - var payload = bs58check.decode(address) + const payload = bs58check.decode(address) // TODO: 4.0.0, move to "toOutputScript" if (payload.length < 21) throw new TypeError(address + ' is too short') if (payload.length > 21) throw new TypeError(address + ' is too long') - var version = payload.readUInt8(0) - var hash = payload.slice(1) + const version = payload.readUInt8(0) + const hash = payload.slice(1) return { version: version, hash: hash } } function fromBech32 (address) { - var result = bech32.decode(address) - var data = bech32.fromWords(result.words.slice(1)) + const result = bech32.decode(address) + const data = bech32.fromWords(result.words.slice(1)) return { version: result.words[0], @@ -34,7 +34,7 @@ function fromBech32 (address) { function toBase58Check (hash, version) { typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments) - var payload = Buffer.allocUnsafe(21) + const payload = Buffer.allocUnsafe(21) payload.writeUInt8(version, 0) hash.copy(payload, 1) @@ -42,7 +42,7 @@ function toBase58Check (hash, version) { } function toBech32 (data, version, prefix) { - var words = bech32.toWords(data) + const words = bech32.toWords(data) words.unshift(version) return bech32.encode(prefix, words) @@ -62,7 +62,7 @@ function fromOutputScript (outputScript, network) { function toOutputScript (address, network) { network = network || networks.bitcoin - var decode + let decode try { decode = fromBase58Check(address) } catch (e) {} diff --git a/src/block.js b/src/block.js index 0d4a585..8c7f3a6 100644 --- a/src/block.js +++ b/src/block.js @@ -19,25 +19,25 @@ function Block () { Block.fromBuffer = function (buffer) { if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)') - var offset = 0 + let offset = 0 function readSlice (n) { offset += n return buffer.slice(offset - n, offset) } function readUInt32 () { - var i = buffer.readUInt32LE(offset) + const i = buffer.readUInt32LE(offset) offset += 4 return i } function readInt32 () { - var i = buffer.readInt32LE(offset) + const i = buffer.readInt32LE(offset) offset += 4 return i } - var block = new Block() + const block = new Block() block.version = readInt32() block.prevHash = readSlice(32) block.merkleRoot = readSlice(32) @@ -48,22 +48,22 @@ Block.fromBuffer = function (buffer) { if (buffer.length === 80) return block function readVarInt () { - var vi = varuint.decode(buffer, offset) + const vi = varuint.decode(buffer, offset) offset += varuint.decode.bytes return vi } function readTransaction () { - var tx = Transaction.fromBuffer(buffer.slice(offset), true) + const tx = Transaction.fromBuffer(buffer.slice(offset), true) offset += tx.byteLength() return tx } - var nTransactions = readVarInt() + const nTransactions = readVarInt() block.transactions = [] for (var i = 0; i < nTransactions; ++i) { - var tx = readTransaction() + const tx = readTransaction() block.transactions.push(tx) } @@ -91,7 +91,7 @@ Block.prototype.getId = function () { } Block.prototype.getUTCDate = function () { - var date = new Date(0) // epoch + const date = new Date(0) // epoch date.setUTCSeconds(this.timestamp) return date @@ -99,9 +99,9 @@ Block.prototype.getUTCDate = function () { // TODO: buffer, offset compatibility Block.prototype.toBuffer = function (headersOnly) { - var buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)) + const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)) - var offset = 0 + let offset = 0 function writeSlice (slice) { slice.copy(buffer, offset) offset += slice.length @@ -129,7 +129,7 @@ Block.prototype.toBuffer = function (headersOnly) { offset += varuint.encode.bytes this.transactions.forEach(function (tx) { - var txSize = tx.byteLength() // TODO: extract from toBuffer? + const txSize = tx.byteLength() // TODO: extract from toBuffer? tx.toBuffer(buffer, offset) offset += txSize }) @@ -142,9 +142,9 @@ Block.prototype.toHex = function (headersOnly) { } Block.calculateTarget = function (bits) { - var exponent = ((bits & 0xff000000) >> 24) - 3 - var mantissa = bits & 0x007fffff - var target = Buffer.alloc(32, 0) + const exponent = ((bits & 0xff000000) >> 24) - 3 + const mantissa = bits & 0x007fffff + const target = Buffer.alloc(32, 0) target.writeUInt32BE(mantissa, 28 - exponent) return target } @@ -153,7 +153,7 @@ Block.calculateMerkleRoot = function (transactions) { typeforce([{ getHash: types.Function }], transactions) if (transactions.length === 0) throw TypeError('Cannot compute merkle root for zero transactions') - var hashes = transactions.map(function (transaction) { + const hashes = transactions.map(function (transaction) { return transaction.getHash() }) @@ -163,13 +163,13 @@ Block.calculateMerkleRoot = function (transactions) { Block.prototype.checkMerkleRoot = function () { if (!this.transactions) return false - var actualMerkleRoot = Block.calculateMerkleRoot(this.transactions) + const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions) return this.merkleRoot.compare(actualMerkleRoot) === 0 } Block.prototype.checkProofOfWork = function () { - var hash = this.getHash().reverse() - var target = Block.calculateTarget(this.bits) + const hash = this.getHash().reverse() + const target = Block.calculateTarget(this.bits) return hash.compare(target) <= 0 } diff --git a/src/bufferutils.js b/src/bufferutils.js index b173b3d..48647d6 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -7,8 +7,8 @@ function verifuint (value, max) { } function readUInt64LE (buffer, offset) { - var a = buffer.readUInt32LE(offset) - var b = buffer.readUInt32LE(offset + 4) + const a = buffer.readUInt32LE(offset) + let b = buffer.readUInt32LE(offset + 4) b *= 0x100000000 verifuint(b + a, 0x001fffffffffffff) diff --git a/src/ecpair.js b/src/ecpair.js index 2b62545..34c7120 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -63,8 +63,8 @@ function fromPublicKey (buffer, options) { } function fromWIF (string, network) { - let decoded = wif.decode(string) - let version = decoded.version + const decoded = wif.decode(string) + const version = decoded.version // list of networks? if (types.Array(network)) { @@ -90,7 +90,7 @@ function fromWIF (string, network) { function makeRandom (options) { typeforce(isOptions, options) options = options || {} - let rng = options.rng || randomBytes + const rng = options.rng || randomBytes let d do { diff --git a/src/script.js b/src/script.js index 63bf75d..30efa0a 100644 --- a/src/script.js +++ b/src/script.js @@ -38,7 +38,7 @@ function compile (chunks) { typeforce(types.Array, chunks) - var bufferSize = chunks.reduce(function (accum, chunk) { + const bufferSize = chunks.reduce(function (accum, chunk) { // data chunk if (Buffer.isBuffer(chunk)) { // adhere to BIP62.3, minimal push policy @@ -53,14 +53,14 @@ function compile (chunks) { return accum + 1 }, 0.0) - var buffer = Buffer.allocUnsafe(bufferSize) - var offset = 0 + const buffer = Buffer.allocUnsafe(bufferSize) + let offset = 0 chunks.forEach(function (chunk) { // data chunk if (Buffer.isBuffer(chunk)) { // adhere to BIP62.3, minimal push policy - var opcode = asMinimalOP(chunk) + const opcode = asMinimalOP(chunk) if (opcode !== undefined) { buffer.writeUInt8(opcode, offset) offset += 1 @@ -88,15 +88,15 @@ function decompile (buffer) { typeforce(types.Buffer, buffer) - var chunks = [] - var i = 0 + const chunks = [] + let i = 0 while (i < buffer.length) { - var opcode = buffer[i] + const opcode = buffer[i] // data chunk if ((opcode > OPS.OP_0) && (opcode <= OPS.OP_PUSHDATA4)) { - var d = pushdata.decode(buffer, i) + const d = pushdata.decode(buffer, i) // did reading a pushDataInt fail? empty script if (d === null) return null @@ -105,11 +105,11 @@ function decompile (buffer) { // attempt to read too much data? empty script if (i + d.number > buffer.length) return null - var data = buffer.slice(i, i + d.number) + const data = buffer.slice(i, i + d.number) i += d.number // decompile minimally - var op = asMinimalOP(data) + const op = asMinimalOP(data) if (op !== undefined) { chunks.push(op) } else { @@ -135,7 +135,7 @@ function toASM (chunks) { return chunks.map(function (chunk) { // data? if (Buffer.isBuffer(chunk)) { - var op = asMinimalOP(chunk) + const op = asMinimalOP(chunk) if (op === undefined) return chunk.toString('hex') chunk = op } @@ -175,7 +175,7 @@ function isCanonicalPubKey (buffer) { } function isDefinedHashType (hashType) { - var hashTypeMod = hashType & ~0x80 + const hashTypeMod = hashType & ~0x80 // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE return hashTypeMod > 0x00 && hashTypeMod < 0x04 diff --git a/src/script_number.js b/src/script_number.js index 6257690..3440797 100644 --- a/src/script_number.js +++ b/src/script_number.js @@ -4,7 +4,7 @@ function decode (buffer, maxLength, minimal) { maxLength = maxLength || 4 minimal = minimal === undefined ? true : minimal - var length = buffer.length + const length = buffer.length if (length === 0) return 0 if (length > maxLength) throw new TypeError('Script number overflow') if (minimal) { @@ -15,16 +15,15 @@ function decode (buffer, maxLength, minimal) { // 40-bit if (length === 5) { - var a = buffer.readUInt32LE(0) - var b = buffer.readUInt8(4) + const a = buffer.readUInt32LE(0) + const b = buffer.readUInt8(4) if (b & 0x80) return -(((b & ~0x80) * 0x100000000) + a) return (b * 0x100000000) + a } - var result = 0 - // 32-bit / 24-bit / 16-bit / 8-bit + let result = 0 for (var i = 0; i < length; ++i) { result |= buffer[i] << (8 * i) } @@ -43,10 +42,10 @@ function scriptNumSize (i) { } function encode (number) { - var value = Math.abs(number) - var size = scriptNumSize(value) - var buffer = Buffer.allocUnsafe(size) - var negative = number < 0 + let value = Math.abs(number) + const size = scriptNumSize(value) + const buffer = Buffer.allocUnsafe(size) + const negative = number < 0 for (var i = 0; i < size; ++i) { buffer.writeUInt8(value & 0xff, i) diff --git a/src/script_signature.js b/src/script_signature.js index 56f6380..bdb3ddb 100644 --- a/src/script_signature.js +++ b/src/script_signature.js @@ -15,21 +15,21 @@ function toDER (x) { function fromDER (x) { if (x[0] === 0x00) x = x.slice(1) - let buffer = Buffer.alloc(32, 0) - let bstart = Math.max(0, 32 - x.length) + const buffer = Buffer.alloc(32, 0) + const bstart = Math.max(0, 32 - x.length) x.copy(buffer, bstart) return buffer } // BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) function decode (buffer) { - let hashType = buffer.readUInt8(buffer.length - 1) - let hashTypeMod = hashType & ~0x80 + const hashType = buffer.readUInt8(buffer.length - 1) + const hashTypeMod = hashType & ~0x80 if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) - let decode = bip66.decode(buffer.slice(0, -1)) - let r = fromDER(decode.r) - let s = fromDER(decode.s) + const decode = bip66.decode(buffer.slice(0, -1)) + const r = fromDER(decode.r) + const s = fromDER(decode.s) return { signature: Buffer.concat([r, s], 64), @@ -43,14 +43,14 @@ function encode (signature, hashType) { hashType: types.UInt8 }, { signature, hashType }) - let hashTypeMod = hashType & ~0x80 + const hashTypeMod = hashType & ~0x80 if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) - let hashTypeBuffer = Buffer.allocUnsafe(1) + const hashTypeBuffer = Buffer.allocUnsafe(1) hashTypeBuffer.writeUInt8(hashType, 0) - let r = toDER(signature.slice(0, 32)) - let s = toDER(signature.slice(32, 64)) + const r = toDER(signature.slice(0, 32)) + const s = toDER(signature.slice(32, 64)) return Buffer.concat([ bip66.encode(r, s), diff --git a/src/templates/index.js b/src/templates/index.js index 4d7ce69..94ce996 100644 --- a/src/templates/index.js +++ b/src/templates/index.js @@ -27,7 +27,7 @@ function classifyOutput (script) { if (scriptHash.output.check(script)) return types.P2SH // XXX: optimization, below functions .decompile before use - var chunks = decompile(script) + const chunks = decompile(script) if (!chunks) throw new TypeError('Invalid script') if (multisig.output.check(chunks)) return types.MULTISIG @@ -40,7 +40,7 @@ function classifyOutput (script) { function classifyInput (script, allowIncomplete) { // XXX: optimization, below functions .decompile before use - var chunks = decompile(script) + const chunks = decompile(script) if (!chunks) throw new TypeError('Invalid script') if (pubKeyHash.input.check(chunks)) return types.P2PKH @@ -53,7 +53,7 @@ function classifyInput (script, allowIncomplete) { function classifyWitness (script, allowIncomplete) { // XXX: optimization, below functions .decompile before use - var chunks = decompile(script) + const chunks = decompile(script) if (!chunks) throw new TypeError('Invalid script') if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js index 54f2a9f..318e911 100644 --- a/src/templates/multisig/input.js +++ b/src/templates/multisig/input.js @@ -11,7 +11,7 @@ function partialSignature (value) { } function check (script, allowIncomplete) { - var chunks = bscript.decompile(script) + const chunks = bscript.decompile(script) if (chunks.length < 2) return false if (chunks[0] !== OPS.OP_0) return false @@ -29,7 +29,7 @@ function encodeStack (signatures, scriptPubKey) { typeforce([partialSignature], signatures) if (scriptPubKey) { - var scriptData = p2mso.decode(scriptPubKey) + const scriptData = p2mso.decode(scriptPubKey) if (signatures.length < scriptData.m) { throw new TypeError('Not enough signatures provided') @@ -59,7 +59,7 @@ function decodeStack (stack, allowIncomplete) { } function decode (buffer, allowIncomplete) { - var stack = bscript.decompile(buffer) + const stack = bscript.decompile(buffer) return decodeStack(stack, allowIncomplete) } diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.js index baef7e7..34b9ff4 100644 --- a/src/templates/multisig/output.js +++ b/src/templates/multisig/output.js @@ -7,14 +7,14 @@ const OPS = require('bitcoin-ops') const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 function check (script, allowIncomplete) { - var chunks = bscript.decompile(script) + const chunks = bscript.decompile(script) if (chunks.length < 4) return false if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false if (!types.Number(chunks[0])) return false if (!types.Number(chunks[chunks.length - 2])) return false - var m = chunks[0] - OP_INT_BASE - var n = chunks[chunks.length - 2] - OP_INT_BASE + const m = chunks[0] - OP_INT_BASE + const n = chunks[chunks.length - 2] - OP_INT_BASE if (m <= 0) return false if (n > 16) return false @@ -22,7 +22,7 @@ function check (script, allowIncomplete) { if (n !== chunks.length - 3) return false if (allowIncomplete) return true - var keys = chunks.slice(1, -2) + const keys = chunks.slice(1, -2) return keys.every(bscript.isCanonicalPubKey) } check.toJSON = function () { return 'multi-sig output' } @@ -36,7 +36,7 @@ function encode (m, pubKeys) { pubKeys: pubKeys }) - var n = pubKeys.length + const n = pubKeys.length if (n < m) throw new TypeError('Not enough pubKeys provided') return bscript.compile([].concat( @@ -48,7 +48,7 @@ function encode (m, pubKeys) { } function decode (buffer, allowIncomplete) { - var chunks = bscript.decompile(buffer) + const chunks = bscript.decompile(buffer) typeforce(check, chunks, allowIncomplete) return { diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js index 1b9c404..2ad09d7 100644 --- a/src/templates/nulldata.js +++ b/src/templates/nulldata.js @@ -6,7 +6,7 @@ const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { - var buffer = bscript.compile(script) + const buffer = bscript.compile(script) return buffer.length > 1 && buffer[0] === OPS.OP_RETURN diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js index e4cb931..cd5ffd3 100644 --- a/src/templates/pubkey/input.js +++ b/src/templates/pubkey/input.js @@ -4,7 +4,7 @@ const bscript = require('../../script') const typeforce = require('typeforce') function check (script) { - var chunks = bscript.decompile(script) + const chunks = bscript.decompile(script) return chunks.length === 1 && bscript.isCanonicalScriptSignature(chunks[0]) @@ -27,7 +27,7 @@ function decodeStack (stack) { } function decode (buffer) { - var stack = bscript.decompile(buffer) + const stack = bscript.decompile(buffer) return decodeStack(stack) } diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.js index f8bff2f..2d34b4f 100644 --- a/src/templates/pubkey/output.js +++ b/src/templates/pubkey/output.js @@ -5,7 +5,7 @@ const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { - var chunks = bscript.decompile(script) + const chunks = bscript.decompile(script) return chunks.length === 2 && bscript.isCanonicalPubKey(chunks[0]) && @@ -20,7 +20,7 @@ function encode (pubKey) { } function decode (buffer) { - var chunks = bscript.decompile(buffer) + const chunks = bscript.decompile(buffer) typeforce(check, chunks) return chunks[0] diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js index 0d5497b..2f633e1 100644 --- a/src/templates/pubkeyhash/input.js +++ b/src/templates/pubkeyhash/input.js @@ -4,7 +4,7 @@ const bscript = require('../../script') const typeforce = require('typeforce') function check (script) { - var chunks = bscript.decompile(script) + const chunks = bscript.decompile(script) return chunks.length === 2 && bscript.isCanonicalScriptSignature(chunks[0]) && @@ -39,7 +39,7 @@ function decodeStack (stack) { } function decode (buffer) { - var stack = bscript.decompile(buffer) + const stack = bscript.decompile(buffer) return decodeStack(stack) } diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.js index c89685b..c496275 100644 --- a/src/templates/pubkeyhash/output.js +++ b/src/templates/pubkeyhash/output.js @@ -6,7 +6,7 @@ const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { - var buffer = bscript.compile(script) + const buffer = bscript.compile(script) return buffer.length === 25 && buffer[0] === OPS.OP_DUP && diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js index a2d0693..fdea2a1 100644 --- a/src/templates/scripthash/input.js +++ b/src/templates/scripthash/input.js @@ -11,14 +11,14 @@ const p2wpkho = require('../witnesspubkeyhash/output') const p2wsho = require('../witnessscripthash/output') function check (script, allowIncomplete) { - var chunks = bscript.decompile(script) + const chunks = bscript.decompile(script) if (chunks.length < 1) return false - var lastChunk = chunks[chunks.length - 1] + const lastChunk = chunks[chunks.length - 1] if (!Buffer.isBuffer(lastChunk)) return false - var scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))) - var redeemScriptChunks = bscript.decompile(lastChunk) + const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))) + const redeemScriptChunks = bscript.decompile(lastChunk) // is redeemScript a valid script? if (!redeemScriptChunks) return false @@ -47,13 +47,13 @@ function check (script, allowIncomplete) { check.toJSON = function () { return 'scriptHash input' } function encodeStack (redeemScriptStack, redeemScript) { - var serializedScriptPubKey = bscript.compile(redeemScript) + const serializedScriptPubKey = bscript.compile(redeemScript) return [].concat(redeemScriptStack, serializedScriptPubKey) } function encode (redeemScriptSig, redeemScript) { - var redeemScriptStack = bscript.decompile(redeemScriptSig) + const redeemScriptStack = bscript.decompile(redeemScriptSig) return bscript.compile(encodeStack(redeemScriptStack, redeemScript)) } @@ -69,8 +69,8 @@ function decodeStack (stack) { } function decode (buffer) { - var stack = bscript.decompile(buffer) - var result = decodeStack(stack) + const stack = bscript.decompile(buffer) + const result = decodeStack(stack) result.redeemScriptSig = bscript.compile(result.redeemScriptStack) delete result.redeemScriptStack return result diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.js index d9776da..68ee237 100644 --- a/src/templates/scripthash/output.js +++ b/src/templates/scripthash/output.js @@ -6,7 +6,7 @@ const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { - var buffer = bscript.compile(script) + const buffer = bscript.compile(script) return buffer.length === 23 && buffer[0] === OPS.OP_HASH160 && diff --git a/src/templates/witnesscommitment/output.js b/src/templates/witnesscommitment/output.js index 5f97d69..8938f45 100644 --- a/src/templates/witnesscommitment/output.js +++ b/src/templates/witnesscommitment/output.js @@ -9,7 +9,7 @@ const OPS = require('bitcoin-ops') const HEADER = Buffer.from('aa21a9ed', 'hex') function check (script) { - var buffer = bscript.compile(script) + const buffer = bscript.compile(script) return buffer.length > 37 && buffer[0] === OPS.OP_RETURN && @@ -22,7 +22,7 @@ check.toJSON = function () { return 'Witness commitment output' } function encode (commitment) { typeforce(types.Hash256bit, commitment) - var buffer = Buffer.allocUnsafe(36) + const buffer = Buffer.allocUnsafe(36) HEADER.copy(buffer, 0) commitment.copy(buffer, 4) diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.js index a0549ff..23de02a 100644 --- a/src/templates/witnesspubkeyhash/input.js +++ b/src/templates/witnesspubkeyhash/input.js @@ -8,7 +8,7 @@ function isCompressedCanonicalPubKey (pubKey) { } function check (script) { - var chunks = bscript.decompile(script) + const chunks = bscript.decompile(script) return chunks.length === 2 && bscript.isCanonicalScriptSignature(chunks[0]) && diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.js index d2b091b..65ec47a 100644 --- a/src/templates/witnesspubkeyhash/output.js +++ b/src/templates/witnesspubkeyhash/output.js @@ -6,7 +6,7 @@ const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { - var buffer = bscript.compile(script) + const buffer = bscript.compile(script) return buffer.length === 22 && buffer[0] === OPS.OP_0 && diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js index c21ee1e..0188048 100644 --- a/src/templates/witnessscripthash/input.js +++ b/src/templates/witnessscripthash/input.js @@ -12,15 +12,15 @@ function check (chunks, allowIncomplete) { typeforce(types.Array, chunks) if (chunks.length < 1) return false - var witnessScript = chunks[chunks.length - 1] + const witnessScript = chunks[chunks.length - 1] if (!Buffer.isBuffer(witnessScript)) return false - var witnessScriptChunks = bscript.decompile(witnessScript) + const witnessScriptChunks = bscript.decompile(witnessScript) // is witnessScript a valid script? if (witnessScriptChunks.length === 0) return false - var witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)) + const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)) // match types if (p2pkh.input.check(witnessRawScriptSig) && diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.js index d800fda..d5b3a21 100644 --- a/src/templates/witnessscripthash/output.js +++ b/src/templates/witnessscripthash/output.js @@ -6,7 +6,7 @@ const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { - var buffer = bscript.compile(script) + const buffer = bscript.compile(script) return buffer.length === 34 && buffer[0] === OPS.OP_0 && diff --git a/src/transaction.js b/src/transaction.js index 8187c73..751446f 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -8,13 +8,13 @@ const types = require('./types') const varuint = require('varuint-bitcoin') function varSliceSize (someScript) { - var length = someScript.length + const length = someScript.length return varuint.encodingLength(length) + length } function vectorSize (someVector) { - var length = someVector.length + const length = someVector.length return varuint.encodingLength(length) + someVector.reduce(function (sum, witness) { return sum + varSliceSize(witness) @@ -47,32 +47,32 @@ const BLANK_OUTPUT = { } Transaction.fromBuffer = function (buffer, __noStrict) { - var offset = 0 + let offset = 0 function readSlice (n) { offset += n return buffer.slice(offset - n, offset) } function readUInt32 () { - var i = buffer.readUInt32LE(offset) + const i = buffer.readUInt32LE(offset) offset += 4 return i } function readInt32 () { - var i = buffer.readInt32LE(offset) + const i = buffer.readInt32LE(offset) offset += 4 return i } function readUInt64 () { - var i = bufferutils.readUInt64LE(buffer, offset) + const i = bufferutils.readUInt64LE(buffer, offset) offset += 8 return i } function readVarInt () { - var vi = varuint.decode(buffer, offset) + const vi = varuint.decode(buffer, offset) offset += varuint.decode.bytes return vi } @@ -82,26 +82,26 @@ Transaction.fromBuffer = function (buffer, __noStrict) { } function readVector () { - var count = readVarInt() - var vector = [] + const count = readVarInt() + const vector = [] for (var i = 0; i < count; i++) vector.push(readVarSlice()) return vector } - var tx = new Transaction() + const tx = new Transaction() tx.version = readInt32() - var marker = buffer.readUInt8(offset) - var flag = buffer.readUInt8(offset + 1) + const marker = buffer.readUInt8(offset) + const flag = buffer.readUInt8(offset + 1) - var hasWitnesses = false + let hasWitnesses = false if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && flag === Transaction.ADVANCED_TRANSACTION_FLAG) { offset += 2 hasWitnesses = true } - var vinLen = readVarInt() + const vinLen = readVarInt() for (var i = 0; i < vinLen; ++i) { tx.ins.push({ hash: readSlice(32), @@ -112,7 +112,7 @@ Transaction.fromBuffer = function (buffer, __noStrict) { }) } - var voutLen = readVarInt() + const voutLen = readVarInt() for (i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), @@ -192,8 +192,8 @@ Transaction.prototype.hasWitnesses = function () { } Transaction.prototype.weight = function () { - var base = this.__byteLength(false) - var total = this.__byteLength(true) + const base = this.__byteLength(false) + const total = this.__byteLength(true) return base * 3 + total } @@ -206,7 +206,7 @@ Transaction.prototype.byteLength = function () { } Transaction.prototype.__byteLength = function (__allowWitness) { - var hasWitnesses = __allowWitness && this.hasWitnesses() + const hasWitnesses = __allowWitness && this.hasWitnesses() return ( (hasWitnesses ? 10 : 8) + @@ -219,7 +219,7 @@ Transaction.prototype.__byteLength = function (__allowWitness) { } Transaction.prototype.clone = function () { - var newTx = new Transaction() + const newTx = new Transaction() newTx.version = this.version newTx.locktime = this.locktime @@ -258,11 +258,11 @@ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashT if (inIndex >= this.ins.length) return ONE // ignore OP_CODESEPARATOR - var ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) { + const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) { return x !== opcodes.OP_CODESEPARATOR })) - var txTmp = this.clone() + const txTmp = this.clone() // SIGHASH_NONE: ignore all outputs? (wildcard payee) if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { @@ -309,7 +309,7 @@ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashT } // serialize and hash - var buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) + const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) buffer.writeInt32LE(hashType, buffer.length - 4) txTmp.__toBuffer(buffer, 0, false) @@ -319,7 +319,7 @@ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashT Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value, hashType) { typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) - var tbuffer, toffset + let tbuffer, toffset function writeSlice (slice) { toffset += slice.copy(tbuffer, toffset) } function writeUInt32 (i) { toffset = tbuffer.writeUInt32LE(i, toffset) } function writeUInt64 (i) { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) } @@ -329,9 +329,9 @@ Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value } function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } - var hashOutputs = ZERO - var hashPrevouts = ZERO - var hashSequence = ZERO + let hashOutputs = ZERO + let hashPrevouts = ZERO + let hashSequence = ZERO if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { tbuffer = Buffer.allocUnsafe(36 * this.ins.length) @@ -360,7 +360,7 @@ Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - var txOutsSize = this.outs.reduce(function (sum, output) { + const txOutsSize = this.outs.reduce(function (sum, output) { return sum + 8 + varSliceSize(output.script) }, 0) @@ -374,7 +374,7 @@ Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value hashOutputs = bcrypto.hash256(tbuffer) } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { - var output = this.outs[inIndex] + const output = this.outs[inIndex] tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) toffset = 0 @@ -387,7 +387,7 @@ Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) toffset = 0 - var input = this.ins[inIndex] + const input = this.ins[inIndex] writeUInt32(this.version) writeSlice(hashPrevouts) writeSlice(hashSequence) @@ -418,7 +418,7 @@ Transaction.prototype.toBuffer = function (buffer, initialOffset) { Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitness) { if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)) - var offset = initialOffset || 0 + let offset = initialOffset || 0 function writeSlice (slice) { offset += slice.copy(buffer, offset) } function writeUInt8 (i) { offset = buffer.writeUInt8(i, offset) } function writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) } @@ -433,7 +433,7 @@ Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitne writeInt32(this.version) - var hasWitnesses = __allowWitness && this.hasWitnesses() + const hasWitnesses = __allowWitness && this.hasWitnesses() if (hasWitnesses) { writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index d5ad34c..3ec3d6e 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -23,8 +23,8 @@ function supportedP2SHType (type) { } function extractChunks (type, chunks, script) { - var pubKeys = [] - var signatures = [] + let pubKeys = [] + let signatures = [] switch (type) { case scriptTypes.P2PKH: // if (redeemScript) throw new Error('Nonstandard... P2SH(P2PKH)') @@ -39,7 +39,7 @@ function extractChunks (type, chunks, script) { case scriptTypes.MULTISIG: if (script) { - var multisig = btemplates.multisig.output.decode(script) + const multisig = btemplates.multisig.output.decode(script) pubKeys = multisig.pubKeys } @@ -57,22 +57,22 @@ function extractChunks (type, chunks, script) { function expandInput (scriptSig, witnessStack) { if (scriptSig.length === 0 && witnessStack.length === 0) return {} - var prevOutScript - var prevOutType - var scriptType - var script - var redeemScript - var witnessScript - var witnessScriptType - var redeemScriptType - var witness = false - var p2wsh = false - var p2sh = false - var witnessProgram - var chunks + let prevOutScript + let prevOutType + let scriptType + let script + let redeemScript + let witnessScript + let witnessScriptType + let redeemScriptType + let witness = false + let p2wsh = false + let p2sh = false + let witnessProgram + let chunks - var scriptSigChunks = bscript.decompile(scriptSig) || [] - var sigType = btemplates.classifyInput(scriptSigChunks, true) + const scriptSigChunks = bscript.decompile(scriptSig) || [] + const sigType = btemplates.classifyInput(scriptSigChunks, true) if (sigType === scriptTypes.P2SH) { p2sh = true redeemScript = scriptSigChunks[scriptSigChunks.length - 1] @@ -82,7 +82,7 @@ function expandInput (scriptSig, witnessStack) { script = redeemScript } - var classifyWitness = btemplates.classifyWitness(witnessStack, true) + const classifyWitness = btemplates.classifyWitness(witnessStack, true) if (classifyWitness === scriptTypes.P2WSH) { witnessScript = witnessStack[witnessStack.length - 1] witnessScriptType = btemplates.classifyOutput(witnessScript) @@ -114,8 +114,8 @@ function expandInput (scriptSig, witnessStack) { chunks = witnessStack.slice(0, -1) } else if (classifyWitness === scriptTypes.P2WPKH) { witness = true - var key = witnessStack[witnessStack.length - 1] - var keyHash = bcrypto.hash160(key) + const key = witnessStack[witnessStack.length - 1] + const keyHash = bcrypto.hash160(key) if (scriptSig.length === 0) { prevOutScript = btemplates.witnessPubKeyHash.output.encode(keyHash) prevOutType = scriptTypes.P2WPKH @@ -147,9 +147,9 @@ function expandInput (scriptSig, witnessStack) { chunks = scriptSigChunks } - var expanded = extractChunks(scriptType, chunks, script) + const expanded = extractChunks(scriptType, chunks, script) - var result = { + const result = { pubKeys: expanded.pubKeys, signatures: expanded.signatures, prevOutScript: prevOutScript, @@ -177,11 +177,11 @@ function fixMultisigOrder (input, transaction, vin) { if (input.redeemScriptType !== scriptTypes.MULTISIG || !input.redeemScript) return if (input.pubKeys.length === input.signatures.length) return - var unmatched = input.signatures.concat() + const unmatched = input.signatures.concat() input.signatures = input.pubKeys.map(function (pubKey) { - var keyPair = ECPair.fromPublicKey(pubKey) - var match + const keyPair = ECPair.fromPublicKey(pubKey) + let match // check for a signature unmatched.some(function (signature, i) { @@ -189,8 +189,8 @@ function fixMultisigOrder (input, transaction, vin) { if (!signature) return false // TODO: avoid O(n) hashForSignature - var parsed = bscript.signature.decode(signature) - var hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType) + const parsed = bscript.signature.decode(signature) + const hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType) // skip if signature does not match pubKey if (!keyPair.verify(hash, parsed.signature)) return false @@ -209,20 +209,20 @@ function fixMultisigOrder (input, transaction, vin) { function expandOutput (script, scriptType, ourPubKey) { typeforce(types.Buffer, script) - var scriptChunks = bscript.decompile(script) || [] + const scriptChunks = bscript.decompile(script) || [] if (!scriptType) { scriptType = btemplates.classifyOutput(script) } - var pubKeys = [] + let pubKeys = [] switch (scriptType) { // does our hash160(pubKey) match the output scripts? case scriptTypes.P2PKH: if (!ourPubKey) break - var pkh1 = scriptChunks[2] - var pkh2 = bcrypto.hash160(ourPubKey) + const pkh1 = scriptChunks[2] + const pkh2 = bcrypto.hash160(ourPubKey) if (pkh1.equals(pkh2)) pubKeys = [ourPubKey] break @@ -230,8 +230,8 @@ function expandOutput (script, scriptType, ourPubKey) { case scriptTypes.P2WPKH: if (!ourPubKey) break - var wpkh1 = scriptChunks[1] - var wpkh2 = bcrypto.hash160(ourPubKey) + const wpkh1 = scriptChunks[1] + const wpkh2 = bcrypto.hash160(ourPubKey) if (wpkh1.equals(wpkh2)) pubKeys = [ourPubKey] break @@ -257,7 +257,7 @@ function checkP2SHInput (input, redeemScriptHash) { if (input.prevOutType) { if (input.prevOutType !== scriptTypes.P2SH) throw new Error('PrevOutScript must be P2SH') - var chunks = bscript.decompile(input.prevOutScript) + const chunks = bscript.decompile(input.prevOutScript) if (!chunks) throw new Error('Invalid prevOutScript') if (!chunks[1].equals(redeemScriptHash)) throw new Error('Inconsistent hash160(redeemScript)') } @@ -267,28 +267,28 @@ function checkP2WSHInput (input, witnessScriptHash) { if (input.prevOutType) { if (input.prevOutType !== scriptTypes.P2WSH) throw new Error('PrevOutScript must be P2WSH') - var chunks = bscript.decompile(input.prevOutScript) + const chunks = bscript.decompile(input.prevOutScript) if (!chunks) throw new Error('Invalid witnessScript') if (!chunks[1].equals(witnessScriptHash)) throw new Error('Inconsistent sha256(witnessScript)') } } function prepareInput (input, kpPubKey, redeemScript, witnessValue, witnessScript) { - var expanded - var prevOutType - var prevOutScript + let expanded + let prevOutType + let prevOutScript - var p2sh = false - var p2shType - var redeemScriptHash + let p2sh = false + let p2shType + let redeemScriptHash - var witness = false - var p2wsh = false - var witnessType - var witnessScriptHash + let witness = false + let p2wsh = false + let witnessType + let witnessScriptHash - var signType - var signScript + let signType + let signScript if (redeemScript && witnessScript) { redeemScriptHash = bcrypto.hash160(redeemScript) @@ -408,15 +408,15 @@ function buildStack (type, signatures, pubKeys, allowIncomplete) { } function buildInput (input, allowIncomplete) { - var scriptType = input.prevOutType - var sig = [] - var witness = [] + let scriptType = input.prevOutType + let sig = [] + let witness = [] if (supportedType(scriptType)) { sig = buildStack(scriptType, input.signatures, input.pubKeys, allowIncomplete) } - var p2sh = false + let p2sh = false if (scriptType === btemplates.types.P2SH) { // We can remove this error later when we have a guarantee prepareInput // rejects unsignable scripts - it MUST be signable at this point. @@ -503,7 +503,7 @@ TransactionBuilder.prototype.setVersion = function (version) { } TransactionBuilder.fromTransaction = function (transaction, network) { - var txb = new TransactionBuilder(network) + const txb = new TransactionBuilder(network) // Copy transaction fields txb.setVersion(transaction.version) @@ -536,7 +536,7 @@ TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOu throw new Error('No, this would invalidate signatures') } - var value + let value // is it a hex string? if (typeof txHash === 'string') { @@ -545,7 +545,7 @@ TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOu // is it a Transaction object? } else if (txHash instanceof Transaction) { - var txOut = txHash.outs[vout] + const txOut = txHash.outs[vout] prevOutScript = txOut.script value = txOut.value @@ -564,10 +564,10 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) throw new Error('coinbase inputs not supported') } - var prevTxOut = txHash.toString('hex') + ':' + vout + const prevTxOut = txHash.toString('hex') + ':' + vout if (this.__prevTxSet[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut) - var input = {} + let input = {} // derive what we can from the scriptSig if (options.script !== undefined) { @@ -581,10 +581,10 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) // derive what we can from the previous transactions output script if (!input.prevOutScript && options.prevOutScript) { - var prevOutType + let prevOutType if (!input.pubKeys && !input.signatures) { - var expanded = expandOutput(options.prevOutScript) + const expanded = expandOutput(options.prevOutScript) if (expanded.pubKeys) { input.pubKeys = expanded.pubKeys @@ -598,7 +598,7 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) input.prevOutType = prevOutType || btemplates.classifyOutput(options.prevOutScript) } - var vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig) + const vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig) this.__inputs[vin] = input this.__prevTxSet[prevTxOut] = true return vin @@ -630,12 +630,12 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) { if (!this.__tx.outs.length) throw new Error('Transaction has no outputs') } - var tx = this.__tx.clone() + const tx = this.__tx.clone() // Create script signatures from inputs this.__inputs.forEach(function (input, i) { - var scriptType = input.witnessScriptType || input.redeemScriptType || input.prevOutType + const scriptType = input.witnessScriptType || input.redeemScriptType || input.prevOutType if (!scriptType && !allowIncomplete) throw new Error('Transaction is not complete') - var result = buildInput(input, allowIncomplete) + const result = buildInput(input, allowIncomplete) // skip if no result if (!allowIncomplete) { @@ -677,7 +677,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy if (!this.__inputs[vin]) throw new Error('No input at index: ' + vin) hashType = hashType || Transaction.SIGHASH_ALL - var input = this.__inputs[vin] + const input = this.__inputs[vin] // if redeemScript was previously provided, enforce consistency if (input.redeemScript !== undefined && @@ -686,7 +686,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy throw new Error('Inconsistent redeemScript') } - var kpPubKey = keyPair.publicKey || keyPair.getPublicKey() + const kpPubKey = keyPair.publicKey || keyPair.getPublicKey() if (!canSign(input)) { if (witnessValue !== undefined) { if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue') @@ -699,7 +699,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy } // ready to sign - var signatureHash + let signatureHash if (input.witness) { signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType) } else { @@ -707,7 +707,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy } // enforce in order signing of public keys - var signed = input.pubKeys.some(function (pubKey, i) { + const signed = input.pubKeys.some(function (pubKey, i) { if (!kpPubKey.equals(pubKey)) return false if (input.signatures[i]) throw new Error('Signature already exists') @@ -717,7 +717,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy input.prevOutType === scriptTypes.P2WSH )) throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH') - let signature = keyPair.sign(signatureHash) + const signature = keyPair.sign(signatureHash) input.signatures[i] = bscript.signature.encode(signature, hashType) return true }) @@ -736,7 +736,7 @@ TransactionBuilder.prototype.__canModifyInputs = function () { return input.signatures.every(function (signature) { if (!signature) return true - var hashType = signatureHashType(signature) + const hashType = signatureHashType(signature) // if SIGHASH_ANYONECANPAY is set, signatures would not // be invalidated by more inputs @@ -746,17 +746,17 @@ TransactionBuilder.prototype.__canModifyInputs = function () { } TransactionBuilder.prototype.__canModifyOutputs = function () { - var nInputs = this.__tx.ins.length - var nOutputs = this.__tx.outs.length + const nInputs = this.__tx.ins.length + const nOutputs = this.__tx.outs.length return this.__inputs.every(function (input) { if (input.signatures === undefined) return true return input.signatures.every(function (signature) { if (!signature) return true - var hashType = signatureHashType(signature) + const hashType = signatureHashType(signature) - var hashTypeMod = hashType & 0x1f + const hashTypeMod = hashType & 0x1f if (hashTypeMod === Transaction.SIGHASH_NONE) return true if (hashTypeMod === Transaction.SIGHASH_SINGLE) { // if SIGHASH_SINGLE is set, and nInputs > nOutputs @@ -770,13 +770,13 @@ TransactionBuilder.prototype.__canModifyOutputs = function () { TransactionBuilder.prototype.__overMaximumFees = function (bytes) { // not all inputs will have .value defined - var incoming = this.__inputs.reduce(function (a, x) { return a + (x.value >>> 0) }, 0) + const incoming = this.__inputs.reduce(function (a, x) { return a + (x.value >>> 0) }, 0) // but all outputs do, and if we have any input value // we can immediately determine if the outputs are too small - var outgoing = this.__tx.outs.reduce(function (a, x) { return a + x.value }, 0) - var fee = incoming - outgoing - var feeRate = fee / bytes + const outgoing = this.__tx.outs.reduce(function (a, x) { return a + x.value }, 0) + const fee = incoming - outgoing + const feeRate = fee / bytes return feeRate > this.maximumFeeRate } diff --git a/test/address.js b/test/address.js index 7bc87f0..99955e7 100644 --- a/test/address.js +++ b/test/address.js @@ -23,7 +23,7 @@ describe('address', function () { if (!f.base58check) return it('decodes ' + f.base58check, function () { - var decode = baddress.fromBase58Check(f.base58check) + const decode = baddress.fromBase58Check(f.base58check) assert.strictEqual(decode.version, f.version) assert.strictEqual(decode.hash.toString('hex'), f.hash) @@ -44,7 +44,7 @@ describe('address', function () { if (!f.bech32) return it('decodes ' + f.bech32, function () { - var actual = baddress.fromBech32(f.bech32) + const actual = baddress.fromBech32(f.bech32) assert.strictEqual(actual.version, f.version) assert.strictEqual(actual.prefix, NETWORKS[f.network].bech32) @@ -64,8 +64,8 @@ describe('address', function () { describe('fromOutputScript', function () { fixtures.standard.forEach(function (f) { it('encodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', function () { - var script = bscript.fromASM(f.script) - var address = baddress.fromOutputScript(script, NETWORKS[f.network]) + const script = bscript.fromASM(f.script) + const address = baddress.fromOutputScript(script, NETWORKS[f.network]) assert.strictEqual(address, f.base58check || f.bech32.toLowerCase()) }) @@ -73,7 +73,7 @@ describe('address', function () { fixtures.invalid.fromOutputScript.forEach(function (f) { it('throws when ' + f.script.slice(0, 30) + '... ' + f.exception, function () { - var script = bscript.fromASM(f.script) + const script = bscript.fromASM(f.script) assert.throws(function () { baddress.fromOutputScript(script) @@ -87,7 +87,7 @@ describe('address', function () { if (!f.base58check) return it('encodes ' + f.hash + ' (' + f.network + ')', function () { - var address = baddress.toBase58Check(Buffer.from(f.hash, 'hex'), f.version) + const address = baddress.toBase58Check(Buffer.from(f.hash, 'hex'), f.version) assert.strictEqual(address, f.base58check) }) @@ -97,7 +97,7 @@ describe('address', function () { describe('toBech32', function () { fixtures.bech32.forEach((f, i) => { if (!f.bech32) return - var data = Buffer.from(f.data, 'hex') + const data = Buffer.from(f.data, 'hex') it('encode ' + f.address, function () { assert.deepEqual(baddress.toBech32(data, f.version, f.prefix), f.address) @@ -118,7 +118,7 @@ describe('address', function () { describe('toOutputScript', function () { fixtures.standard.forEach(function (f) { it('decodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', function () { - var script = baddress.toOutputScript(f.base58check || f.bech32, NETWORKS[f.network]) + const script = baddress.toOutputScript(f.base58check || f.bech32, NETWORKS[f.network]) assert.strictEqual(bscript.toASM(script), f.script) }) diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index bda6899..f0aecf7 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -17,19 +17,19 @@ describe('Bitcoin-core', function () { // base58EncodeDecode describe('base58', function () { base58EncodeDecode.forEach(function (f) { - var fhex = f[0] - var fb58 = f[1] + const fhex = f[0] + const fb58 = f[1] it('can decode ' + fb58, function () { - var buffer = base58.decode(fb58) - var actual = buffer.toString('hex') + const buffer = base58.decode(fb58) + const actual = buffer.toString('hex') assert.strictEqual(actual, fhex) }) it('can encode ' + fhex, function () { - var buffer = Buffer.from(fhex, 'hex') - var actual = base58.encode(buffer) + const buffer = Buffer.from(fhex, 'hex') + const actual = base58.encode(buffer) assert.strictEqual(actual, fb58) }) @@ -38,20 +38,20 @@ describe('Bitcoin-core', function () { // base58KeysValid describe('address.toBase58Check', function () { - var typeMap = { + const typeMap = { 'pubkey': 'pubKeyHash', 'script': 'scriptHash' } base58KeysValid.forEach(function (f) { - var expected = f[0] - var hash = Buffer.from(f[1], 'hex') - var params = f[2] + const expected = f[0] + const hash = Buffer.from(f[1], 'hex') + const params = f[2] if (params.isPrivkey) return - var network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin - var version = network[typeMap[params.addrType]] + const network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin + const version = network[typeMap[params.addrType]] it('can export ' + expected, function () { assert.strictEqual(bitcoin.address.toBase58Check(hash, version), expected) @@ -61,7 +61,7 @@ describe('Bitcoin-core', function () { // base58KeysInvalid describe('address.fromBase58Check', function () { - var allowedNetworks = [ + const allowedNetworks = [ bitcoin.networks.bitcoin.pubkeyhash, bitcoin.networks.bitcoin.scripthash, bitcoin.networks.testnet.pubkeyhash, @@ -69,11 +69,11 @@ describe('Bitcoin-core', function () { ] base58KeysInvalid.forEach(function (f) { - var string = f[0] + const string = f[0] it('throws on ' + string, function () { assert.throws(function () { - var address = bitcoin.address.fromBase58Check(string) + const address = bitcoin.address.fromBase58Check(string) assert.notEqual(allowedNetworks.indexOf(address.version), -1, 'Invalid network') }, /(Invalid (checksum|network))|(too (short|long))/) @@ -84,14 +84,14 @@ describe('Bitcoin-core', function () { // base58KeysValid describe('ECPair', function () { base58KeysValid.forEach(function (f) { - var string = f[0] - var hex = f[1] - var params = f[2] + const string = f[0] + const hex = f[1] + const params = f[2] if (!params.isPrivkey) return - var network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin - var keyPair = bitcoin.ECPair.fromWIF(string, network) + const network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin + const keyPair = bitcoin.ECPair.fromWIF(string, network) it('fromWIF imports ' + string, function () { assert.strictEqual(keyPair.privateKey.toString('hex'), hex) @@ -106,13 +106,13 @@ describe('Bitcoin-core', function () { // base58KeysInvalid describe('ECPair.fromWIF', function () { - var allowedNetworks = [ + const allowedNetworks = [ bitcoin.networks.bitcoin, bitcoin.networks.testnet ] base58KeysInvalid.forEach(function (f) { - var string = f[0] + const string = f[0] it('throws on ' + string, function () { assert.throws(function () { @@ -125,7 +125,7 @@ describe('Bitcoin-core', function () { describe('Block.fromHex', function () { blocksValid.forEach(function (f) { it('can parse ' + f.id, function () { - var block = bitcoin.Block.fromHex(f.hex) + const block = bitcoin.Block.fromHex(f.hex) assert.strictEqual(block.getId(), f.id) assert.strictEqual(block.transactions.length, f.transactions) @@ -139,19 +139,19 @@ describe('Bitcoin-core', function () { // Objects that are only a single string are ignored if (f.length === 1) return - var inputs = f[0] - var fhex = f[1] - // var verifyFlags = f[2] // TODO: do we need to test this? + const inputs = f[0] + const fhex = f[1] + // const verifyFlags = f[2] // TODO: do we need to test this? it('can decode ' + fhex, function () { - var transaction = bitcoin.Transaction.fromHex(fhex) + const transaction = bitcoin.Transaction.fromHex(fhex) transaction.ins.forEach(function (txIn, i) { - var input = inputs[i] + const input = inputs[i] // reverse because test data is reversed - var prevOutHash = Buffer.from(input[0], 'hex').reverse() - var prevOutIndex = input[1] + const prevOutHash = Buffer.from(input[0], 'hex').reverse() + const prevOutIndex = input[1] assert.deepEqual(txIn.hash, prevOutHash) @@ -168,29 +168,29 @@ describe('Bitcoin-core', function () { // Objects that are only a single string are ignored if (f.length === 1) return - var txHex = f[0] - var scriptHex = f[1] - var inIndex = f[2] - var hashType = f[3] - var expectedHash = f[4] + const txHex = f[0] + const scriptHex = f[1] + const inIndex = f[2] + const hashType = f[3] + const expectedHash = f[4] - var hashTypes = [] + const hashTypes = [] if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_NONE) hashTypes.push('SIGHASH_NONE') else if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_SINGLE) hashTypes.push('SIGHASH_SINGLE') else hashTypes.push('SIGHASH_ALL') if (hashType & bitcoin.Transaction.SIGHASH_ANYONECANPAY) hashTypes.push('SIGHASH_ANYONECANPAY') - var hashTypeName = hashTypes.join(' | ') + const hashTypeName = hashTypes.join(' | ') it('should hash ' + txHex.slice(0, 40) + '... (' + hashTypeName + ')', function () { - var transaction = bitcoin.Transaction.fromHex(txHex) + const transaction = bitcoin.Transaction.fromHex(txHex) assert.strictEqual(transaction.toHex(), txHex) - var script = Buffer.from(scriptHex, 'hex') - var scriptChunks = bitcoin.script.decompile(script) + const script = Buffer.from(scriptHex, 'hex') + const scriptChunks = bitcoin.script.decompile(script) assert.strictEqual(bitcoin.script.compile(scriptChunks).toString('hex'), scriptHex) - var hash = transaction.hashForSignature(inIndex, script, hashType) + const hash = transaction.hashForSignature(inIndex, script, hashType) // reverse because test data is reversed assert.equal(hash.reverse().toString('hex'), expectedHash) @@ -200,11 +200,11 @@ describe('Bitcoin-core', function () { describe('script.signature.decode', function () { sigCanonical.forEach(function (hex) { - var buffer = Buffer.from(hex, 'hex') + const buffer = Buffer.from(hex, 'hex') it('can parse ' + hex, function () { - var parsed = bitcoin.script.signature.decode(buffer) - var actual = bitcoin.script.signature.encode(parsed.signature, parsed.hashType) + const parsed = bitcoin.script.signature.decode(buffer) + const actual = bitcoin.script.signature.encode(parsed.signature, parsed.hashType) assert.strictEqual(actual.toString('hex'), hex) }) @@ -214,8 +214,8 @@ describe('Bitcoin-core', function () { if (i === 0) return if (i % 2 !== 0) return - var description = sigNoncanonical[i - 1].slice(0, -1) - var buffer = Buffer.from(hex, 'hex') + const description = sigNoncanonical[i - 1].slice(0, -1) + const buffer = Buffer.from(hex, 'hex') it('throws on ' + description, function () { assert.throws(function () { diff --git a/test/block.js b/test/block.js index 5794d66..1da9de3 100644 --- a/test/block.js +++ b/test/block.js @@ -8,8 +8,8 @@ const fixtures = require('./fixtures/block') describe('Block', function () { describe('version', function () { it('should be interpreted as an int32le', function () { - var blockHex = 'ffffffff0000000000000000000000000000000000000000000000000000000000000000414141414141414141414141414141414141414141414141414141414141414101000000020000000300000000' - var block = Block.fromHex(blockHex) + const blockHex = 'ffffffff0000000000000000000000000000000000000000000000000000000000000000414141414141414141414141414141414141414141414141414141414141414101000000020000000300000000' + const block = Block.fromHex(blockHex) assert.equal(-1, block.version) assert.equal(1, block.timestamp) }) @@ -18,7 +18,7 @@ describe('Block', function () { describe('calculateTarget', function () { fixtures.targets.forEach(function (f) { it('returns ' + f.expected + ' for 0x' + f.bits, function () { - var bits = parseInt(f.bits, 16) + const bits = parseInt(f.bits, 16) assert.equal(Block.calculateTarget(bits).toString('hex'), f.expected) }) @@ -28,7 +28,7 @@ describe('Block', function () { describe('fromBuffer/fromHex', function () { fixtures.valid.forEach(function (f) { it('imports ' + f.description, function () { - var block = Block.fromHex(f.hex) + const block = Block.fromHex(f.hex) assert.strictEqual(block.version, f.version) assert.strictEqual(block.prevHash.toString('hex'), f.prevHash) @@ -51,7 +51,7 @@ describe('Block', function () { describe('toBuffer/toHex', function () { fixtures.valid.forEach(function (f) { - var block + let block beforeEach(function () { block = Block.fromHex(f.hex) @@ -66,7 +66,7 @@ describe('Block', function () { describe('getHash/getId', function () { fixtures.valid.forEach(function (f) { - var block + let block beforeEach(function () { block = Block.fromHex(f.hex) @@ -81,14 +81,14 @@ describe('Block', function () { describe('getUTCDate', function () { fixtures.valid.forEach(function (f) { - var block + let block beforeEach(function () { block = Block.fromHex(f.hex) }) it('returns UTC date of ' + f.id, function () { - var utcDate = block.getUTCDate().getTime() + const utcDate = block.getUTCDate().getTime() assert.strictEqual(utcDate, f.timestamp * 1e3) }) @@ -105,7 +105,7 @@ describe('Block', function () { fixtures.valid.forEach(function (f) { if (f.hex.length === 160) return - var block + let block beforeEach(function () { block = Block.fromHex(f.hex) @@ -121,7 +121,7 @@ describe('Block', function () { fixtures.valid.forEach(function (f) { if (f.hex.length === 160) return - var block + let block beforeEach(function () { block = Block.fromHex(f.hex) @@ -135,7 +135,7 @@ describe('Block', function () { describe('checkProofOfWork', function () { fixtures.valid.forEach(function (f) { - var block + let block beforeEach(function () { block = Block.fromHex(f.hex) diff --git a/test/bufferutils.js b/test/bufferutils.js index da98828..aa044af 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -9,8 +9,8 @@ describe('bufferutils', function () { describe('readUInt64LE', function () { fixtures.valid.forEach(function (f) { it('decodes ' + f.hex, function () { - var buffer = Buffer.from(f.hex, 'hex') - var number = bufferutils.readUInt64LE(buffer, 0) + const buffer = Buffer.from(f.hex, 'hex') + const number = bufferutils.readUInt64LE(buffer, 0) assert.strictEqual(number, f.dec) }) @@ -18,7 +18,7 @@ describe('bufferutils', function () { fixtures.invalid.readUInt64LE.forEach(function (f) { it('throws on ' + f.description, function () { - var buffer = Buffer.from(f.hex, 'hex') + const buffer = Buffer.from(f.hex, 'hex') assert.throws(function () { bufferutils.readUInt64LE(buffer, 0) @@ -30,7 +30,7 @@ describe('bufferutils', function () { describe('writeUInt64LE', function () { fixtures.valid.forEach(function (f) { it('encodes ' + f.dec, function () { - var buffer = Buffer.alloc(8, 0) + const buffer = Buffer.alloc(8, 0) bufferutils.writeUInt64LE(buffer, f.dec, 0) assert.strictEqual(buffer.toString('hex'), f.hex) @@ -39,7 +39,7 @@ describe('bufferutils', function () { fixtures.invalid.readUInt64LE.forEach(function (f) { it('throws on ' + f.description, function () { - var buffer = Buffer.alloc(8, 0) + const buffer = Buffer.alloc(8, 0) assert.throws(function () { bufferutils.writeUInt64LE(buffer, f.dec, 0) diff --git a/test/crypto.js b/test/crypto.js index 91bc674..18f2a37 100644 --- a/test/crypto.js +++ b/test/crypto.js @@ -9,12 +9,12 @@ describe('crypto', function () { ['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(function (algorithm) { describe(algorithm, function () { fixtures.forEach(function (f) { - var fn = bcrypto[algorithm] - var expected = f[algorithm] + const fn = bcrypto[algorithm] + const expected = f[algorithm] it('returns ' + expected + ' for ' + f.hex, function () { - var data = Buffer.from(f.hex, 'hex') - var actual = fn(data).toString('hex') + const data = Buffer.from(f.hex, 'hex') + const actual = fn(data).toString('hex') assert.strictEqual(actual, expected) }) diff --git a/test/ecpair.js b/test/ecpair.js index f873651..fc215f1 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -24,13 +24,13 @@ const GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6 describe('ECPair', function () { describe('constructor', function () { it('defaults to compressed', function () { - let keyPair = ECPair.fromPrivateKey(ONE) + const keyPair = ECPair.fromPrivateKey(ONE) assert.strictEqual(keyPair.compressed, true) }) it('supports the uncompressed option', function () { - let keyPair = ECPair.fromPrivateKey(ONE, { + const keyPair = ECPair.fromPrivateKey(ONE, { compressed: false }) @@ -38,7 +38,7 @@ describe('ECPair', function () { }) it('supports the network option', function () { - let keyPair = ECPair.fromPrivateKey(ONE, { + const keyPair = ECPair.fromPrivateKey(ONE, { compressed: false, network: NETWORKS.testnet }) @@ -48,10 +48,10 @@ describe('ECPair', function () { fixtures.valid.forEach(function (f) { it('derives public key for ' + f.WIF, function () { - let d = Buffer.from(f.d, 'hex') + const d = Buffer.from(f.d, 'hex') console.log(d) - let keyPair = ECPair.fromPrivateKey(d, { + const keyPair = ECPair.fromPrivateKey(d, { compressed: f.compressed }) @@ -62,12 +62,12 @@ describe('ECPair', function () { fixtures.invalid.constructor.forEach(function (f) { it('throws ' + f.exception, function () { if (f.d) { - let d = Buffer.from(f.d, 'hex') + const d = Buffer.from(f.d, 'hex') assert.throws(function () { ECPair.fromPrivateKey(d, f.options) }, new RegExp(f.exception)) } else { - let Q = Buffer.from(f.Q, 'hex') + const Q = Buffer.from(f.Q, 'hex') assert.throws(function () { ECPair.fromPublicKey(Q, f.options) }, new RegExp(f.exception)) @@ -95,8 +95,8 @@ describe('ECPair', function () { describe('fromWIF', function () { fixtures.valid.forEach(function (f) { it('imports ' + f.WIF + ' (' + f.network + ')', function () { - let network = NETWORKS[f.network] - let keyPair = ECPair.fromWIF(f.WIF, network) + const network = NETWORKS[f.network] + const keyPair = ECPair.fromWIF(f.WIF, network) assert.strictEqual(keyPair.privateKey.toString('hex'), f.d) assert.strictEqual(keyPair.compressed, f.compressed) @@ -106,7 +106,7 @@ describe('ECPair', function () { fixtures.valid.forEach(function (f) { it('imports ' + f.WIF + ' (via list of networks)', function () { - let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) assert.strictEqual(keyPair.privateKey.toString('hex'), f.d) assert.strictEqual(keyPair.compressed, f.compressed) @@ -117,7 +117,7 @@ describe('ECPair', function () { fixtures.invalid.fromWIF.forEach(function (f) { it('throws on ' + f.WIF, function () { assert.throws(function () { - let networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST + const networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST ECPair.fromWIF(f.WIF, networks) }, new RegExp(f.exception)) @@ -128,29 +128,29 @@ describe('ECPair', function () { describe('toWIF', function () { fixtures.valid.forEach(function (f) { it('exports ' + f.WIF, function () { - let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) - let result = keyPair.toWIF() + const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + const result = keyPair.toWIF() assert.strictEqual(result, f.WIF) }) }) }) describe('makeRandom', function () { - let d = Buffer.alloc(32, 4) - let exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' + const d = Buffer.alloc(32, 4) + const exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' describe('uses randombytes RNG', function () { it('generates a ECPair', function () { - let stub = { randombytes: function () { return d } } - let ProxiedECPair = proxyquire('../src/ecpair', stub) + const stub = { randombytes: function () { return d } } + const ProxiedECPair = proxyquire('../src/ecpair', stub) - let keyPair = ProxiedECPair.makeRandom() + const keyPair = ProxiedECPair.makeRandom() assert.strictEqual(keyPair.toWIF(), exWIF) }) }) it('allows a custom RNG to be used', function () { - let keyPair = ECPair.makeRandom({ + const keyPair = ECPair.makeRandom({ rng: function (size) { return d.slice(0, size) } }) @@ -158,14 +158,14 @@ describe('ECPair', function () { }) it('retains the same defaults as ECPair constructor', function () { - let keyPair = ECPair.makeRandom() + const keyPair = ECPair.makeRandom() assert.strictEqual(keyPair.compressed, true) assert.strictEqual(keyPair.network, NETWORKS.bitcoin) }) it('supports the options parameter', function () { - let keyPair = ECPair.makeRandom({ + const keyPair = ECPair.makeRandom({ compressed: false, network: NETWORKS.testnet }) @@ -185,7 +185,7 @@ describe('ECPair', function () { }) it('loops until d is within interval [1, n) : 1', hoodwink(function () { - let rng = this.stub(function f () { + const rng = this.stub(function f () { if (f.calls === 0) return ZERO // 0 return ONE // >0 }, 2) @@ -194,7 +194,7 @@ describe('ECPair', function () { })) it('loops until d is within interval [1, n) : n - 1', hoodwink(function () { - let rng = this.stub(function f () { + const rng = this.stub(function f () { if (f.calls === 0) return ZERO // <1 if (f.calls === 1) return GROUP_ORDER // >n-1 return GROUP_ORDER_LESS_1 // n-1 @@ -207,8 +207,8 @@ describe('ECPair', function () { describe('.network', function () { fixtures.valid.forEach(function (f) { it('returns ' + f.network + ' for ' + f.WIF, function () { - let network = NETWORKS[f.network] - let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + const network = NETWORKS[f.network] + const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) assert.strictEqual(keyPair.network, network) }) @@ -216,7 +216,9 @@ describe('ECPair', function () { }) describe('tinysecp wrappers', function () { - let keyPair, hash, signature + let keyPair + let hash + let signature beforeEach(function () { keyPair = ECPair.makeRandom() diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 91b25d5..4cdb6da 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -60,7 +60,7 @@ function verify (txo, callback) { fetch(txo.txId, function (err, tx) { if (err) return callback(err) - var txoActual = tx.outs[txo.vout] + const txoActual = tx.outs[txo.vout] if (txo.address) assert.strictEqual(txoActual.address, txo.address) if (txo.value) assert.strictEqual(txoActual.value, txo.value) callback() diff --git a/test/integration/addresses.js b/test/integration/addresses.js index a38b5cf..e7c715b 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -28,17 +28,17 @@ function getAddress (node, network) { describe('bitcoinjs-lib (addresses)', function () { it('can generate a random address', function () { - var keyPair = bitcoin.ECPair.makeRandom({ rng: rng }) - var address = getAddress(keyPair) + const keyPair = bitcoin.ECPair.makeRandom({ rng: rng }) + const address = getAddress(keyPair) assert.strictEqual(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64') }) it('can generate an address from a SHA256 hash', function () { - var hash = bitcoin.crypto.sha256(Buffer.from('correct horse battery staple')) + const hash = bitcoin.crypto.sha256(Buffer.from('correct horse battery staple')) - var keyPair = bitcoin.ECPair.fromPrivateKey(hash) - var address = getAddress(keyPair) + const keyPair = bitcoin.ECPair.fromPrivateKey(hash) + const address = getAddress(keyPair) // Generating addresses from SHA256 hashes is not secure if the input to the hash function is predictable // Do not use with predictable inputs @@ -46,70 +46,70 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can import an address via WIF', function () { - var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var address = getAddress(keyPair) + const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + const address = getAddress(keyPair) assert.strictEqual(address, '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31') }) it('can generate a 2-of-3 multisig P2SH address', function () { - var pubKeys = [ + const pubKeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9' ].map(function (hex) { return Buffer.from(hex, 'hex') }) - var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) // 2 of 3 - var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey) + const redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) // 2 of 3 + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey) assert.strictEqual(address, '36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7') }) it('can generate a SegWit address', function () { - var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey)) - var address = bitcoin.address.fromOutputScript(scriptPubKey) + const scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey)) + const address = bitcoin.address.fromOutputScript(scriptPubKey) assert.strictEqual(address, 'bc1qt97wqg464zrhnx23upykca5annqvwkwujjglky') }) it('can generate a SegWit address (via P2SH)', function () { - var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey)) - var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey) + const redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey)) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey) assert.strictEqual(address, '34AgLJhwXrvmkZS1o5TrcdeevMt22Nar53') }) it('can generate a SegWit 3-of-4 multisig address', function () { - var pubKeys = [ + const pubKeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', '023e4740d0ba639e28963f3476157b7cf2fb7c6fdf4254f97099cf8670b505ea59', '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9' ].map(function (hex) { return Buffer.from(hex, 'hex') }) - var witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) // 3 of 4 - var scriptPubKey = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey) + const witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) // 3 of 4 + const scriptPubKey = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey) assert.strictEqual(address, 'bc1q75f6dv4q8ug7zhujrsp5t0hzf33lllnr3fe7e2pra3v24mzl8rrqtp3qul') }) it('can generate a SegWit 2-of-2 multisig address (via P2SH)', function () { - var pubKeys = [ + const pubKeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9' ].map(function (hex) { return Buffer.from(hex, 'hex') }) - var witnessScript = bitcoin.script.multisig.output.encode(2, pubKeys) // 2 of 2 - var redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) - var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey) + const witnessScript = bitcoin.script.multisig.output.encode(2, pubKeys) // 2 of 2 + const redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey) assert.strictEqual(address, '3P4mrxQfmExfhxqjLnR2Ah4WES5EB1KBrN') }) @@ -134,19 +134,19 @@ describe('bitcoinjs-lib (addresses)', function () { // other networks it('can generate a Testnet address', function () { - let testnet = bitcoin.networks.testnet - let keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: rng }) - let wif = keyPair.toWIF() - let address = getAddress(keyPair, testnet) + const testnet = bitcoin.networks.testnet + const keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: rng }) + const wif = keyPair.toWIF() + const address = getAddress(keyPair, testnet) assert.strictEqual(address, 'mubSzQNtZfDj1YdNP6pNDuZy6zs6GDn61L') assert.strictEqual(wif, 'cRgnQe9MUu1JznntrLaoQpB476M8PURvXVQB5R2eqms5tXnzNsrr') }) it('can generate a Litecoin address', function () { - let keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: rng }) - let wif = keyPair.toWIF() - let address = getAddress(keyPair, LITECOIN) + const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: rng }) + const wif = keyPair.toWIF() + const address = getAddress(keyPair, LITECOIN) assert.strictEqual(address, 'LZJSxZbjqJ2XVEquqfqHg1RQTDdfST5PTn') assert.strictEqual(wif, 'T7A4PUSgTDHecBxW1ZiYFrDNRih2o7M8Gf9xpoCgudPF9gDiNvuS') diff --git a/test/integration/bip32.js b/test/integration/bip32.js index dc3591a..c780267 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -15,40 +15,40 @@ function getAddress (node, network) { describe('bitcoinjs-lib (BIP32)', function () { it('can import a BIP32 testnet xpriv and export to WIF', function () { - var xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' - var node = bip32.fromBase58(xpriv, bitcoin.networks.testnet) + const xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' + const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet) assert.equal(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') }) it('can export a BIP32 xpriv, then import it', function () { - var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' - var seed = bip39.mnemonicToSeed(mnemonic) - var node = bip32.fromSeed(seed) - var string = node.toBase58() - var restored = bip32.fromBase58(string) + const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' + const seed = bip39.mnemonicToSeed(mnemonic) + const node = bip32.fromSeed(seed) + const string = node.toBase58() + const restored = bip32.fromBase58(string) assert.equal(getAddress(node), getAddress(restored)) // same public key assert.equal(node.toWIF(), restored.toWIF()) // same private key }) it('can export a BIP32 xpub', function () { - var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' - var seed = bip39.mnemonicToSeed(mnemonic) - var node = bip32.fromSeed(seed) - var string = node.neutered().toBase58() + const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' + const seed = bip39.mnemonicToSeed(mnemonic) + const node = bip32.fromSeed(seed) + const string = node.neutered().toBase58() assert.equal(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') }) it('can create a BIP32, bitcoin, account 0, external address', function () { - var path = "m/0'/0/0" - var root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) + const path = "m/0'/0/0" + const root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) - var child1 = root.derivePath(path) + const child1 = root.derivePath(path) // option 2, manually - var child1b = root.deriveHardened(0) + const child1b = root.deriveHardened(0) .derive(0) .derive(0) @@ -57,12 +57,12 @@ describe('bitcoinjs-lib (BIP32)', function () { }) it('can create a BIP44, bitcoin, account 0, external address', function () { - var root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) + const root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) - var child1 = root.derivePath("m/44'/0'/0'/0/0") + const child1 = root.derivePath("m/44'/0'/0'/0/0") // option 2, manually - var child1b = root.deriveHardened(44) + const child1b = root.deriveHardened(44) .deriveHardened(0) .deriveHardened(0) .derive(0) @@ -73,29 +73,29 @@ describe('bitcoinjs-lib (BIP32)', function () { }) it('can create a BIP49, bitcoin testnet, account 0, external address', function () { - var mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' - var seed = bip39.mnemonicToSeed(mnemonic) - var root = bip32.fromSeed(seed) + const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' + const seed = bip39.mnemonicToSeed(mnemonic) + const root = bip32.fromSeed(seed) - var path = "m/49'/1'/0'/0/0" - var child = root.derivePath(path) + const path = "m/49'/1'/0'/0/0" + const child = root.derivePath(path) - var keyhash = bitcoin.crypto.hash160(child.publicKey) - var scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash) - var addressBytes = bitcoin.crypto.hash160(scriptSig) - var outputScript = bitcoin.script.scriptHash.output.encode(addressBytes) - var address = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.testnet) + const keyhash = bitcoin.crypto.hash160(child.publicKey) + const scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash) + const addressBytes = bitcoin.crypto.hash160(scriptSig) + const outputScript = bitcoin.script.scriptHash.output.encode(addressBytes) + const address = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.testnet) assert.equal(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2') }) it('can use BIP39 to generate BIP32 addresses', function () { // var mnemonic = bip39.generateMnemonic() - var mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' + const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' assert(bip39.validateMnemonic(mnemonic)) - var seed = bip39.mnemonicToSeed(mnemonic) - var root = bip32.fromSeed(seed) + const seed = bip39.mnemonicToSeed(mnemonic) + const root = bip32.fromSeed(seed) // receive addresses assert.strictEqual(getAddress(root.derivePath("m/0'/0/0")), '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC') diff --git a/test/integration/blocks.js b/test/integration/blocks.js index d4a7ec4..a0bdea3 100644 --- a/test/integration/blocks.js +++ b/test/integration/blocks.js @@ -7,16 +7,16 @@ const bitcoin = require('../../') describe('bitcoinjs-lib (blocks)', function () { it('can extract a height from a CoinBase transaction', function () { // from 00000000000000000097669cdca131f24d40c4cc7d80eaa65967a2d09acf6ce6 - let txHex = '010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98fd16761d220400000000000000aa340000d49f0000ffffffff02b07fc366000000001976a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12bf1e400120000000000000000000000000000000000000000000000000000000000000000000000000' - let tx = bitcoin.Transaction.fromHex(txHex) + const txHex = '010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98fd16761d220400000000000000aa340000d49f0000ffffffff02b07fc366000000001976a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12bf1e400120000000000000000000000000000000000000000000000000000000000000000000000000' + const tx = bitcoin.Transaction.fromHex(txHex) assert.strictEqual(tx.ins.length, 1) - let script = tx.ins[0].script + const script = tx.ins[0].script // bitcoin.script.decompile(script) // returns [] :( assert.strictEqual(script[0], 0x03) - let heightBuffer = script.slice(1, 4) - let height = bitcoin.script.number.decode(heightBuffer) + const heightBuffer = script.slice(1, 4) + const height = bitcoin.script.number.decode(heightBuffer) assert.strictEqual(height, 498303) }) }) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 90e78a9..0823b55 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -15,7 +15,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { regtestUtils.mine(11, done) }) - let hashType = bitcoin.Transaction.SIGHASH_ALL + const hashType = bitcoin.Transaction.SIGHASH_ALL function cltvCheckSigOutput (aQ, bQ, lockTime) { return bitcoin.script.compile([ @@ -43,24 +43,24 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { this.timeout(30000) // 3 hours ago - let lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) - let redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - let scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - let address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) // fund the P2SH(CLTV) address regtestUtils.faucet(address, 1e5, function (err, unspent) { if (err) return done(err) - var txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.setLockTime(lockTime) txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) // {Alice's signature} OP_TRUE - var tx = txb.buildIncomplete() - var signatureHash = tx.hashForSignature(0, redeemScript, hashType) - var redeemScriptSig = bitcoin.script.scriptHash.input.encode([ + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.opcodes.OP_TRUE ], redeemScript) @@ -87,24 +87,24 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { if (err) return done(err) // 5 blocks from now - var lockTime = bip65.encode({ blocks: height + 5 }) - var redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const lockTime = bip65.encode({ blocks: height + 5 }) + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) // fund the P2SH(CLTV) address regtestUtils.faucet(address, 1e5, function (err, unspent) { if (err) return done(err) - var txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.setLockTime(lockTime) txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) // {Alice's signature} OP_TRUE - var tx = txb.buildIncomplete() - var signatureHash = tx.hashForSignature(0, redeemScript, hashType) - var redeemScriptSig = bitcoin.script.scriptHash.input.encode([ + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.opcodes.OP_TRUE ], redeemScript) @@ -136,24 +136,24 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { this.timeout(30000) // two hours ago - var lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) - var redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) // fund the P2SH(CLTV) address regtestUtils.faucet(address, 2e5, function (err, unspent) { if (err) return done(err) - var txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.setLockTime(lockTime) txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4) // {Alice's signature} {Bob's signature} OP_FALSE - var tx = txb.buildIncomplete() - var signatureHash = tx.hashForSignature(0, redeemScript, hashType) - var redeemScriptSig = bitcoin.script.scriptHash.input.encode([ + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.opcodes.OP_FALSE @@ -178,24 +178,24 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { this.timeout(30000) // two hours from now - var lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) - var redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) // fund the P2SH(CLTV) address regtestUtils.faucet(address, 2e4, function (err, unspent) { if (err) return done(err) - var txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.setLockTime(lockTime) txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) // {Alice's signature} OP_TRUE - var tx = txb.buildIncomplete() - var signatureHash = tx.hashForSignature(0, redeemScript, hashType) - var redeemScriptSig = bitcoin.script.scriptHash.input.encode([ + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.opcodes.OP_TRUE diff --git a/test/integration/crypto.js b/test/integration/crypto.js index deddf67..8352f29 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -15,18 +15,18 @@ describe('bitcoinjs-lib (crypto)', function () { this.timeout(30000) // https://blockchain.info/tx/f4c16475f2a6e9c602e4a287f9db3040e319eb9ece74761a4b84bc820fbeef50 - var tx = bitcoin.Transaction.fromHex('01000000020b668015b32a6178d8524cfef6dc6fc0a4751915c2e9b2ed2d2eab02424341c8000000006a47304402205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022024bf5f506968f5f23f1835574d5afe0e9021b4a5b65cf9742332d5e4acb68f41012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffffa95fa69f11dc1cbb77ef64f25a95d4b12ebda57d19d843333819d95c9172ff89000000006b48304502205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022100832176b59e8f50c56631acbc824bcba936c9476c559c42a4468be98975d07562012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffff02b000eb04000000001976a91472956eed9a8ecb19ae7e3ebd7b06cae4668696a788ac303db000000000001976a9146c0bd55dd2592287cd9992ce3ba3fc1208fb76da88ac00000000') + const tx = bitcoin.Transaction.fromHex('01000000020b668015b32a6178d8524cfef6dc6fc0a4751915c2e9b2ed2d2eab02424341c8000000006a47304402205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022024bf5f506968f5f23f1835574d5afe0e9021b4a5b65cf9742332d5e4acb68f41012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffffa95fa69f11dc1cbb77ef64f25a95d4b12ebda57d19d843333819d95c9172ff89000000006b48304502205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022100832176b59e8f50c56631acbc824bcba936c9476c559c42a4468be98975d07562012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffff02b000eb04000000001976a91472956eed9a8ecb19ae7e3ebd7b06cae4668696a788ac303db000000000001976a9146c0bd55dd2592287cd9992ce3ba3fc1208fb76da88ac00000000') tx.ins.forEach(function (input, vin) { - var script = input.script - var scriptChunks = bitcoin.script.decompile(script) + const script = input.script + const scriptChunks = bitcoin.script.decompile(script) assert(bitcoin.script.pubKeyHash.input.check(scriptChunks), 'Expected pubKeyHash script') - var prevOutScript = bitcoin.address.toOutputScript('1ArJ9vRaQcoQ29mTWZH768AmRwzb6Zif1z') - var scriptSignature = bitcoin.script.signature.decode(scriptChunks[0]) - var publicKey = bitcoin.ECPair.fromPublicKey(scriptChunks[1]) + const prevOutScript = bitcoin.address.toOutputScript('1ArJ9vRaQcoQ29mTWZH768AmRwzb6Zif1z') + const scriptSignature = bitcoin.script.signature.decode(scriptChunks[0]) + const publicKey = bitcoin.ECPair.fromPublicKey(scriptChunks[1]) - var m = tx.hashForSignature(vin, prevOutScript, scriptSignature.hashType) + const m = tx.hashForSignature(vin, prevOutScript, scriptSignature.hashType) assert(publicKey.verify(m, scriptSignature.signature), 'Invalid m') // store the required information @@ -35,34 +35,34 @@ describe('bitcoinjs-lib (crypto)', function () { }) // finally, run the tasks, then on to the math - var n = secp256k1.n + const n = secp256k1.n for (var i = 0; i < tx.ins.length; ++i) { for (var j = i + 1; j < tx.ins.length; ++j) { - var inputA = tx.ins[i] - var inputB = tx.ins[j] + const inputA = tx.ins[i] + const inputB = tx.ins[j] // enforce matching r values - let r = inputA.signature.slice(0, 32) - let rB = inputB.signature.slice(0, 32) + const r = inputA.signature.slice(0, 32) + const rB = inputB.signature.slice(0, 32) assert.strictEqual(r.toString('hex'), rB.toString('hex')) - var rInv = bigi.fromBuffer(r).modInverse(n) + const rInv = bigi.fromBuffer(r).modInverse(n) - var s1 = bigi.fromBuffer(inputA.signature.slice(32, 64)) - var s2 = bigi.fromBuffer(inputB.signature.slice(32, 64)) - var z1 = inputA.z - var z2 = inputB.z + const s1 = bigi.fromBuffer(inputA.signature.slice(32, 64)) + const s2 = bigi.fromBuffer(inputB.signature.slice(32, 64)) + const z1 = inputA.z + const z2 = inputB.z - var zz = z1.subtract(z2).mod(n) - var ss = s1.subtract(s2).mod(n) + const zz = z1.subtract(z2).mod(n) + const ss = s1.subtract(s2).mod(n) // k = (z1 - z2) / (s1 - s2) // d1 = (s1 * k - z1) / r // d2 = (s2 * k - z2) / r - var k = zz.multiply(ss.modInverse(n)).mod(n) - var d1 = ((s1.multiply(k).mod(n)).subtract(z1).mod(n)).multiply(rInv).mod(n) - var d2 = ((s2.multiply(k).mod(n)).subtract(z2).mod(n)).multiply(rInv).mod(n) + const k = zz.multiply(ss.modInverse(n)).mod(n) + const d1 = ((s1.multiply(k).mod(n)).subtract(z1).mod(n)).multiply(rInv).mod(n) + const d2 = ((s2.multiply(k).mod(n)).subtract(z2).mod(n)).multiply(rInv).mod(n) // enforce matching private keys assert.strictEqual(d1.toString(), d2.toString()) @@ -75,41 +75,41 @@ describe('bitcoinjs-lib (crypto)', function () { assert(master.isNeutered(), 'You already have the parent private key') assert(!child.isNeutered(), 'Missing child private key') - var serQP = master.publicKey - var d1 = child.privateKey - var d2 - var data = Buffer.alloc(37) + const serQP = master.publicKey + const d1 = child.privateKey + const data = Buffer.alloc(37) serQP.copy(data, 0) // search index space until we find it + let d2 for (var i = 0; i < 0x80000000; ++i) { data.writeUInt32BE(i, 33) // calculate I - var I = crypto.createHmac('sha512', master.chainCode).update(data).digest() - var IL = I.slice(0, 32) + const I = crypto.createHmac('sha512', master.chainCode).update(data).digest() + const IL = I.slice(0, 32) // See bip32.js:273 to understand d2 = tinysecp.privateSub(d1, IL) - var Qp = bip32.fromPrivateKey(d2, Buffer.alloc(32, 0)).publicKey + const Qp = bip32.fromPrivateKey(d2, Buffer.alloc(32, 0)).publicKey if (Qp.equals(serQP)) break } - var node = bip32.fromPrivateKey(d2, master.chainCode, master.network) + const node = bip32.fromPrivateKey(d2, master.chainCode, master.network) node.depth = master.depth node.index = master.index node.masterFingerprint = master.masterFingerprint return node } - var seed = crypto.randomBytes(32) - var master = bip32.fromSeed(seed) - var child = master.derive(6) // m/6 + const seed = crypto.randomBytes(32) + const master = bip32.fromSeed(seed) + const child = master.derive(6) // m/6 // now for the recovery - var neuteredMaster = master.neutered() - var recovered = recoverParent(neuteredMaster, child) + const neuteredMaster = master.neutered() + const recovered = recoverParent(neuteredMaster, child) assert.strictEqual(recovered.toBase58(), master.toBase58()) }) }) diff --git a/test/integration/csv.js b/test/integration/csv.js index 8413959..ac0b919 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -15,7 +15,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { regtestUtils.mine(11, done) }) - let hashType = bitcoin.Transaction.SIGHASH_ALL + const hashType = bitcoin.Transaction.SIGHASH_ALL // IF MTP (from when confirmed) > seconds, aQ can redeem function csvCheckSigOutput (aQ, bQ, sequence) { @@ -43,23 +43,23 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { if (err) return done(err) // 5 blocks from now - let sequence = bip68.encode({ blocks: 5 }) - let redeemScript = csvCheckSigOutput(alice, bob, sequence) - let scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - let address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const sequence = bip68.encode({ blocks: 5 }) + const redeemScript = csvCheckSigOutput(alice, bob, sequence) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) // fund the P2SH(CSV) address regtestUtils.faucet(address, 1e5, function (err, unspent) { if (err) return done(err) - let txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout, sequence) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) // {Alice's signature} OP_TRUE - let tx = txb.buildIncomplete() - let signatureHash = tx.hashForSignature(0, redeemScript, hashType) - let redeemScriptSig = bitcoin.script.scriptHash.input.encode([ + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.opcodes.OP_TRUE ], redeemScript) @@ -91,23 +91,23 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { this.timeout(30000) // two hours after confirmation - let sequence = bip68.encode({ seconds: 7168 }) - let redeemScript = csvCheckSigOutput(alice, bob, sequence) - let scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - let address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const sequence = bip68.encode({ seconds: 7168 }) + const redeemScript = csvCheckSigOutput(alice, bob, sequence) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) // fund the P2SH(CSV) address regtestUtils.faucet(address, 2e4, function (err, unspent) { if (err) return done(err) - let txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout, sequence) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) // {Alice's signature} OP_TRUE - let tx = txb.buildIncomplete() - let signatureHash = tx.hashForSignature(0, redeemScript, hashType) - let redeemScriptSig = bitcoin.script.scriptHash.input.encode([ + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.opcodes.OP_TRUE diff --git a/test/integration/stealth.js b/test/integration/stealth.js index f6d81e6..01a933b 100644 --- a/test/integration/stealth.js +++ b/test/integration/stealth.js @@ -13,60 +13,60 @@ function getAddress (node) { // vG = (dG \+ sha256(e * dG)G) function stealthSend (e, Q) { - var eQ = ecc.pointMultiply(Q, e, true) // shared secret - var c = bitcoin.crypto.sha256(eQ) - var Qc = ecc.pointAddScalar(Q, c) - var vG = bitcoin.ECPair.fromPublicKey(Qc) + const eQ = ecc.pointMultiply(Q, e, true) // shared secret + const c = bitcoin.crypto.sha256(eQ) + const Qc = ecc.pointAddScalar(Q, c) + const vG = bitcoin.ECPair.fromPublicKey(Qc) return vG } // v = (d + sha256(eG * d)) function stealthReceive (d, eG) { - var eQ = ecc.pointMultiply(eG, d) // shared secret - var c = bitcoin.crypto.sha256(eQ) - var dc = ecc.privateAdd(d, c) - var v = bitcoin.ECPair.fromPrivateKey(dc) + const eQ = ecc.pointMultiply(eG, d) // shared secret + const c = bitcoin.crypto.sha256(eQ) + const dc = ecc.privateAdd(d, c) + const v = bitcoin.ECPair.fromPrivateKey(dc) return v } // d = (v - sha256(e * dG)) function stealthRecoverLeaked (v, e, Q) { - var eQ = ecc.pointMultiply(Q, e) // shared secret - var c = bitcoin.crypto.sha256(eQ) - var vc = ecc.privateSub(v, c) - var d = bitcoin.ECPair.fromPrivateKey(vc) + const eQ = ecc.pointMultiply(Q, e) // shared secret + const c = bitcoin.crypto.sha256(eQ) + const vc = ecc.privateSub(v, c) + const d = bitcoin.ECPair.fromPrivateKey(vc) return d } // vG = (rG \+ sha256(e * dG)G) function stealthDualSend (e, R, Q) { - var eQ = ecc.pointMultiply(Q, e) // shared secret - var c = bitcoin.crypto.sha256(eQ) - var Rc = ecc.pointAddScalar(R, c) - var vG = bitcoin.ECPair.fromPublicKey(Rc) + const eQ = ecc.pointMultiply(Q, e) // shared secret + const c = bitcoin.crypto.sha256(eQ) + const Rc = ecc.pointAddScalar(R, c) + const vG = bitcoin.ECPair.fromPublicKey(Rc) return vG } // vG = (rG \+ sha256(eG * d)G) function stealthDualScan (d, R, eG) { - var eQ = ecc.pointMultiply(eG, d) // shared secret - var c = bitcoin.crypto.sha256(eQ) - var Rc = ecc.pointAddScalar(R, c) - var vG = bitcoin.ECPair.fromPublicKey(Rc) + const eQ = ecc.pointMultiply(eG, d) // shared secret + const c = bitcoin.crypto.sha256(eQ) + const Rc = ecc.pointAddScalar(R, c) + const vG = bitcoin.ECPair.fromPublicKey(Rc) return vG } // v = (r + sha256(eG * d)) function stealthDualReceive (d, r, eG) { - var eQ = ecc.pointMultiply(eG, d) // shared secret - var c = bitcoin.crypto.sha256(eQ) - var rc = ecc.privateAdd(r, c) - var v = bitcoin.ECPair.fromPrivateKey(rc) + const eQ = ecc.pointMultiply(eG, d) // shared secret + const c = bitcoin.crypto.sha256(eQ) + const rc = ecc.privateAdd(r, c) + const v = bitcoin.ECPair.fromPrivateKey(rc) return v } @@ -74,16 +74,16 @@ function stealthDualReceive (d, r, eG) { describe('bitcoinjs-lib (crypto)', function () { it('can generate a single-key stealth address', function () { // XXX: should be randomly generated, see next test for example - var recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient - var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender + const recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient + const nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender // ... recipient reveals public key (recipient.Q) to sender - var forSender = stealthSend(nonce.privateKey, recipient.publicKey) + const forSender = stealthSend(nonce.privateKey, recipient.publicKey) assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient - var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) + const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n') @@ -92,15 +92,15 @@ describe('bitcoinjs-lib (crypto)', function () { }) it('can generate a single-key stealth address (randomly)', function () { - var recipient = bitcoin.ECPair.makeRandom() // private to recipient - var nonce = bitcoin.ECPair.makeRandom() // private to sender + const recipient = bitcoin.ECPair.makeRandom() // private to recipient + const nonce = bitcoin.ECPair.makeRandom() // private to sender // ... recipient reveals public key (recipient.Q) to sender - var forSender = stealthSend(nonce.privateKey, recipient.publicKey) + const forSender = stealthSend(nonce.privateKey, recipient.publicKey) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient - var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) + const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) assert.doesNotThrow(function () { forRecipient.toWIF() }) // sender and recipient, both derived same address @@ -108,38 +108,38 @@ describe('bitcoinjs-lib (crypto)', function () { }) it('can recover parent recipient.d, if a derived private key is leaked [and nonce was revealed]', function () { - var recipient = bitcoin.ECPair.makeRandom() // private to recipient - var nonce = bitcoin.ECPair.makeRandom() // private to sender + const recipient = bitcoin.ECPair.makeRandom() // private to recipient + const nonce = bitcoin.ECPair.makeRandom() // private to sender // ... recipient reveals public key (recipient.Q) to sender - var forSender = stealthSend(nonce.privateKey, recipient.publicKey) + const forSender = stealthSend(nonce.privateKey, recipient.publicKey) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to recipient - var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) + const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) assert.doesNotThrow(function () { forRecipient.toWIF() }) // ... recipient accidentally leaks forRecipient.d on the blockchain - var leaked = stealthRecoverLeaked(forRecipient.privateKey, nonce.privateKey, recipient.publicKey) + const leaked = stealthRecoverLeaked(forRecipient.privateKey, nonce.privateKey, recipient.publicKey) assert.equal(leaked.toWIF(), recipient.toWIF()) }) it('can generate a dual-key stealth address', function () { // XXX: should be randomly generated, see next test for example - var recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient - var scan = bitcoin.ECPair.fromWIF('L5DkCk3xLLoGKncqKsWQTdaPSR4V8gzc14WVghysQGkdryRudjBM') // private to scanner/recipient - var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender + const recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient + const scan = bitcoin.ECPair.fromWIF('L5DkCk3xLLoGKncqKsWQTdaPSR4V8gzc14WVghysQGkdryRudjBM') // private to scanner/recipient + const nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - var forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) + const forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to scanner - var forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) + const forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - var forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) + const forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) assert.doesNotThrow(function () { forRecipient.toWIF() }) // scanner, sender and recipient, all derived same address @@ -148,20 +148,20 @@ describe('bitcoinjs-lib (crypto)', function () { }) it('can generate a dual-key stealth address (randomly)', function () { - var recipient = bitcoin.ECPair.makeRandom() // private to recipient - var scan = bitcoin.ECPair.makeRandom() // private to scanner/recipient - var nonce = bitcoin.ECPair.makeRandom() // private to sender + const recipient = bitcoin.ECPair.makeRandom() // private to recipient + const scan = bitcoin.ECPair.makeRandom() // private to scanner/recipient + const nonce = bitcoin.ECPair.makeRandom() // private to sender // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - var forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) + const forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) // ... sender reveals nonce public key (nonce.Q) to scanner - var forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) + const forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - var forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) + const forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) assert.doesNotThrow(function () { forRecipient.toWIF() }) // scanner, sender and recipient, all derived same address diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 1e796a5..3235851 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -19,8 +19,8 @@ function rng () { describe('bitcoinjs-lib (transactions)', function () { it('can create a 1-to-1 Transaction', function () { - var alice = bitcoin.ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') - var txb = new bitcoin.TransactionBuilder() + const alice = bitcoin.ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') + const txb = new bitcoin.TransactionBuilder() txb.setVersion(1) txb.addInput('61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', 0) // Alice's previous transaction output, has 15000 satoshis @@ -34,10 +34,10 @@ describe('bitcoinjs-lib (transactions)', function () { }) it('can create a 2-to-2 Transaction', function () { - var alice = bitcoin.ECPair.fromWIF('L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1') - var bob = bitcoin.ECPair.fromWIF('KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z') + const alice = bitcoin.ECPair.fromWIF('L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1') + const bob = bitcoin.ECPair.fromWIF('KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z') - var txb = new bitcoin.TransactionBuilder() + const txb = new bitcoin.TransactionBuilder() txb.setVersion(1) txb.addInput('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c', 6) // Alice's previous transaction output, has 200000 satoshis txb.addInput('7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730', 0) // Bob's previous transaction output, has 300000 satoshis @@ -55,9 +55,9 @@ describe('bitcoinjs-lib (transactions)', function () { it('can create (and broadcast via 3PBP) a typical Transaction', function (done) { this.timeout(30000) - var alice1 = bitcoin.ECPair.makeRandom({ network: regtest }) - var alice2 = bitcoin.ECPair.makeRandom({ network: regtest }) - var aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng }) + const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }) + const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }) + const aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng }) // give Alice 2 unspent outputs regtestUtils.faucet(getAddress(alice1, regtest), 5e4, function (err, unspent0) { @@ -66,7 +66,7 @@ describe('bitcoinjs-lib (transactions)', function () { regtestUtils.faucet(getAddress(alice2, regtest), 7e4, function (err, unspent1) { if (err) return done(err) - var txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent0.txId, unspent0.vout) // alice1 unspent txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" @@ -87,14 +87,14 @@ describe('bitcoinjs-lib (transactions)', function () { it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', function (done) { this.timeout(30000) - var keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) regtestUtils.faucet(getAddress(keyPair, regtest), 2e5, function (err, unspent) { if (err) return done(err) - var txb = new bitcoin.TransactionBuilder(regtest) - var data = Buffer.from('bitcoinjs-lib', 'utf8') - var dataScript = bitcoin.script.nullData.output.encode([data]) + const txb = new bitcoin.TransactionBuilder(regtest) + const data = Buffer.from('bitcoinjs-lib', 'utf8') + const dataScript = bitcoin.script.nullData.output.encode([data]) txb.addInput(unspent.txId, unspent.vout) txb.addOutput(dataScript, 1000) @@ -117,20 +117,20 @@ describe('bitcoinjs-lib (transactions)', function () { ] const pubKeys = keyPairs.map(function (x) { return x.publicKey }) - var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) - var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) regtestUtils.faucet(address, 2e4, function (err, unspent) { if (err) return done(err) - var txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) txb.sign(0, keyPairs[0], redeemScript) txb.sign(0, keyPairs[2], redeemScript) - var tx = txb.build() + const tx = txb.build() // build and broadcast to the Bitcoin RegTest network regtestUtils.broadcast(tx.toHex(), function (err) { @@ -152,20 +152,20 @@ describe('bitcoinjs-lib (transactions)', function () { const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const pubKeyHash = bitcoin.crypto.hash160(keyPair.publicKey) - var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash) - var redeemScriptHash = bitcoin.crypto.hash160(redeemScript) - var scriptPubKey = bitcoin.script.scriptHash.output.encode(redeemScriptHash) - var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash) + const redeemScriptHash = bitcoin.crypto.hash160(redeemScript) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(redeemScriptHash) + const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) regtestUtils.faucet(address, 5e4, function (err, unspent) { if (err) return done(err) - var txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) txb.sign(0, keyPair, redeemScript, null, unspent.value) - var tx = txb.build() + const tx = txb.build() // build and broadcast to the Bitcoin RegTest network regtestUtils.broadcast(tx.toHex(), function (err) { @@ -192,22 +192,22 @@ describe('bitcoinjs-lib (transactions)', function () { ] const pubKeys = keyPairs.map(function (x) { return x.publicKey }) - var witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) - var redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) - var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - var address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) + const redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) + const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) + const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) regtestUtils.faucet(address, 6e4, function (err, unspent) { if (err) return done(err) - var txb = new bitcoin.TransactionBuilder(regtest) + const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4) txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript) txb.sign(0, keyPairs[2], redeemScript, null, unspent.value, witnessScript) txb.sign(0, keyPairs[3], redeemScript, null, unspent.value, witnessScript) - var tx = txb.build() + const tx = txb.build() // build and broadcast to the Bitcoin RegTest network regtestUtils.broadcast(tx.toHex(), function (err) { @@ -224,21 +224,21 @@ describe('bitcoinjs-lib (transactions)', function () { }) it('can verify Transaction signatures', function () { - var txHex = '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700' - var keyPairs = [ + const txHex = '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700' + const keyPairs = [ '032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d', '0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a', '039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f' ].map(function (q) { return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')) }) - var tx = bitcoin.Transaction.fromHex(txHex) + const tx = bitcoin.Transaction.fromHex(txHex) tx.ins.forEach(function (input, i) { - var keyPair = keyPairs[i] - var prevOutScript = bitcoin.address.toOutputScript(getAddress(keyPair)) - var scriptSig = bitcoin.script.pubKeyHash.input.decode(input.script) - var ss = bitcoin.script.signature.decode(scriptSig.signature) - var hash = tx.hashForSignature(i, prevOutScript, ss.hashType) + const keyPair = keyPairs[i] + const prevOutScript = bitcoin.address.toOutputScript(getAddress(keyPair)) + const scriptSig = bitcoin.script.pubKeyHash.input.decode(input.script) + const ss = bitcoin.script.signature.decode(scriptSig.signature) + const hash = tx.hashForSignature(i, prevOutScript, ss.hashType) assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.publicKey.toString('hex')) assert.strictEqual(keyPair.verify(hash, ss.signature), true) diff --git a/test/script.js b/test/script.js index 71c9d72..269e18a 100644 --- a/test/script.js +++ b/test/script.js @@ -25,7 +25,7 @@ describe('script', function () { describe('fromASM/toASM', function () { fixtures.valid.forEach(function (f) { it('encodes/decodes ' + f.asm, function () { - var script = bscript.fromASM(f.asm) + const script = bscript.fromASM(f.asm) assert.strictEqual(bscript.toASM(script), f.asm) }) }) @@ -42,10 +42,10 @@ describe('script', function () { describe('fromASM/toASM (templates)', function () { fixtures2.valid.forEach(function (f) { if (f.inputHex) { - var ih = bscript.toASM(Buffer.from(f.inputHex, 'hex')) + const ih = bscript.toASM(Buffer.from(f.inputHex, 'hex')) it('encodes/decodes ' + ih, function () { - var script = bscript.fromASM(f.input) + const script = bscript.fromASM(f.input) assert.strictEqual(script.toString('hex'), f.inputHex) assert.strictEqual(bscript.toASM(script), f.input) }) @@ -53,7 +53,7 @@ describe('script', function () { if (f.outputHex) { it('encodes/decodes ' + f.output, function () { - var script = bscript.fromASM(f.output) + const script = bscript.fromASM(f.output) assert.strictEqual(script.toString('hex'), f.outputHex) assert.strictEqual(bscript.toASM(script), f.output) }) @@ -64,8 +64,8 @@ describe('script', function () { describe('isPushOnly', function () { fixtures.valid.forEach(function (f) { it('returns ' + !!f.stack + ' for ' + f.asm, function () { - var script = bscript.fromASM(f.asm) - var chunks = bscript.decompile(script) + const script = bscript.fromASM(f.asm) + const chunks = bscript.decompile(script) assert.strictEqual(bscript.isPushOnly(chunks), !!f.stack) }) @@ -77,9 +77,9 @@ describe('script', function () { it('returns ' + !!f.stack + ' for ' + f.asm, function () { if (!f.stack || !f.asm) return - var script = bscript.fromASM(f.asm) + const script = bscript.fromASM(f.asm) - var stack = bscript.toStack(script) + const stack = bscript.toStack(script) assert.deepEqual(stack.map(function (x) { return x.toString('hex') }), f.stack) @@ -92,12 +92,12 @@ describe('script', function () { describe('compile (via fromASM)', function () { fixtures.valid.forEach(function (f) { it('(' + f.type + ') compiles ' + f.asm, function () { - var scriptSig = bscript.fromASM(f.asm) + const scriptSig = bscript.fromASM(f.asm) assert.strictEqual(scriptSig.toString('hex'), f.script) if (f.nonstandard) { - var scriptSigNS = bscript.fromASM(f.nonstandard.scriptSig) + const scriptSigNS = bscript.fromASM(f.nonstandard.scriptSig) assert.strictEqual(scriptSigNS.toString('hex'), f.script) } @@ -108,13 +108,13 @@ describe('script', function () { describe('decompile', function () { fixtures.valid.forEach(function (f) { it('decompiles ' + f.asm, function () { - var chunks = bscript.decompile(Buffer.from(f.script, 'hex')) + const chunks = bscript.decompile(Buffer.from(f.script, 'hex')) assert.strictEqual(bscript.compile(chunks).toString('hex'), f.script) assert.strictEqual(bscript.toASM(chunks), f.asm) if (f.nonstandard) { - var chunksNS = bscript.decompile(Buffer.from(f.nonstandard.scriptSigHex, 'hex')) + const chunksNS = bscript.decompile(Buffer.from(f.nonstandard.scriptSigHex, 'hex')) assert.strictEqual(bscript.compile(chunksNS).toString('hex'), f.script) @@ -126,7 +126,7 @@ describe('script', function () { fixtures.invalid.decompile.forEach(function (f) { it('fails to decompile ' + f.script + ', because "' + f.description + '"', function () { - var chunks = bscript.decompile(Buffer.from(f.script, 'hex')) + const chunks = bscript.decompile(Buffer.from(f.script, 'hex')) assert.strictEqual(chunks, null) }) @@ -136,7 +136,7 @@ describe('script', function () { describe('SCRIPT_VERIFY_MINIMALDATA policy', function () { fixtures.valid.forEach(function (f) { it('compliant for ' + f.type + ' scriptSig ' + f.asm, function () { - var script = Buffer.from(f.script, 'hex') + const script = Buffer.from(f.script, 'hex') assert(minimalData(script)) }) @@ -144,8 +144,8 @@ describe('script', function () { function testEncodingForSize (i) { it('compliant for data PUSH of length ' + i, function () { - var buffer = Buffer.alloc(i) - var script = bscript.compile([buffer]) + const buffer = Buffer.alloc(i) + const script = bscript.compile([buffer]) assert(minimalData(script), 'Failed for ' + i + ' length script: ' + script.toString('hex')) }) diff --git a/test/script_number.js b/test/script_number.js index 9eb0820..d217ff1 100644 --- a/test/script_number.js +++ b/test/script_number.js @@ -8,7 +8,7 @@ describe('script-number', function () { describe('decode', function () { fixtures.forEach(function (f) { it(f.hex + ' returns ' + f.number, function () { - var actual = scriptNumber.decode(Buffer.from(f.hex, 'hex'), f.bytes) + const actual = scriptNumber.decode(Buffer.from(f.hex, 'hex'), f.bytes) assert.strictEqual(actual, f.number) }) @@ -18,7 +18,7 @@ describe('script-number', function () { describe('encode', function () { fixtures.forEach(function (f) { it(f.number + ' returns ' + f.hex, function () { - var actual = scriptNumber.encode(f.number) + const actual = scriptNumber.encode(f.number) assert.strictEqual(actual.toString('hex'), f.hex) }) diff --git a/test/script_signature.js b/test/script_signature.js index 2c22540..9908ebc 100644 --- a/test/script_signature.js +++ b/test/script_signature.js @@ -23,7 +23,7 @@ describe('Script Signatures', function () { describe('encode', function () { fixtures.valid.forEach(function (f) { it('encodes ' + f.hex, function () { - var buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType) + const buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType) assert.strictEqual(buffer.toString('hex'), f.hex) }) @@ -33,7 +33,7 @@ describe('Script Signatures', function () { if (!f.raw) return it('throws ' + f.exception, function () { - var signature = fromRaw(f.raw) + const signature = fromRaw(f.raw) assert.throws(function () { bscriptSig.encode(signature, f.hashType) @@ -45,7 +45,7 @@ describe('Script Signatures', function () { describe('decode', function () { fixtures.valid.forEach(function (f) { it('decodes ' + f.hex, function () { - var decode = bscriptSig.decode(Buffer.from(f.hex, 'hex')) + const decode = bscriptSig.decode(Buffer.from(f.hex, 'hex')) assert.deepEqual(toRaw(decode.signature), f.raw) assert.strictEqual(decode.hashType, f.hashType) @@ -54,7 +54,7 @@ describe('Script Signatures', function () { fixtures.invalid.forEach(function (f) { it('throws on ' + f.hex, function () { - var buffer = Buffer.from(f.hex, 'hex') + const buffer = Buffer.from(f.hex, 'hex') assert.throws(function () { bscriptSig.decode(buffer) diff --git a/test/templates.js b/test/templates.js index a850564..7058c51 100644 --- a/test/templates.js +++ b/test/templates.js @@ -17,8 +17,8 @@ describe('script-templates', function () { if (!f.input) return it('classifies ' + f.input + ' as ' + f.type, function () { - var input = bscript.fromASM(f.input) - var type = btemplates.classifyInput(input) + const input = bscript.fromASM(f.input) + const type = btemplates.classifyInput(input) assert.strictEqual(type, f.type) }) @@ -29,8 +29,8 @@ describe('script-templates', function () { if (!f.typeIncomplete) return it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, function () { - var input = bscript.fromASM(f.input) - var type = btemplates.classifyInput(input, true) + const input = bscript.fromASM(f.input) + const type = btemplates.classifyInput(input, true) assert.strictEqual(type, f.typeIncomplete) }) @@ -42,8 +42,8 @@ describe('script-templates', function () { if (!f.output) return it('classifies ' + f.output + ' as ' + f.type, function () { - var output = bscript.fromASM(f.output) - var type = btemplates.classifyOutput(output) + const output = bscript.fromASM(f.output) + const type = btemplates.classifyOutput(output) assert.strictEqual(type, f.type) }) @@ -60,24 +60,24 @@ describe('script-templates', function () { 'nullData', 'witnessCommitment' ].forEach(function (name) { - var inputType = btemplates[name].input - var outputType = btemplates[name].output + const inputType = btemplates[name].input + const outputType = btemplates[name].output describe(name + '.input.check', function () { fixtures.valid.forEach(function (f) { if (name.toLowerCase() === btemplates.types.P2WPKH) return if (name.toLowerCase() === btemplates.types.P2WSH) return - var expected = name.toLowerCase() === f.type.toLowerCase() + const expected = name.toLowerCase() === f.type.toLowerCase() if (inputType && f.input) { - var input = bscript.fromASM(f.input) + const input = bscript.fromASM(f.input) it('returns ' + expected + ' for ' + f.input, function () { assert.strictEqual(inputType.check(input), expected) }) if (f.typeIncomplete) { - var expectedIncomplete = name.toLowerCase() === f.typeIncomplete + const expectedIncomplete = name.toLowerCase() === f.typeIncomplete it('returns ' + expected + ' for ' + f.input, function () { assert.strictEqual(inputType.check(input, true), expectedIncomplete) @@ -92,7 +92,7 @@ describe('script-templates', function () { if (!f.input && !f.inputHex) return it('returns false for ' + f.description + ' (' + (f.input || f.inputHex) + ')', function () { - var input + let input if (f.input) { input = bscript.fromASM(f.input) @@ -107,11 +107,11 @@ describe('script-templates', function () { describe(name + '.output.check', function () { fixtures.valid.forEach(function (f) { - var expected = name.toLowerCase() === f.type + const expected = name.toLowerCase() === f.type if (outputType && f.output) { it('returns ' + expected + ' for ' + f.output, function () { - var output = bscript.fromASM(f.output) + const output = bscript.fromASM(f.output) if (name.toLowerCase() === 'nulldata' && f.type === btemplates.types.WITNESS_COMMITMENT) return if (name.toLowerCase() === 'witnesscommitment' && f.type === btemplates.types.NULLDATA) return @@ -126,7 +126,7 @@ describe('script-templates', function () { if (!f.output && !f.outputHex) return it('returns false for ' + f.description + ' (' + (f.output || f.outputHex) + ')', function () { - var output + let output if (f.output) { output = bscript.fromASM(f.output) @@ -144,8 +144,8 @@ describe('script-templates', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkey') return - var signature = Buffer.from(f.signature, 'hex') - var input = btemplates.pubKey.input.encode(signature) + const signature = Buffer.from(f.signature, 'hex') + const input = btemplates.pubKey.input.encode(signature) it('encodes to ' + f.input, function () { assert.strictEqual(bscript.toASM(input), f.input) @@ -161,8 +161,8 @@ describe('script-templates', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkey') return - var pubKey = Buffer.from(f.pubKey, 'hex') - var output = btemplates.pubKey.output.encode(pubKey) + const pubKey = Buffer.from(f.pubKey, 'hex') + const output = btemplates.pubKey.output.encode(pubKey) it('encodes to ' + f.output, function () { assert.strictEqual(bscript.toASM(output), f.output) @@ -178,9 +178,9 @@ describe('script-templates', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkeyhash') return - var pubKey = Buffer.from(f.pubKey, 'hex') - var signature = Buffer.from(f.signature, 'hex') - var input = btemplates.pubKeyHash.input.encode(signature, pubKey) + const pubKey = Buffer.from(f.pubKey, 'hex') + const signature = Buffer.from(f.signature, 'hex') + const input = btemplates.pubKeyHash.input.encode(signature, pubKey) it('encodes to ' + f.input, function () { assert.strictEqual(bscript.toASM(input), f.input) @@ -199,9 +199,9 @@ describe('script-templates', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkeyhash') return - var pubKey = Buffer.from(f.pubKey, 'hex') - var pubKeyHash = bcrypto.hash160(pubKey) - var output = btemplates.pubKeyHash.output.encode(pubKeyHash) + const pubKey = Buffer.from(f.pubKey, 'hex') + const pubKeyHash = bcrypto.hash160(pubKey) + const output = btemplates.pubKeyHash.output.encode(pubKeyHash) it('encodes to ' + f.output, function () { assert.strictEqual(bscript.toASM(output), f.output) @@ -214,7 +214,7 @@ describe('script-templates', function () { fixtures.invalid.pubKeyHash.outputs.forEach(function (f) { if (!f.hash) return - var hash = Buffer.from(f.hash, 'hex') + const hash = Buffer.from(f.hash, 'hex') it('throws on ' + f.exception, function () { assert.throws(function () { @@ -227,13 +227,13 @@ describe('script-templates', function () { describe('multisig.input', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'multisig' && f.typeIncomplete !== 'multisig') return - var allowIncomplete = f.typeIncomplete !== undefined + const allowIncomplete = f.typeIncomplete !== undefined - var signatures = f.signatures.map(function (signature) { + const signatures = f.signatures.map(function (signature) { return signature ? Buffer.from(signature, 'hex') : ops.OP_0 }) - var input = btemplates.multisig.input.encode(signatures) + const input = btemplates.multisig.input.encode(signatures) it('encodes to ' + f.input, function () { assert.strictEqual(bscript.toASM(input), f.input) @@ -246,10 +246,10 @@ describe('script-templates', function () { fixtures.invalid.multisig.inputs.forEach(function (f) { if (!f.output) return - var output = bscript.fromASM(f.output) + const output = bscript.fromASM(f.output) it('throws on ' + f.exception, function () { - var signatures = f.signatures.map(function (signature) { + const signatures = f.signatures.map(function (signature) { return signature ? Buffer.from(signature, 'hex') : ops.OP_0 }) @@ -264,10 +264,10 @@ describe('script-templates', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'multisig') return - var pubKeys = f.pubKeys.map(function (p) { return Buffer.from(p, 'hex') }) - var m = pubKeys.length + const pubKeys = f.pubKeys.map(function (p) { return Buffer.from(p, 'hex') }) + const m = pubKeys.length - var output = btemplates.multisig.output.encode(m, pubKeys) + const output = btemplates.multisig.output.encode(m, pubKeys) it('encodes ' + f.output, function () { assert.strictEqual(bscript.toASM(output), f.output) @@ -283,7 +283,7 @@ describe('script-templates', function () { fixtures.invalid.multisig.outputs.forEach(function (f) { if (!f.pubKeys) return - var pubKeys = f.pubKeys.map(function (p) { + const pubKeys = f.pubKeys.map(function (p) { return Buffer.from(p, 'hex') }) @@ -299,9 +299,9 @@ describe('script-templates', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'scripthash') return - var redeemScriptSig = bscript.fromASM(f.redeemScriptSig) - var redeemScript = bscript.fromASM(f.redeemScript) - var input = btemplates.scriptHash.input.encode(redeemScriptSig, redeemScript) + const redeemScriptSig = bscript.fromASM(f.redeemScriptSig) + const redeemScript = bscript.fromASM(f.redeemScript) + const input = btemplates.scriptHash.input.encode(redeemScriptSig, redeemScript) it('encodes to ' + f.output, function () { if (f.input) { @@ -325,9 +325,9 @@ describe('script-templates', function () { if (f.type !== 'scripthash') return if (!f.output) return - var redeemScript = bscript.fromASM(f.redeemScript) - var scriptHash = bcrypto.hash160(redeemScript) - var output = btemplates.scriptHash.output.encode(scriptHash) + const redeemScript = bscript.fromASM(f.redeemScript) + const scriptHash = bcrypto.hash160(redeemScript) + const output = btemplates.scriptHash.output.encode(scriptHash) it('encodes to ' + f.output, function () { assert.strictEqual(bscript.toASM(output), f.output) @@ -340,7 +340,7 @@ describe('script-templates', function () { fixtures.invalid.scriptHash.outputs.forEach(function (f) { if (!f.hash) return - var hash = Buffer.from(f.hash, 'hex') + const hash = Buffer.from(f.hash, 'hex') it('throws on ' + f.exception, function () { assert.throws(function () { @@ -355,17 +355,17 @@ describe('script-templates', function () { if (f.type !== 'pubkeyhash' && f.type !== 'witnesspubkeyhash') return if (!f.inputStack) return - var pubKey = Buffer.from(f.pubKey, 'hex') - var signature = Buffer.from(f.signature, 'hex') + const pubKey = Buffer.from(f.pubKey, 'hex') + const signature = Buffer.from(f.signature, 'hex') it('encodes to ' + f.input, function () { - var inputStack = btemplates.witnessPubKeyHash.input.encodeStack(signature, pubKey) + const inputStack = btemplates.witnessPubKeyHash.input.encodeStack(signature, pubKey) assert.deepEqual(inputStack.map(toHex), f.inputStack) }) it('decodes to original arguments', function () { - var fInputStack = f.inputStack.map(fromHex) + const fInputStack = f.inputStack.map(fromHex) assert.deepEqual(btemplates.witnessPubKeyHash.input.decodeStack(fInputStack), { signature: signature, @@ -380,9 +380,9 @@ describe('script-templates', function () { if (f.type !== 'witnesspubkeyhash') return if (!f.output) return - var pubKey = Buffer.from(f.pubKey, 'hex') - var pubKeyHash = bcrypto.hash160(pubKey) - var output = btemplates.witnessPubKeyHash.output.encode(pubKeyHash) + const pubKey = Buffer.from(f.pubKey, 'hex') + const pubKeyHash = bcrypto.hash160(pubKey) + const output = btemplates.witnessPubKeyHash.output.encode(pubKeyHash) it('encodes to ' + f.output, function () { assert.strictEqual(bscript.toASM(output), f.output) @@ -395,7 +395,7 @@ describe('script-templates', function () { fixtures.invalid.witnessPubKeyHash.outputs.forEach(function (f) { if (!f.hash) return - var hash = Buffer.from(f.hash, 'hex') + const hash = Buffer.from(f.hash, 'hex') it('throws on ' + f.exception, function () { assert.throws(function () { @@ -410,17 +410,17 @@ describe('script-templates', function () { if (f.type !== 'witnessscripthash') return if (!f.inputStack || !f.witnessData) return - var witnessData = f.witnessData.map(fromHex) - var witnessScript = bscript.fromASM(f.witnessScript || f.redeemScript) + const witnessData = f.witnessData.map(fromHex) + const witnessScript = bscript.fromASM(f.witnessScript || f.redeemScript) it('encodes to ' + f.input, function () { - var inputStack = btemplates.witnessScriptHash.input.encodeStack(witnessData, witnessScript) + const inputStack = btemplates.witnessScriptHash.input.encodeStack(witnessData, witnessScript) assert.deepEqual(inputStack.map(toHex), f.inputStack) }) it('decodes to original arguments', function () { - var result = btemplates.witnessScriptHash.input.decodeStack(f.inputStack.map(fromHex)) + const result = btemplates.witnessScriptHash.input.decodeStack(f.inputStack.map(fromHex)) assert.deepEqual(result.witnessData.map(toHex), f.witnessData) assert.strictEqual(bscript.toASM(result.witnessScript), f.witnessScript) @@ -433,9 +433,9 @@ describe('script-templates', function () { if (f.type !== 'witnessscripthash') return if (!f.output) return - var witnessScriptPubKey = bscript.fromASM(f.witnessScript) - var scriptHash = bcrypto.hash256(witnessScriptPubKey) - var output = btemplates.witnessScriptHash.output.encode(scriptHash) + const witnessScriptPubKey = bscript.fromASM(f.witnessScript) + const scriptHash = bcrypto.hash256(witnessScriptPubKey) + const output = btemplates.witnessScriptHash.output.encode(scriptHash) it('encodes to ' + f.output, function () { assert.strictEqual(bscript.toASM(output), f.output) @@ -448,7 +448,7 @@ describe('script-templates', function () { fixtures.invalid.witnessScriptHash.outputs.forEach(function (f) { if (!f.hash) return - var hash = Buffer.from(f.hash, 'hex') + const hash = Buffer.from(f.hash, 'hex') it('throws on ' + f.exception, function () { assert.throws(function () { @@ -463,8 +463,8 @@ describe('script-templates', function () { if (f.type !== 'witnesscommitment') return if (!f.scriptPubKey) return - var commitment = Buffer.from(f.witnessCommitment, 'hex') - var scriptPubKey = btemplates.witnessCommitment.output.encode(commitment) + const commitment = Buffer.from(f.witnessCommitment, 'hex') + const scriptPubKey = btemplates.witnessCommitment.output.encode(commitment) it('encodes to ' + f.scriptPubKey, function () { assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) @@ -477,7 +477,7 @@ describe('script-templates', function () { fixtures.invalid.witnessCommitment.outputs.forEach(function (f) { if (f.commitment) { - var hash = Buffer.from(f.commitment, 'hex') + const hash = Buffer.from(f.commitment, 'hex') it('throws on bad encode data', function () { assert.throws(function () { btemplates.witnessCommitment.output.encode(hash) @@ -499,8 +499,8 @@ describe('script-templates', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'nulldata') return - var data = f.data.map(function (x) { return Buffer.from(x, 'hex') }) - var output = btemplates.nullData.output.encode(data) + const data = f.data.map(function (x) { return Buffer.from(x, 'hex') }) + const output = btemplates.nullData.output.encode(data) it('encodes to ' + f.output, function () { assert.strictEqual(bscript.toASM(output), f.output) diff --git a/test/transaction.js b/test/transaction.js index fa69640..e249631 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -7,13 +7,13 @@ const Transaction = require('../src/transaction') describe('Transaction', function () { function fromRaw (raw, noWitness) { - var tx = new Transaction() + const tx = new Transaction() tx.version = raw.version tx.locktime = raw.locktime raw.ins.forEach(function (txIn, i) { - var txHash = Buffer.from(txIn.hash, 'hex') - var scriptSig + const txHash = Buffer.from(txIn.hash, 'hex') + let scriptSig if (txIn.data) { scriptSig = Buffer.from(txIn.data, 'hex') @@ -24,7 +24,7 @@ describe('Transaction', function () { tx.addInput(txHash, txIn.index, txIn.sequence, scriptSig) if (!noWitness && txIn.witness) { - var witness = txIn.witness.map(function (x) { + const witness = txIn.witness.map(function (x) { return Buffer.from(x, 'hex') }) @@ -33,7 +33,7 @@ describe('Transaction', function () { }) raw.outs.forEach(function (txOut) { - var script + let script if (txOut.data) { script = Buffer.from(txOut.data, 'hex') @@ -49,18 +49,18 @@ describe('Transaction', function () { describe('fromBuffer/fromHex', function () { function importExport (f) { - var id = f.id || f.hash - var txHex = f.hex || f.txHex + const id = f.id || f.hash + const txHex = f.hex || f.txHex it('imports ' + f.description + ' (' + id + ')', function () { - var actual = Transaction.fromHex(txHex) + const actual = Transaction.fromHex(txHex) assert.strictEqual(actual.toHex(), txHex) }) if (f.whex) { it('imports ' + f.description + ' (' + id + ') as witness', function () { - var actual = Transaction.fromHex(f.whex) + const actual = Transaction.fromHex(f.whex) assert.strictEqual(actual.toHex(), f.whex) }) @@ -80,8 +80,8 @@ describe('Transaction', function () { }) it('.version should be interpreted as an int32le', function () { - var txHex = 'ffffffff0000ffffffff' - var tx = Transaction.fromHex(txHex) + const txHex = 'ffffffff0000ffffffff' + const tx = Transaction.fromHex(txHex) assert.equal(-1, tx.version) assert.equal(0xffffffff, tx.locktime) }) @@ -90,26 +90,26 @@ describe('Transaction', function () { describe('toBuffer/toHex', function () { fixtures.valid.forEach(function (f) { it('exports ' + f.description + ' (' + f.id + ')', function () { - var actual = fromRaw(f.raw, true) + const actual = fromRaw(f.raw, true) assert.strictEqual(actual.toHex(), f.hex) }) if (f.whex) { it('exports ' + f.description + ' (' + f.id + ') as witness', function () { - var wactual = fromRaw(f.raw) + const wactual = fromRaw(f.raw) assert.strictEqual(wactual.toHex(), f.whex) }) } }) it('accepts target Buffer and offset parameters', function () { - var f = fixtures.valid[0] - var actual = fromRaw(f.raw) - var byteLength = actual.byteLength() + const f = fixtures.valid[0] + const actual = fromRaw(f.raw) + const byteLength = actual.byteLength() - var target = Buffer.alloc(byteLength * 2) - var a = actual.toBuffer(target, 0) - var b = actual.toBuffer(target, byteLength) + const target = Buffer.alloc(byteLength * 2) + const a = actual.toBuffer(target, 0) + const b = actual.toBuffer(target, byteLength) assert.strictEqual(a.length, byteLength) assert.strictEqual(b.length, byteLength) @@ -132,7 +132,7 @@ describe('Transaction', function () { describe('weight/virtualSize', function () { it('computes virtual size', function () { fixtures.valid.forEach(function (f) { - var transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) + const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) assert.strictEqual(transaction.virtualSize(), f.virtualSize) }) @@ -140,7 +140,7 @@ describe('Transaction', function () { it('computes weight', function () { fixtures.valid.forEach(function (f) { - var transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) + const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) assert.strictEqual(transaction.weight(), f.weight) }) @@ -148,19 +148,19 @@ describe('Transaction', function () { }) describe('addInput', function () { - var prevTxHash + let prevTxHash beforeEach(function () { prevTxHash = Buffer.from('ffffffff00ffff000000000000000000000000000000000000000000101010ff', 'hex') }) it('returns an index', function () { - var tx = new Transaction() + const tx = new Transaction() assert.strictEqual(tx.addInput(prevTxHash, 0), 0) assert.strictEqual(tx.addInput(prevTxHash, 0), 1) }) it('defaults to empty script, witness and 0xffffffff SEQUENCE number', function () { - var tx = new Transaction() + const tx = new Transaction() tx.addInput(prevTxHash, 0) assert.strictEqual(tx.ins[0].script.length, 0) @@ -170,8 +170,8 @@ describe('Transaction', function () { fixtures.invalid.addInput.forEach(function (f) { it('throws on ' + f.exception, function () { - var tx = new Transaction() - var hash = Buffer.from(f.hash, 'hex') + const tx = new Transaction() + const hash = Buffer.from(f.hash, 'hex') assert.throws(function () { tx.addInput(hash, f.index) @@ -182,7 +182,7 @@ describe('Transaction', function () { describe('addOutput', function () { it('returns an index', function () { - var tx = new Transaction() + const tx = new Transaction() assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 0) assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 1) }) @@ -190,7 +190,8 @@ describe('Transaction', function () { describe('clone', function () { fixtures.valid.forEach(function (f) { - var actual, expected + let actual + let expected beforeEach(function () { expected = Transaction.fromHex(f.hex) @@ -210,7 +211,7 @@ describe('Transaction', function () { describe('getHash/getId', function () { function verify (f) { it('should return the id for ' + f.id + '(' + f.description + ')', function () { - var tx = Transaction.fromHex(f.whex || f.hex) + const tx = Transaction.fromHex(f.whex || f.hex) assert.strictEqual(tx.getHash().toString('hex'), f.hash) assert.strictEqual(tx.getId(), f.id) @@ -223,7 +224,7 @@ describe('Transaction', function () { describe('isCoinbase', function () { function verify (f) { it('should return ' + f.coinbase + ' for ' + f.id + '(' + f.description + ')', function () { - var tx = Transaction.fromHex(f.hex) + const tx = Transaction.fromHex(f.hex) assert.strictEqual(tx.isCoinbase(), f.coinbase) }) @@ -234,13 +235,13 @@ describe('Transaction', function () { describe('hashForSignature', function () { it('does not use Witness serialization', function () { - var randScript = Buffer.from('6a', 'hex') + const randScript = Buffer.from('6a', 'hex') - var tx = new Transaction() + const tx = new Transaction() tx.addInput(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'), 0) tx.addOutput(randScript, 5000000000) - var original = tx.__toBuffer + const original = tx.__toBuffer tx.__toBuffer = function (a, b, c) { if (c !== false) throw new Error('hashForSignature MUST pass false') @@ -259,8 +260,8 @@ describe('Transaction', function () { fixtures.hashForSignature.forEach(function (f) { it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : f.script), function () { - var tx = Transaction.fromHex(f.txHex) - var script = bscript.fromASM(f.script) + const tx = Transaction.fromHex(f.txHex) + const script = bscript.fromASM(f.script) assert.strictEqual(tx.hashForSignature(f.inIndex, script, f.type).toString('hex'), f.hash) }) @@ -270,8 +271,8 @@ describe('Transaction', function () { describe('hashForWitnessV0', function () { fixtures.hashForWitnessV0.forEach(function (f) { it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : ''), function () { - var tx = Transaction.fromHex(f.txHex) - var script = bscript.fromASM(f.script) + const tx = Transaction.fromHex(f.txHex) + const script = bscript.fromASM(f.script) assert.strictEqual(tx.hashForWitnessV0(f.inIndex, script, f.value, f.type).toString('hex'), f.hash) }) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index cfb6671..e4b5e60 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -20,16 +20,16 @@ function getAddress (node) { } function construct (f, dontSign) { - var network = NETWORKS[f.network] - var txb = new TransactionBuilder(network) + const network = NETWORKS[f.network] + let txb = new TransactionBuilder(network) if (Number.isFinite(f.version)) txb.setVersion(f.version) if (f.locktime !== undefined) txb.setLockTime(f.locktime) f.inputs.forEach(function (input) { - var prevTx + let prevTx if (input.txRaw) { - var constructed = construct(input.txRaw) + const constructed = construct(input.txRaw) if (input.txRaw.incomplete) prevTx = constructed.buildIncomplete() else prevTx = constructed.build() } else if (input.txHex) { @@ -38,7 +38,7 @@ function construct (f, dontSign) { prevTx = input.txId } - var prevTxScript + let prevTxScript if (input.prevTxScript) { prevTxScript = bscript.fromASM(input.prevTxScript) } @@ -56,27 +56,31 @@ function construct (f, dontSign) { if (dontSign) return txb - var stages = f.stages && f.stages.concat() + const stages = f.stages && f.stages.concat() f.inputs.forEach(function (input, index) { if (!input.signs) return input.signs.forEach(function (sign) { - var keyPair = ECPair.fromWIF(sign.keyPair, network) - var redeemScript - var witnessScript - var value + const keyPair = ECPair.fromWIF(sign.keyPair, network) + let redeemScript + let witnessScript + let value + if (sign.redeemScript) { redeemScript = bscript.fromASM(sign.redeemScript) } + if (sign.value) { value = sign.value } + if (sign.witnessScript) { witnessScript = bscript.fromASM(sign.witnessScript) } + txb.sign(index, keyPair, redeemScript, sign.hashType, value, witnessScript) if (sign.stage) { - var tx = txb.buildIncomplete() + const tx = txb.buildIncomplete() assert.strictEqual(tx.toHex(), stages.shift()) txb = TransactionBuilder.fromTransaction(tx, network) } @@ -88,23 +92,23 @@ function construct (f, dontSign) { describe('TransactionBuilder', function () { // constants - var keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) - var scripts = [ + const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) + const scripts = [ '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP' ].map(function (x) { return baddress.toOutputScript(x) }) - var txHash = Buffer.from('0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', 'hex') + const txHash = Buffer.from('0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', 'hex') describe('fromTransaction', function () { fixtures.valid.build.forEach(function (f) { it('returns TransactionBuilder, with ' + f.description, function () { - var network = NETWORKS[f.network || 'bitcoin'] + const network = NETWORKS[f.network || 'bitcoin'] - var tx = Transaction.fromHex(f.txHex) - var txb = TransactionBuilder.fromTransaction(tx, network) - var txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + const tx = Transaction.fromHex(f.txHex) + const txb = TransactionBuilder.fromTransaction(tx, network) + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() assert.strictEqual(txAfter.toHex(), f.txHex) assert.strictEqual(txb.network, network) @@ -113,10 +117,10 @@ describe('TransactionBuilder', function () { fixtures.valid.fromTransaction.forEach(function (f) { it('returns TransactionBuilder, with ' + f.description, function () { - var tx = new Transaction() + const tx = new Transaction() f.inputs.forEach(function (input) { - var txHash2 = Buffer.from(input.txId, 'hex').reverse() + const txHash2 = Buffer.from(input.txId, 'hex').reverse() tx.addInput(txHash2, input.vout, undefined, bscript.fromASM(input.scriptSig)) }) @@ -125,8 +129,8 @@ describe('TransactionBuilder', function () { tx.addOutput(bscript.fromASM(output.script), output.value) }) - var txb = TransactionBuilder.fromTransaction(tx) - var txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + const txb = TransactionBuilder.fromTransaction(tx) + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() txAfter.ins.forEach(function (input, i) { assert.equal(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) @@ -139,8 +143,8 @@ describe('TransactionBuilder', function () { }) it('classifies transaction inputs', function () { - var tx = Transaction.fromHex(fixtures.valid.classification.hex) - var txb = TransactionBuilder.fromTransaction(tx) + const tx = Transaction.fromHex(fixtures.valid.classification.hex) + const txb = TransactionBuilder.fromTransaction(tx) txb.__inputs.forEach(function (i) { assert.strictEqual(i.prevOutType, 'scripthash') @@ -151,7 +155,7 @@ describe('TransactionBuilder', function () { fixtures.invalid.fromTransaction.forEach(function (f) { it('throws ' + f.exception, function () { - var tx = Transaction.fromHex(f.txHex) + const tx = Transaction.fromHex(f.txHex) assert.throws(function () { TransactionBuilder.fromTransaction(tx) @@ -161,16 +165,16 @@ describe('TransactionBuilder', function () { }) describe('addInput', function () { - var txb + let txb beforeEach(function () { txb = new TransactionBuilder() }) it('accepts a txHash, index [and sequence number]', function () { - var vin = txb.addInput(txHash, 1, 54) + const vin = txb.addInput(txHash, 1, 54) assert.strictEqual(vin, 0) - var txIn = txb.__tx.ins[0] + const txIn = txb.__tx.ins[0] assert.strictEqual(txIn.hash, txHash) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) @@ -178,10 +182,10 @@ describe('TransactionBuilder', function () { }) it('accepts a txHash, index [, sequence number and scriptPubKey]', function () { - var vin = txb.addInput(txHash, 1, 54, scripts[1]) + const vin = txb.addInput(txHash, 1, 54, scripts[1]) assert.strictEqual(vin, 0) - var txIn = txb.__tx.ins[0] + const txIn = txb.__tx.ins[0] assert.strictEqual(txIn.hash, txHash) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) @@ -189,14 +193,14 @@ describe('TransactionBuilder', function () { }) it('accepts a prevTx, index [and sequence number]', function () { - var prevTx = new Transaction() + const prevTx = new Transaction() prevTx.addOutput(scripts[0], 0) prevTx.addOutput(scripts[1], 1) - var vin = txb.addInput(prevTx, 1, 54) + const vin = txb.addInput(prevTx, 1, 54) assert.strictEqual(vin, 0) - var txIn = txb.__tx.ins[0] + const txIn = txb.__tx.ins[0] assert.deepEqual(txIn.hash, prevTx.getHash()) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) @@ -219,26 +223,26 @@ describe('TransactionBuilder', function () { }) describe('addOutput', function () { - var txb + let txb beforeEach(function () { txb = new TransactionBuilder() }) it('accepts an address string and value', function () { - let address = getAddress(keyPair) - var vout = txb.addOutput(address, 1000) + const address = getAddress(keyPair) + const vout = txb.addOutput(address, 1000) assert.strictEqual(vout, 0) - var txout = txb.__tx.outs[0] + const txout = txb.__tx.outs[0] assert.deepEqual(txout.script, scripts[0]) assert.strictEqual(txout.value, 1000) }) it('accepts a ScriptPubKey and value', function () { - var vout = txb.addOutput(scripts[0], 1000) + const vout = txb.addOutput(scripts[0], 1000) assert.strictEqual(vout, 0) - var txout = txb.__tx.outs[0] + const txout = txb.__tx.outs[0] assert.deepEqual(txout.script, scripts[0]) assert.strictEqual(txout.value, 1000) }) @@ -290,7 +294,7 @@ describe('TransactionBuilder', function () { describe('setLockTime', function () { it('throws if if there exist any scriptSigs', function () { - var txb = new TransactionBuilder() + const txb = new TransactionBuilder() txb.addInput(txHash, 0) txb.sign(0, keyPair) @@ -302,12 +306,12 @@ describe('TransactionBuilder', function () { describe('sign', function () { it('supports the alternative abstract interface { publicKey, sign }', function () { - var keyPair = { + const keyPair = { publicKey: ECPair.makeRandom({ rng: function () { return Buffer.alloc(32, 1) } }).publicKey, sign: function (hash) { return Buffer.alloc(64, 0x5f) } } - var txb = new TransactionBuilder() + const txb = new TransactionBuilder() txb.setVersion(1) txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) txb.addOutput('1111111111111111111114oLvT2', 100000) @@ -317,13 +321,14 @@ describe('TransactionBuilder', function () { fixtures.invalid.sign.forEach(function (f) { it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), function () { - var txb = construct(f, true) + const txb = construct(f, true) f.inputs.forEach(function (input, index) { input.signs.forEach(function (sign) { - var keyPairNetwork = NETWORKS[sign.network || f.network] - var keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork) - var redeemScript, witnessScript + const keyPairNetwork = NETWORKS[sign.network || f.network] + const keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork) + let redeemScript + let witnessScript if (sign.redeemScript) { redeemScript = bscript.fromASM(sign.redeemScript) @@ -349,8 +354,8 @@ describe('TransactionBuilder', function () { describe('build', function () { fixtures.valid.build.forEach(function (f) { it('builds "' + f.description + '"', function () { - var txb = construct(f) - var tx = f.incomplete ? txb.buildIncomplete() : txb.build() + const txb = construct(f) + const tx = f.incomplete ? txb.buildIncomplete() : txb.build() assert.strictEqual(tx.toHex(), f.txHex) }) @@ -361,7 +366,7 @@ describe('TransactionBuilder', function () { describe('for ' + (f.description || f.exception), function () { it('throws ' + f.exception, function () { assert.throws(function () { - var txb + let txb if (f.txHex) { txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) } else { @@ -376,7 +381,7 @@ describe('TransactionBuilder', function () { if (f.incomplete) { it('throws ' + f.exception, function () { assert.throws(function () { - var txb + let txb if (f.txHex) { txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) } else { @@ -388,7 +393,7 @@ describe('TransactionBuilder', function () { }) } else { it('does not throw if buildIncomplete', function () { - var txb + let txb if (f.txHex) { txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) } else { @@ -402,11 +407,11 @@ describe('TransactionBuilder', function () { }) it('for incomplete with 0 signatures', function () { - var randomTxData = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000' - var randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH' + const randomTxData = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000' + const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH' - var randomTx = Transaction.fromHex(randomTxData) - var tx = new TransactionBuilder() + const randomTx = Transaction.fromHex(randomTxData) + let tx = new TransactionBuilder() tx.addInput(randomTx, 0) tx.addOutput(randomAddress, 1000) tx = tx.buildIncomplete() @@ -414,10 +419,10 @@ describe('TransactionBuilder', function () { }) it('for incomplete P2SH with 0 signatures', function () { - var inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', 'hex') // arbitrary P2SH input - var inpTx = Transaction.fromBuffer(inp) + const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', 'hex') // arbitrary P2SH input + const inpTx = Transaction.fromBuffer(inp) - var txb = new TransactionBuilder(NETWORKS.testnet) + const txb = new TransactionBuilder(NETWORKS.testnet) txb.addInput(inpTx, 0) txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output @@ -425,10 +430,10 @@ describe('TransactionBuilder', function () { }) it('for incomplete P2WPKH with 0 signatures', function () { - var inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', 'hex') - var inpTx = Transaction.fromBuffer(inp) + const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', 'hex') + const inpTx = Transaction.fromBuffer(inp) - var txb = new TransactionBuilder(NETWORKS.testnet) + const txb = new TransactionBuilder(NETWORKS.testnet) txb.addInput(inpTx, 0) txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output @@ -436,9 +441,9 @@ describe('TransactionBuilder', function () { }) it('for incomplete P2WSH with 0 signatures', function () { - var inpTx = Transaction.fromBuffer(Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', 'hex')) + const inpTx = Transaction.fromBuffer(Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', 'hex')) - var txb = new TransactionBuilder(NETWORKS.testnet) + const txb = new TransactionBuilder(NETWORKS.testnet) txb.addInput(inpTx, 0) txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output @@ -449,25 +454,25 @@ describe('TransactionBuilder', function () { describe('multisig', function () { fixtures.valid.multisig.forEach(function (f) { it(f.description, function () { - var txb = construct(f, true) - var tx - var network = NETWORKS[f.network] + const network = NETWORKS[f.network] + let txb = construct(f, true) + let tx f.inputs.forEach(function (input, i) { - var redeemScript = bscript.fromASM(input.redeemScript) + const redeemScript = bscript.fromASM(input.redeemScript) input.signs.forEach(function (sign) { // rebuild the transaction each-time after the first if (tx) { // do we filter OP_0's beforehand? if (sign.filterOP_0) { - var scriptSig = tx.ins[i].script + const scriptSig = tx.ins[i].script // ignore OP_0 on the front, ignore redeemScript - var signatures = bscript.decompile(scriptSig).slice(1, -1).filter(function (x) { return x !== ops.OP_0 }) + const signatures = bscript.decompile(scriptSig).slice(1, -1).filter(function (x) { return x !== ops.OP_0 }) // rebuild/replace the scriptSig without them - var replacement = btemplates.scriptHash.input.encode(btemplates.multisig.input.encode(signatures), redeemScript) + const replacement = btemplates.scriptHash.input.encode(btemplates.multisig.input.encode(signatures), redeemScript) assert.strictEqual(bscript.toASM(replacement), sign.scriptSigFiltered) tx.ins[i].script = replacement @@ -476,7 +481,7 @@ describe('TransactionBuilder', function () { txb = TransactionBuilder.fromTransaction(tx, network) } - var keyPair2 = ECPair.fromWIF(sign.keyPair, network) + const keyPair2 = ECPair.fromWIF(sign.keyPair, network) txb.sign(i, keyPair2, redeemScript, sign.hashType) // update the tx @@ -493,10 +498,10 @@ describe('TransactionBuilder', function () { }) describe('various edge case', function () { - var network = NETWORKS.testnet + const network = NETWORKS.testnet it('should warn of high fee for segwit transaction based on VSize, not Size', function () { - var rawtx = '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + + const rawtx = '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + '1df90000000000fffffffffdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a1df9' + '0100000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca40000' + '000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca401000000' + @@ -511,7 +516,7 @@ describe('TransactionBuilder', function () { 'd561abaac86c37a353b52895a5e6c196d6f44802473044022007be81ffd4297441ab10e740fc9bab9545a2' + '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000' - var txb = TransactionBuilder.fromTransaction(Transaction.fromHex(rawtx)) + const txb = TransactionBuilder.fromTransaction(Transaction.fromHex(rawtx)) txb.__inputs[0].value = 241530 txb.__inputs[1].value = 241530 txb.__inputs[2].value = 248920 @@ -523,64 +528,64 @@ describe('TransactionBuilder', function () { }) it('should classify witness inputs with witness = true during multisigning', function () { - var keyPair = ECPair.fromWIF('cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', network) - var witnessScript = Buffer.from('522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', 'hex') - var redeemScript = Buffer.from('002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', 'hex') - var scriptPubKey = Buffer.from('a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', 'hex') - var txb = new TransactionBuilder(network) + const keyPair = ECPair.fromWIF('cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', network) + const witnessScript = Buffer.from('522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', 'hex') + const redeemScript = Buffer.from('002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', 'hex') + const scriptPubKey = Buffer.from('a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', 'hex') + const txb = new TransactionBuilder(network) txb.setVersion(1) txb.addInput('a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', 1, 0xffffffff, scriptPubKey) txb.addOutput(scriptPubKey, 99000) txb.sign(0, keyPair, redeemScript, null, 100000, witnessScript) // 2-of-2 signed only once - var tx = txb.buildIncomplete() + const tx = txb.buildIncomplete() // Only input is segwit, so txid should be accurate with the final tx assert.equal(tx.getId(), 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') - var txHex = tx.toHex() - var newTxb = TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) + const txHex = tx.toHex() + const newTxb = TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) // input should have the key 'witness' set to true assert.equal(newTxb.__inputs[0].witness, true) }) it('should handle badly pre-filled OP_0s', function () { // OP_0 is used where a signature is missing - var redeemScripSig = bscript.fromASM('OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') - var redeemScript = bscript.fromASM('OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG') + const redeemScripSig = bscript.fromASM('OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') + const redeemScript = bscript.fromASM('OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG') - var tx = new Transaction() + const tx = new Transaction() tx.addInput(Buffer.from('cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f07149', 'hex'), 0, undefined, redeemScripSig) tx.addOutput(Buffer.from('76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac', 'hex'), 1000) // now import the Transaction - var txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet) + const txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet) - var keyPair2 = ECPair.fromWIF('91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', network) + const keyPair2 = ECPair.fromWIF('91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', network) txb.sign(0, keyPair2, redeemScript) - var tx2 = txb.build() + const tx2 = txb.build() assert.equal(tx2.getId(), 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') assert.equal(bscript.toASM(tx2.ins[0].script), 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') }) it('should not classify blank scripts as nonstandard', function () { - var txb = new TransactionBuilder() + let txb = new TransactionBuilder() txb.setVersion(1) txb.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) - var incomplete = txb.buildIncomplete().toHex() - var keyPair = ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') + const incomplete = txb.buildIncomplete().toHex() + const keyPair = ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') // sign, as expected txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) txb.sign(0, keyPair) - var txId = txb.build().getId() + const txId = txb.build().getId() assert.equal(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') // and, repeat txb = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) txb.sign(0, keyPair) - var txId2 = txb.build().getId() + const txId2 = txb.build().getId() assert.equal(txId, txId2) }) }) diff --git a/test/types.js b/test/types.js index ab2110d..adc6f35 100644 --- a/test/types.js +++ b/test/types.js @@ -6,8 +6,8 @@ const typeforce = require('typeforce') describe('types', function () { describe('Buffer Hash160/Hash256', function () { - var buffer20byte = Buffer.alloc(20) - var buffer32byte = Buffer.alloc(32) + const buffer20byte = Buffer.alloc(20) + const buffer32byte = Buffer.alloc(32) it('return true for valid size', function () { assert(types.Hash160bit(buffer20byte)) From d2ea50b296db672e24e8d5619c807e0a3528f7a3 Mon Sep 17 00:00:00 2001 From: Will O'Beirne <wbobeirne@gmail.com> Date: Mon, 25 Jun 2018 14:38:55 -0400 Subject: [PATCH 049/568] Replace bigi with bnjs. Updated README links to tests. --- README.md | 69 +++++++++++++++++++------------------- package.json | 2 +- test/integration/crypto.js | 24 ++++++------- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index d2d500e..14ee958 100644 --- a/README.md +++ b/README.md @@ -79,41 +79,42 @@ The below examples are implemented as integration tests, they should be very eas Otherwise, pull requests are appreciated. Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). -- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L12) -- [Generate an address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L19) -- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L29) -- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L36) -- [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L50) -- [Generate a SegWit P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L60) -- [Generate a SegWit 3-of-4 multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L71) -- [Generate a SegWit 2-of-2 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L86) -- [Support the retrieval of transactions for an address (3rd party blockchain)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L100) -- [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L121) -- [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L131) -- [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L14) -- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L28) -- [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L46) -- [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L88) -- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L115) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L151) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L183) -- [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L8) -- [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L15) -- [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L26) -- [Create a BIP32, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L35) -- [Create a BIP44, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L50) -- [Create a BIP49, bitcoin testnet, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L66) -- [Use BIP39 to generate BIP32 addresses](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L83) -- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L37) -- [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L71) -- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L104) +- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L30) +- [Generate an address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L37) +- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L48) +- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L55) +- [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L69) +- [Generate a SegWit P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L78) +- [Generate a SegWit 3-of-4 multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L88) +- [Generate a SegWit 2-of-2 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L103) +- [Support the retrieval of transactions for an address (3rd party blockchain)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L117) +- [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L136) +- [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L146) +- [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L21) +- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L36) +- [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L55) +- [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L87) +- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L109) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L149) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L184) +- [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L17) +- [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L24) +- [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L35) +- [Create a BIP32, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L44) +- [Create a BIP44, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L59) +- [Create a BIP49, bitcoin testnet, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L75) +- [Use BIP39 to generate BIP32 addresses](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L92) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L42) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L83) +- [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L135) +- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L177) - [Recover a private key from duplicate R values](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L14) -- [Recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L115) -- [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L70:) -- [Generate a single-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L89:) -- [Recover parent recipient.d, if a derived private key is leaked (and nonce was revealed)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L105) -- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L122) -- [Generate a dual-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L145) +- [Recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L73) +- [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L75) +- [Generate a single-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L94) +- [Recover parent recipient.d, if a derived private key is leaked (and nonce was revealed)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L110) +- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L127) +- [Generate a dual-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L150) If you have a use case that you feel could be listed here, please [ask for it](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new)! diff --git a/package.json b/package.json index cf3db97..7f1ef7b 100644 --- a/package.json +++ b/package.json @@ -47,10 +47,10 @@ "wif": "^2.0.1" }, "devDependencies": { - "bigi": "^1.4.2", "bip39": "^2.3.0", "bip65": "^1.0.1", "bip68": "^1.0.3", + "bn.js": "4.11.8", "bs58": "^4.0.0", "dhttp": "^2.5.0", "hoodwink": "^1.0.0", diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 58ae431..c46cf6b 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -1,7 +1,7 @@ /* global describe, it */ var assert = require('assert') -var bigi = require('bigi') +var BN = require('bn.js') var bitcoin = require('../../') var bip32 = require('bip32') var crypto = require('crypto') @@ -31,11 +31,11 @@ describe('bitcoinjs-lib (crypto)', function () { // store the required information input.signature = scriptSignature.signature - input.z = bigi.fromBuffer(m) + input.z = new BN(m) }) - // finally, run the tasks, then on to the math - var n = secp256k1.n + // finally, run the tasks, then on to the mathmod + var n = new BN(secp256k1.n.toString()) for (var i = 0; i < tx.ins.length; ++i) { for (var j = i + 1; j < tx.ins.length; ++j) { @@ -47,22 +47,22 @@ describe('bitcoinjs-lib (crypto)', function () { let rB = inputB.signature.slice(0, 32) assert.strictEqual(r.toString('hex'), rB.toString('hex')) - var rInv = bigi.fromBuffer(r).modInverse(n) + var rInv = new BN(r).invm(n) - var s1 = bigi.fromBuffer(inputA.signature.slice(32, 64)) - var s2 = bigi.fromBuffer(inputB.signature.slice(32, 64)) + var s1 = new BN(inputA.signature.slice(32, 64)) + var s2 = new BN(inputB.signature.slice(32, 64)) var z1 = inputA.z var z2 = inputB.z - var zz = z1.subtract(z2).mod(n) - var ss = s1.subtract(s2).mod(n) + var zz = z1.sub(z2).mod(n) + var ss = s1.sub(s2).mod(n) // k = (z1 - z2) / (s1 - s2) // d1 = (s1 * k - z1) / r // d2 = (s2 * k - z2) / r - var k = zz.multiply(ss.modInverse(n)).mod(n) - var d1 = ((s1.multiply(k).mod(n)).subtract(z1).mod(n)).multiply(rInv).mod(n) - var d2 = ((s2.multiply(k).mod(n)).subtract(z2).mod(n)).multiply(rInv).mod(n) + var k = zz.mul(ss.invm(n)).mod(n) + var d1 = ((s1.mul(k).mod(n)).sub(z1).mod(n)).mul(rInv).mod(n) + var d2 = ((s2.mul(k).mod(n)).sub(z2).mod(n)).mul(rInv).mod(n) // enforce matching private keys assert.strictEqual(d1.toString(), d2.toString()) From 3588fc1db3d78b536ca8e236baa32a82021b01c8 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 26 Jun 2018 20:13:43 +1000 Subject: [PATCH 050/568] fix BN require --- test/integration/crypto.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 2783097..cd2af49 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -2,7 +2,7 @@ const assert = require('assert') -const bigi = require('bigi') +const BN = require('bn.js') const bitcoin = require('../../') const bip32 = require('bip32') const crypto = require('crypto') From 7756c5dd762ac8487d24f55ceb57dbc9cbf55771 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 25 Jun 2018 16:15:45 +1000 Subject: [PATCH 051/568] bump tiny-secp256k1 --- package.json | 4 ++-- test/integration/crypto.js | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7f1ef7b..d6947a2 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", "safe-buffer": "^5.1.1", - "tiny-secp256k1": "^0.1.0", + "tiny-secp256k1": "^0.2.2", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" @@ -50,7 +50,7 @@ "bip39": "^2.3.0", "bip65": "^1.0.1", "bip68": "^1.0.3", - "bn.js": "4.11.8", + "bn.js": "^4.11.8", "bs58": "^4.0.0", "dhttp": "^2.5.0", "hoodwink": "^1.0.0", diff --git a/test/integration/crypto.js b/test/integration/crypto.js index cd2af49..78a6ab0 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -1,6 +1,5 @@ /* global describe, it */ - const assert = require('assert') const BN = require('bn.js') const bitcoin = require('../../') From f9a739e1db2ba5ee4dde11f90a514aabcdaddf1b Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 5 Jun 2018 17:24:47 +1000 Subject: [PATCH 052/568] add payments p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh --- src/payments/index.js | 19 ++ src/payments/lazy.js | 30 +++ src/payments/p2ms.js | 140 ++++++++++++++ src/payments/p2pk.js | 80 ++++++++ src/payments/p2pkh.js | 127 +++++++++++++ src/payments/p2sh.js | 176 ++++++++++++++++++ src/payments/p2wpkh.js | 124 +++++++++++++ src/payments/p2wsh.js | 154 ++++++++++++++++ src/payments/package.json | 40 ++++ src/script.js | 4 +- test/fixtures/p2ms.json | 378 ++++++++++++++++++++++++++++++++++++++ test/fixtures/p2pk.json | 152 +++++++++++++++ test/fixtures/p2pkh.json | 214 +++++++++++++++++++++ test/fixtures/p2sh.json | 346 ++++++++++++++++++++++++++++++++++ test/fixtures/p2wpkh.json | 195 ++++++++++++++++++++ test/fixtures/p2wsh.json | 326 ++++++++++++++++++++++++++++++++ test/payments.js | 65 +++++++ test/payments.utils.js | 128 +++++++++++++ 18 files changed, 2696 insertions(+), 2 deletions(-) create mode 100644 src/payments/index.js create mode 100644 src/payments/lazy.js create mode 100644 src/payments/p2ms.js create mode 100644 src/payments/p2pk.js create mode 100644 src/payments/p2pkh.js create mode 100644 src/payments/p2sh.js create mode 100644 src/payments/p2wpkh.js create mode 100644 src/payments/p2wsh.js create mode 100644 src/payments/package.json create mode 100644 test/fixtures/p2ms.json create mode 100644 test/fixtures/p2pk.json create mode 100644 test/fixtures/p2pkh.json create mode 100644 test/fixtures/p2sh.json create mode 100644 test/fixtures/p2wpkh.json create mode 100644 test/fixtures/p2wsh.json create mode 100644 test/payments.js create mode 100644 test/payments.utils.js diff --git a/src/payments/index.js b/src/payments/index.js new file mode 100644 index 0000000..9e869f5 --- /dev/null +++ b/src/payments/index.js @@ -0,0 +1,19 @@ +const p2ms = require('./p2ms') +const p2pk = require('./p2pk') +const p2pkh = require('./p2pkh') +const p2sh = require('./p2sh') +const p2wpkh = require('./p2wpkh') +const p2wsh = require('./p2wsh') + +module.exports = { + p2ms: p2ms, + p2pk: p2pk, + p2pkh: p2pkh, + p2sh: p2sh, + p2wpkh: p2wpkh, + p2wsh: p2wsh +} + +// TODO +// OP_RETURN +// witness commitment diff --git a/src/payments/lazy.js b/src/payments/lazy.js new file mode 100644 index 0000000..8538752 --- /dev/null +++ b/src/payments/lazy.js @@ -0,0 +1,30 @@ +function prop (object, name, f) { + Object.defineProperty(object, name, { + configurable: true, + enumerable: true, + get: function () { + let value = f.call(this) + this[name] = value + return value + }, + set: function (value) { + Object.defineProperty(this, name, { + configurable: true, + enumerable: true, + value: value, + writable: true + }) + } + }) +} + +function value (f) { + let value + return function () { + if (value !== undefined) return value + value = f() + return value + } +} + +module.exports = { prop, value } diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js new file mode 100644 index 0000000..9bce722 --- /dev/null +++ b/src/payments/p2ms.js @@ -0,0 +1,140 @@ +let lazy = require('./lazy') +let typef = require('typeforce') +let OPS = require('bitcoin-ops') +let ecc = require('tiny-secp256k1') + +let bscript = require('../script') +let BITCOIN_NETWORK = require('../networks').bitcoin +let OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 + +function stacksEqual (a, b) { + if (a.length !== b.length) return false + + return a.every(function (x, i) { + return x.equals(b[i]) + }) +} + +// input: OP_0 [signatures ...] +// output: m [pubKeys ...] n OP_CHECKMULTISIG +function p2ms (a, opts) { + if ( + !a.output && + !(a.pubkeys && a.m !== undefined) + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + function isAcceptableSignature (x) { + return bscript.isCanonicalScriptSignature(x) || (opts.allowIncomplete && (x === OPS.OP_0)) + } + + typef({ + network: typef.maybe(typef.Object), + m: typef.maybe(typef.Number), + n: typef.maybe(typef.Number), + output: typef.maybe(typef.Buffer), + pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), + + signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), + input: typef.maybe(typef.Buffer) + }, a) + + let network = a.network || BITCOIN_NETWORK + let o = { network } + + let chunks + let decoded = false + function decode (output) { + if (decoded) return + decoded = true + chunks = bscript.decompile(output) + let om = chunks[0] - OP_INT_BASE + let on = chunks[chunks.length - 2] - OP_INT_BASE + o.m = om + o.n = on + o.pubkeys = chunks.slice(1, -2) + } + + lazy.prop(o, 'output', function () { + if (!a.m) return + if (!o.n) return + if (!a.pubkeys) return + return bscript.compile([].concat( + OP_INT_BASE + a.m, + a.pubkeys, + OP_INT_BASE + o.n, + OPS.OP_CHECKMULTISIG + )) + }) + lazy.prop(o, 'm', function () { + if (!o.output) return + decode(o.output) + return o.m + }) + lazy.prop(o, 'n', function () { + if (!o.pubkeys) return + return o.pubkeys.length + }) + lazy.prop(o, 'pubkeys', function () { + if (!a.output) return + decode(a.output) + return o.pubkeys + }) + lazy.prop(o, 'signatures', function () { + if (!a.input) return + return bscript.decompile(a.input).slice(1) + }) + lazy.prop(o, 'input', function () { + if (!a.signatures) return + return bscript.compile([OPS.OP_0].concat(a.signatures)) + }) + lazy.prop(o, 'witness', function () { + if (!o.input) return + return [] + }) + + // extended validation + if (opts.validate) { + if (a.output) { + decode(a.output) + if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid') + if (!typef.Number(chunks[chunks.length - 2])) throw new TypeError('Output is invalid') + if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid') + + if ( + o.m <= 0 || + o.n > 16 || + o.m > o.n || + o.n !== chunks.length - 3) throw new TypeError('Output is invalid') + if (!o.pubkeys.every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid') + + if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch') + if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch') + if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys)) throw new TypeError('Pubkeys mismatch') + } + + if (a.pubkeys) { + if (a.n !== undefined && a.n !== a.pubkeys.length) throw new TypeError('Pubkey count mismatch') + o.n = a.pubkeys.length + + if (o.n < o.m) throw new TypeError('Pubkey count cannot be less than m') + } + + if (a.signatures) { + if (a.signatures.length < o.m) throw new TypeError('Not enough signatures provided') + if (a.signatures.length > o.m) throw new TypeError('Too many signatures provided') + } + + if (a.input) { + if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') + if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') + + if (a.signatures && !stacksEqual(a.signatures.equals(o.signatures))) throw new TypeError('Signature mismatch') + if (a.m !== undefined && a.m !== a.signatures.length) throw new TypeError('Signature count mismatch') + } + } + + return Object.assign(o, a) +} + +module.exports = p2ms diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js new file mode 100644 index 0000000..b0408aa --- /dev/null +++ b/src/payments/p2pk.js @@ -0,0 +1,80 @@ +let lazy = require('./lazy') +let typef = require('typeforce') +let OPS = require('bitcoin-ops') +let ecc = require('tiny-secp256k1') + +let bscript = require('../script') +let BITCOIN_NETWORK = require('../networks').bitcoin + +// input: {signature} +// output: {pubKey} OP_CHECKSIG +function p2pk (a, opts) { + if ( + !a.output && + !a.pubkey + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + typef({ + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + pubkey: typef.maybe(ecc.isPoint), + + signature: typef.maybe(bscript.isCanonicalScriptSignature), + input: typef.maybe(typef.Buffer) + }, a) + + let _chunks = lazy.value(function () { return bscript.decompile(a.input) }) + + let network = a.network || BITCOIN_NETWORK + let o = { network } + + lazy.prop(o, 'output', function () { + if (!a.pubkey) return + return bscript.compile([ + a.pubkey, + OPS.OP_CHECKSIG + ]) + }) + lazy.prop(o, 'pubkey', function () { + if (!a.output) return + return a.output.slice(1, -1) + }) + lazy.prop(o, 'signature', function () { + if (!a.input) return + return _chunks()[0] + }) + lazy.prop(o, 'input', function () { + if (!a.signature) return + return bscript.compile([a.signature]) + }) + lazy.prop(o, 'witness', function () { + if (!o.input) return + return [] + }) + + // extended validation + if (opts.validate) { + if (a.pubkey && a.output) { + if (!a.pubkey.equals(o.pubkey)) throw new TypeError('Pubkey mismatch') + } + + if (a.output) { + if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') + if (!ecc.isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid') + } + + if (a.signature) { + if (a.input && !a.input.equals(o.input)) throw new TypeError('Input mismatch') + } + + if (a.input) { + if (_chunks().length !== 1) throw new TypeError('Input is invalid') + if (!bscript.isCanonicalScriptSignature(_chunks()[0])) throw new TypeError('Input has invalid signature') + } + } + + return Object.assign(o, a) +} + +module.exports = p2pk diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js new file mode 100644 index 0000000..9d0733d --- /dev/null +++ b/src/payments/p2pkh.js @@ -0,0 +1,127 @@ +let lazy = require('./lazy') +let typef = require('typeforce') +let OPS = require('bitcoin-ops') +let ecc = require('tiny-secp256k1') + +let baddress = require('../address') +let bcrypto = require('../crypto') +let bscript = require('../script') +let BITCOIN_NETWORK = require('../networks').bitcoin + +// input: {signature} {pubkey} +// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG +function p2pkh (a, opts) { + if ( + !a.address && + !a.hash && + !a.output && + !a.pubkey && + !a.input + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + typef({ + network: typef.maybe(typef.Object), + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + output: typef.maybe(typef.BufferN(25)), + + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + input: typef.maybe(typef.Buffer) + }, a) + + let _address = lazy.value(function () { return baddress.fromBase58Check(a.address) }) + let _chunks = lazy.value(function () { return bscript.decompile(a.input) }) + + let network = a.network || BITCOIN_NETWORK + let o = { network } + + lazy.prop(o, 'address', function () { + if (!o.hash) return + return baddress.toBase58Check(o.hash, network.pubKeyHash) + }) + lazy.prop(o, 'hash', function () { + if (a.output) return a.output.slice(3, 23) + if (a.address) return _address().hash + if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey) + }) + lazy.prop(o, 'output', function () { + if (!o.hash) return + return bscript.compile([ + OPS.OP_DUP, + OPS.OP_HASH160, + o.hash, + OPS.OP_EQUALVERIFY, + OPS.OP_CHECKSIG + ]) + }) + lazy.prop(o, 'pubkey', function () { + if (!a.input) return + return _chunks()[1] + }) + lazy.prop(o, 'signature', function () { + if (!a.input) return + return _chunks()[0] + }) + lazy.prop(o, 'input', function () { + if (!a.pubkey) return + if (!a.signature) return + return bscript.compile([a.signature, a.pubkey]) + }) + lazy.prop(o, 'witness', function () { + if (!o.input) return + return [] + }) + + // extended validation + if (opts.validate) { + let hash + if (a.address) { + if (_address().version !== network.pubKeyHash) throw new TypeError('Network mismatch') + if (_address().hash.length !== 20) throw new TypeError('Invalid address') + else hash = _address().hash + } + + if (a.hash) { + if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') + else hash = a.hash + } + + if (a.output) { + if ( + a.output.length !== 25 || + a.output[0] !== OPS.OP_DUP || + a.output[1] !== OPS.OP_HASH160 || + a.output[2] !== 0x14 || + a.output[23] !== OPS.OP_EQUALVERIFY || + a.output[24] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') + + if (hash && !hash.equals(a.output.slice(3, 23))) throw new TypeError('Hash mismatch') + else hash = a.output.slice(3, 23) + } + + if (a.pubkey) { + let pkh = bcrypto.hash160(a.pubkey) + if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + else hash = pkh + } + + if (a.input) { + let chunks = _chunks() + if (chunks.length !== 2) throw new TypeError('Input is invalid') + if (!bscript.isCanonicalScriptSignature(chunks[0])) throw new TypeError('Input has invalid signature') + if (!ecc.isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey') + + if (a.signature && !a.signature.equals(chunks[0])) throw new TypeError('Signature mismatch') + if (a.pubkey && !a.pubkey.equals(chunks[1])) throw new TypeError('Pubkey mismatch') + + let pkh = bcrypto.hash160(chunks[1]) + if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + } + } + + return Object.assign(o, a) +} + +module.exports = p2pkh diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js new file mode 100644 index 0000000..c67d921 --- /dev/null +++ b/src/payments/p2sh.js @@ -0,0 +1,176 @@ +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') + +const baddress = require('../address') +const bcrypto = require('../crypto') +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin + +function stacksEqual (a, b) { + if (a.length !== b.length) return false + + return a.every(function (x, i) { + return x.equals(b[i]) + }) +} + +// input: [redeemScriptSig ...] {redeemScript} +// witness: <?> +// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL +function p2sh (a, opts) { + if ( + !a.address && + !a.hash && + !a.output && + !a.redeem && + !a.input + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + typef({ + network: typef.maybe(typef.Object), + + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + output: typef.maybe(typef.BufferN(23)), + + redeem: typef.maybe({ + network: typef.maybe(typef.Object), + output: typef.Buffer, + input: typef.maybe(typef.Buffer), + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }), + input: typef.maybe(typef.Buffer), + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a) + + const network = a.network || BITCOIN_NETWORK + const o = { network } + + const _address = lazy.value(function () { return baddress.fromBase58Check(a.address) }) + const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) + const _redeem = lazy.value(function () { + const chunks = _chunks() + return { + network: network, + output: chunks[chunks.length - 1], + input: bscript.compile(chunks.slice(0, -1)), + witness: a.witness || [] + } + }) + + // output dependents + lazy.prop(o, 'address', function () { + if (!o.hash) return + return baddress.toBase58Check(o.hash, network.scriptHash) + }) + lazy.prop(o, 'hash', function () { + // in order of least effort + if (a.output) return a.output.slice(2, 22) + if (a.address) return _address().hash + if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output) + }) + lazy.prop(o, 'output', function () { + if (!o.hash) return + return bscript.compile([ + OPS.OP_HASH160, + o.hash, + OPS.OP_EQUAL + ]) + }) + + // input dependents + lazy.prop(o, 'redeem', function () { + if (!a.input) return + return _redeem() + }) + lazy.prop(o, 'input', function () { + if (!a.redeem || !a.redeem.input) return + return bscript.compile([].concat( + bscript.decompile(a.redeem.input), + a.redeem.output + )) + }) + lazy.prop(o, 'witness', function () { + if (o.redeem && o.redeem.witness) return o.redeem.witness + if (o.input) return [] + }) + + if (opts.validate) { + let hash + if (a.address) { + if (_address().version !== network.scriptHash) throw new TypeError('Network mismatch') + if (_address().hash.length !== 20) throw new TypeError('Invalid address') + else hash = _address().hash + } + + if (a.hash) { + if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') + else hash = a.hash + } + + if (a.output) { + if ( + a.output.length !== 23 || + a.output[0] !== OPS.OP_HASH160 || + a.output[1] !== 0x14 || + a.output[22] !== OPS.OP_EQUAL) throw new TypeError('Output is invalid') + const hash2 = a.output.slice(2, 22) + if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + else hash = hash2 + } + + // inlined to prevent 'no-inner-declarations' failing + const checkRedeem = function (redeem) { + // is the redeem output empty/invalid? + const decompile = bscript.decompile(redeem.output) + if (!decompile || decompile.length < 1) throw new TypeError('Redeem.output too short') + + // match hash against other sources + const hash2 = bcrypto.hash160(redeem.output) + if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + else hash = hash2 + + if (redeem.input) { + const hasInput = redeem.input.length > 0 + const hasWitness = redeem.witness && redeem.witness.length > 0 + if (!hasInput && !hasWitness) throw new TypeError('Empty input') + if (hasInput && hasWitness) throw new TypeError('Input and witness provided') + if (hasInput) { + const richunks = bscript.decompile(redeem.input) + if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig') + } + } + } + + if (a.input) { + const chunks = _chunks() + if (!chunks || chunks.length < 1) throw new TypeError('Input too short') + if (!Buffer.isBuffer(_redeem().output)) throw new TypeError('Input is invalid') + + checkRedeem(_redeem()) + } + + if (a.redeem) { + if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') + if (o.redeem) { + if (a.redeem.output && !a.redeem.output.equals(o.redeem.output)) throw new TypeError('Redeem.output mismatch') + if (a.redeem.input && !a.redeem.input.equals(o.redeem.input)) throw new TypeError('Redeem.input mismatch') + } + + checkRedeem(a.redeem) + } + + if (a.witness) { + if ( + a.redeem && + a.redeem.witness && + !stacksEqual(a.redeem.witness, a.witness)) throw new TypeError('Witness and redeem.witness mismatch') + } + } + + return Object.assign(o, a) +} + +module.exports = p2sh diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js new file mode 100644 index 0000000..f2bdeb4 --- /dev/null +++ b/src/payments/p2wpkh.js @@ -0,0 +1,124 @@ +let lazy = require('./lazy') +let typef = require('typeforce') +let OPS = require('bitcoin-ops') +let ecc = require('tiny-secp256k1') + +let baddress = require('../address') +let bcrypto = require('../crypto') +let bscript = require('../script') +let BITCOIN_NETWORK = require('../networks').bitcoin + +let EMPTY_BUFFER = Buffer.alloc(0) + +// witness: {signature} {pubKey} +// input: <> +// output: OP_0 {pubKeyHash} +function p2wpkh (a, opts) { + if ( + !a.address && + !a.hash && + !a.output && + !a.pubkey && + !a.witness + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + typef({ + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + input: typef.maybe(typef.BufferN(0)), + network: typef.maybe(typef.Object), + output: typef.maybe(typef.BufferN(22)), + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a) + + let _address = lazy.value(function () { return baddress.fromBech32(a.address) }) + + let network = a.network || BITCOIN_NETWORK + let o = { network } + + lazy.prop(o, 'address', function () { + if (!o.hash) return + return baddress.toBech32(o.hash, 0x00, network.bech32) + }) + lazy.prop(o, 'hash', function () { + if (a.output) return a.output.slice(2, 22) + if (a.address) return _address().data + if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey) + }) + lazy.prop(o, 'output', function () { + if (!o.hash) return + return bscript.compile([ + OPS.OP_0, + o.hash + ]) + }) + lazy.prop(o, 'pubkey', function () { + if (a.pubkey) return a.pubkey + if (!a.witness) return + return a.witness[1] + }) + lazy.prop(o, 'signature', function () { + if (!a.witness) return + return a.witness[0] + }) + lazy.prop(o, 'input', function () { + if (!o.witness) return + return EMPTY_BUFFER + }) + lazy.prop(o, 'witness', function () { + if (!a.pubkey) return + if (!a.signature) return + return [a.signature, a.pubkey] + }) + + // extended validation + if (opts.validate) { + let hash + if (a.address) { + if (network && network.bech32 !== _address().prefix) throw new TypeError('Network mismatch') + if (_address().version !== 0x00) throw new TypeError('Invalid version') + if (_address().data.length !== 20) throw new TypeError('Invalid data') + if (hash && !hash.equals(_address().data)) throw new TypeError('Hash mismatch') + else hash = _address().data + } + + if (a.pubkey) { + let pkh = bcrypto.hash160(a.pubkey) + if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + else hash = pkh + } + + if (a.hash) { + if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') + else hash = a.hash + } + + if (a.output) { + if ( + a.output.length !== 22 || + a.output[0] !== OPS.OP_0 || + a.output[1] !== 0x14) throw new TypeError('Output is invalid') + if (hash && !hash.equals(a.output.slice(2))) throw new TypeError('Hash mismatch') + else hash = a.output.slice(2) + } + + if (a.witness) { + if (a.witness.length !== 2) throw new TypeError('Input is invalid') + if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Input has invalid signature') + if (!ecc.isPoint(a.witness[1])) throw new TypeError('Input has invalid pubkey') + + if (a.signature && !a.signature.equals(a.witness[0])) throw new TypeError('Signature mismatch') + if (a.pubkey && !a.pubkey.equals(a.witness[1])) throw new TypeError('Pubkey mismatch') + + let pkh = bcrypto.hash160(a.witness[1]) + if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + } + } + + return Object.assign(o, a) +} + +module.exports = p2wpkh diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js new file mode 100644 index 0000000..8e2e4fe --- /dev/null +++ b/src/payments/p2wsh.js @@ -0,0 +1,154 @@ +let lazy = require('./lazy') +let typef = require('typeforce') +let OPS = require('bitcoin-ops') + +let baddress = require('../address') +let bcrypto = require('../crypto') +let bscript = require('../script') +let BITCOIN_NETWORK = require('../networks').bitcoin + +let EMPTY_BUFFER = Buffer.alloc(0) + +function stacksEqual (a, b) { + if (a.length !== b.length) return false + + return a.every(function (x, i) { + return x.equals(b[i]) + }) +} + +// input: <> +// witness: [redeemScriptSig ...] {redeemScript} +// output: OP_0 {sha256(redeemScript)} +function p2wsh (a, opts) { + if ( + !a.address && + !a.hash && + !a.output && + !a.redeem && + !a.witness + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + typef({ + network: typef.maybe(typef.Object), + + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(32)), + output: typef.maybe(typef.BufferN(34)), + + redeem: typef.maybe({ + input: typef.maybe(typef.Buffer), + network: typef.maybe(typef.Object), + output: typef.Buffer, + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }), + input: typef.maybe(typef.BufferN(0)), + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a) + + let _address = lazy.value(function () { return baddress.fromBech32(a.address) }) + let _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input) }) + + let network = a.network || BITCOIN_NETWORK + let o = { network } + + lazy.prop(o, 'address', function () { + if (!o.hash) return + return baddress.toBech32(o.hash, 0x00, network.bech32) + }) + lazy.prop(o, 'hash', function () { + if (a.output) return a.output.slice(2) + if (a.address) return baddress.fromBech32(a.address).data + if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output) + }) + lazy.prop(o, 'output', function () { + if (!o.hash) return + return bscript.compile([ + OPS.OP_0, + o.hash + ]) + }) + lazy.prop(o, 'redeem', function () { + if (!a.witness) return + return { + output: a.witness[a.witness.length - 1], + input: EMPTY_BUFFER, + witness: a.witness.slice(0, -1) + } + }) + lazy.prop(o, 'input', function () { + if (!o.witness) return + return EMPTY_BUFFER + }) + lazy.prop(o, 'witness', function () { + // transform redeem input to witness stack? + if (a.redeem && a.redeem.input && a.redeem.input.length > 0) { + let stack = bscript.toStack(_rchunks()) + + // assign, and blank the existing input + o.redeem = Object.assign({ witness: stack }, a.redeem) + o.redeem.input = EMPTY_BUFFER + return [].concat(stack, a.redeem.output) + } + + if (!a.redeem) return + if (!a.redeem.witness) return + return [].concat(a.redeem.witness, a.redeem.output) + }) + + // extended validation + if (opts.validate) { + let hash + if (a.address) { + if (_address().prefix !== network.bech32) throw new TypeError('Network mismatch') + if (_address().version !== 0x00) throw new TypeError('Invalid version') + if (_address().data.length !== 32) throw new TypeError('Invalid data') + else hash = _address().data + } + + if (a.hash) { + if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') + else hash = a.hash + } + + if (a.output) { + if ( + a.output.length !== 34 || + a.output[0] !== OPS.OP_0 || + a.output[1] !== 0x20) throw new TypeError('Output is invalid') + let hash2 = a.output.slice(2) + if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + else hash = hash2 + } + + if (a.redeem) { + if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') + + // is there two redeem sources? + if ( + a.redeem.input && + a.redeem.input.length > 0 && + a.redeem.witness) throw new TypeError('Ambiguous witness source') + + // is the redeem output non-empty? + if (bscript.decompile(a.redeem.output).length === 0) throw new TypeError('Redeem.output is invalid') + + // match hash against other sources + let hash2 = bcrypto.sha256(a.redeem.output) + if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + else hash = hash2 + + if (a.redeem.input && !bscript.isPushOnly(_rchunks())) throw new TypeError('Non push-only scriptSig') + if (a.witness && a.redeem.witness && !stacksEqual(a.witness, a.redeem.witness)) throw new TypeError('Witness and redeem.witness mismatch') + } + + if (a.witness) { + if (a.redeem && !a.redeem.output.equals(a.witness[a.witness.length - 1])) throw new TypeError('Witness and redeem.output mismatch') + } + } + + return Object.assign(o, a) +} + +module.exports = p2wsh diff --git a/src/payments/package.json b/src/payments/package.json new file mode 100644 index 0000000..9383db2 --- /dev/null +++ b/src/payments/package.json @@ -0,0 +1,40 @@ +{ + "name": "bitcoinjs-playground", + "version": "1.0.0", + "description": "Go nuts!", + "main": "_testnet.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bitcoinjs/bitcoinjs-playground.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/bitcoinjs/bitcoinjs-playground/issues" + }, + "homepage": "https://github.com/bitcoinjs/bitcoinjs-playground#readme", + "dependencies": { + "async": "^2.5.0", + "bech32": "^1.1.3", + "bip21": "^2.0.1", + "bip32-utils": "^0.11.1", + "bip38": "^2.0.2", + "bip39": "^2.5.0", + "bip69": "^2.1.1", + "bitcoin-ops": "^1.4.1", + "bitcoinjs-lib": "^3.3.2", + "bs58": "^4.0.1", + "bs58check": "^2.1.1", + "cb-http-client": "^0.2.3", + "coinselect": "^3.1.11", + "dhttp": "^2.4.2", + "merkle-lib": "^2.0.10", + "mocha": "^5.0.5", + "tape": "^4.9.0", + "typeforce": "^1.11.4", + "utxo": "^2.0.4" + } +} diff --git a/src/script.js b/src/script.js index 30efa0a..ad7c4e4 100644 --- a/src/script.js +++ b/src/script.js @@ -98,11 +98,11 @@ function decompile (buffer) { if ((opcode > OPS.OP_0) && (opcode <= OPS.OP_PUSHDATA4)) { const d = pushdata.decode(buffer, i) - // did reading a pushDataInt fail? empty script + // did reading a pushDataInt fail? if (d === null) return null i += d.size - // attempt to read too much data? empty script + // attempt to read too much data? if (i + d.number > buffer.length) return null const data = buffer.slice(i, i + d.number) diff --git a/test/fixtures/p2ms.json b/test/fixtures/p2ms.json new file mode 100644 index 0000000..d5bb0c8 --- /dev/null +++ b/test/fixtures/p2ms.json @@ -0,0 +1,378 @@ +{ + "valid": [ + { + "description": "output from output", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG" + }, + "expected": { + "m": 2, + "n": 2, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ], + "signatures": null, + "input": null, + "witness": null + } + }, + { + "description": "output from m/pubkeys", + "arguments": { + "m": 1, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ] + }, + "expected": { + "m": 1, + "n": 2, + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ], + "signatures": null, + "input": null, + "witness": null + } + }, + { + "description": "input/output from m/pubkeys/signatures", + "arguments": { + "m": 2, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002", + "030000000000000000000000000000000000000000000000000000000000000003" + ], + "signatures": [ + "300602010002010001", + "300602010102010001" + ] + }, + "expected": { + "m": 2, + "n": 3, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002", + "030000000000000000000000000000000000000000000000000000000000000003" + ], + "signatures": [ + "300602010002010001", + "300602010102010001" + ], + "input": "OP_0 300602010002010001 300602010102010001", + "witness": [] + } + }, + { + "description": "input/output from output/signatures", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "signatures": [ + "300602010002010001", + "300602010102010001" + ] + }, + "expected": { + "m": 2, + "n": 3, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002", + "030000000000000000000000000000000000000000000000000000000000000003" + ], + "signatures": [ + "300602010002010001", + "300602010102010001" + ], + "input": "OP_0 300602010002010001 300602010102010001", + "witness": [] + } + }, + { + "description": "input/output from input/output", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "input": "OP_0 300602010002010001 300602010102010001" + }, + "expected": { + "m": 2, + "n": 3, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002", + "030000000000000000000000000000000000000000000000000000000000000003" + ], + "signatures": [ + "300602010002010001", + "300602010102010001" + ], + "input": "OP_0 300602010002010001 300602010102010001", + "witness": [] + } + }, + { + "description": "input/output from input/output, even if incomplete", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "input": "OP_0 OP_0 300602010102010001" + }, + "options": { + "allowIncomplete": true + }, + "expected": { + "m": 2, + "n": 2, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ], + "signatures": [ + 0, + "300602010102010001" + ], + "input": "OP_0 OP_0 300602010102010001", + "witness": [] + } + }, + { + "description": "input/output from output/signatures, even if incomplete", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "signatures": [ + 0, + "300602010102010001" + ] + }, + "options": { + "allowIncomplete": true + }, + "expected": { + "m": 2, + "n": 2, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ], + "signatures": [ + 0, + "300602010102010001" + ], + "input": "OP_0 OP_0 300602010102010001", + "witness": [] + } + } + ], + "invalid": [ + { + "exception": "Not enough data", + "arguments": {} + }, + { + "exception": "Not enough data", + "arguments": { + "m": 2 + } + }, + { + "exception": "Not enough data", + "arguments": { + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ] + } + }, + { + "description": "Non OP_INT chunk (m)", + "exception": "Output is invalid", + "arguments": { + "output": "OP_RESERVED" + } + }, + { + "description": "Non OP_INT chunk (n)", + "exception": "Output is invalid", + "arguments": { + "output": "OP_1 OP_RESERVED" + } + }, + { + "description": "Missing OP_CHECKMULTISIG", + "exception": "Output is invalid", + "arguments": { + "output": "OP_1 OP_2 OP_RESERVED" + } + }, + { + "description": "m is 0", + "exception": "Output is invalid", + "arguments": { + "output": "OP_0 OP_2 OP_CHECKMULTISIG" + } + }, + { + "description": "n is 0 (m > n)", + "exception": "Output is invalid", + "arguments": { + "output": "OP_2 OP_0 OP_CHECKMULTISIG" + } + }, + { + "description": "m > n", + "exception": "Output is invalid", + "arguments": { + "output": "OP_3 OP_2 OP_CHECKMULTISIG" + } + }, + { + "description": "n !== output pubkeys", + "exception": "Output is invalid", + "arguments": { + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_2 OP_CHECKMULTISIG" + } + }, + { + "description": "Non-canonical output public key", + "exception": "Output is invalid", + "arguments": { + "output": "OP_1 ffff OP_1 OP_CHECKMULTISIG" + } + }, + { + "exception": "n mismatch", + "arguments": { + "n": 2, + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_1 OP_CHECKMULTISIG" + } + }, + { + "exception": "m mismatch", + "arguments": { + "m": 2, + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_1 OP_CHECKMULTISIG" + } + }, + { + "exception": "Pubkeys mismatch", + "arguments": { + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000002 OP_1 OP_CHECKMULTISIG" + } + }, + { + "exception": "Pubkey count mismatch", + "arguments": { + "m": 2, + "n": 3, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ] + } + }, + { + "exception": "Pubkey count cannot be less than m", + "arguments": { + "m": 4, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ] + } + }, + { + "exception": "Not enough signatures provided", + "arguments": { + "m": 2, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "signatures": [ + "300602010002010001" + ] + } + }, + { + "exception": "Too many signatures provided", + "arguments": { + "m": 2, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "signatures": [ + "300602010002010001", + "300602010002010001", + "300602010002010001" + ] + } + }, + { + "description": "Missing OP_0", + "exception": "Input is invalid", + "arguments": { + "m": 2, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "input": "OP_RESERVED" + } + }, + { + "exception": "Input has invalid signature\\(s\\)", + "arguments": { + "m": 1, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "input": "OP_0 ffffffffffffffff" + } + } + ], + "dynamic": { + "depends": { + "m": [ "output" ], + "n": [ "output", [ "m", "pubkeys" ] ], + "output": [ "output", [ "m", "pubkeys" ] ], + "pubkeys": [ "output" ], + "signatures": [ ["input", "output"] ], + "input": [ ["signatures", "output"] ], + "witness": [ ["input", "output"] ] + }, + "details": [ + { + "description": "p2ms", + "m": 2, + "n": 3, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002", + "030000000000000000000000000000000000000000000000000000000000000003" + ], + "signatures": [ + "300602010002010001", + "300602010102010001" + ], + "input": "OP_0 300602010002010001 300602010102010001", + "witness": [] + } + ] + } +} diff --git a/test/fixtures/p2pk.json b/test/fixtures/p2pk.json new file mode 100644 index 0000000..ff0bbcd --- /dev/null +++ b/test/fixtures/p2pk.json @@ -0,0 +1,152 @@ +{ + "valid": [ + { + "description": "output from output", + "arguments": { + "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG" + }, + "expected": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signatures": null, + "input": null, + "witness": null + } + }, + { + "description": "output from pubkey", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001" + }, + "expected": { + "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", + "signatures": null, + "input": null, + "witness": null + } + }, + { + "description": "input/output from output/signature", + "arguments": { + "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", + "signature": "300602010002010001" + }, + "expected": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "input": "300602010002010001", + "witness": [] + } + }, + { + "description": "input/output from pubkey/signature", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "300602010002010001" + }, + "expected": { + "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", + "input": "300602010002010001", + "witness": [] + } + }, + { + "description": "input/output from input/output", + "arguments": { + "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", + "input": "300602010002010001" + }, + "expected": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "300602010002010001", + "witness": [] + } + } + ], + "invalid": [ + { + "exception": "Not enough data", + "arguments": {} + }, + { + "exception": "Not enough data", + "arguments": { + "input": "300602010002010001" + } + }, + { + "exception": "Not enough data", + "arguments": { + "signature": "300602010002010001" + } + }, + { + "description": "Non-canonical signature", + "exception": "Expected property \"signature\" of type \\?isCanonicalScriptSignature, got Buffer", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "3044" + } + }, + { + "description": "Unexpected OP_RESERVED", + "exception": "Output is invalid", + "arguments": { + "output": "OP_RESERVED" + } + }, + { + "description": "Non-canonical output public key", + "exception": "Output pubkey is invalid", + "arguments": { + "output": "ffff OP_CHECKSIG" + } + }, + { + "description": "Unexpected OP_0 (at end)", + "exception": "Output is invalid", + "arguments": { + "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG OP_0" + } + }, + { + "exception": "Pubkey mismatch", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "output": "030000000000000000000000000000000000000000000000000000000000000002 OP_CHECKSIG" + } + }, + { + "description": "Too many chunks", + "exception": "Input is invalid", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "input": "300602010002010001 OP_RESERVED" + } + }, + { + "exception": "Input has invalid signature", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "input": "ffffffffffffffff" + } + } + ], + "dynamic": { + "depends": { + "output": [ "pubkey" ], + "pubkey": [ "output" ], + "signature": [ ["input", "output"] ], + "input": [ ["signature", "output"] ], + "witness": [ ["input", "output"] ] + }, + "details": [ + { + "description": "p2pk", + "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "300602010002010001", + "input": "300602010002010001", + "witness": [] + } + ] + } +} diff --git a/test/fixtures/p2pkh.json b/test/fixtures/p2pkh.json new file mode 100644 index 0000000..7d47152 --- /dev/null +++ b/test/fixtures/p2pkh.json @@ -0,0 +1,214 @@ +{ + "valid": [ + { + "description": "output from address", + "arguments": { + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh" + }, + "expected": { + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", + "signature": null, + "input": null, + "witness": null + } + }, + { + "description": "output from hash", + "arguments": { + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1" + }, + "expected": { + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", + "signature": null, + "input": null, + "witness": null + } + }, + { + "description": "output from output", + "arguments": { + "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG" + }, + "expected": { + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "signature": null, + "input": null, + "witness": null + } + }, + { + "description": "output from pubkey", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001" + }, + "expected": { + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", + "signature": null, + "input": null, + "witness": null + } + }, + { + "description": "input/output from pubkey/signature", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "300602010002010001" + }, + "expected": { + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", + "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001", + "witness": [] + } + }, + { + "description": "input/output from input", + "arguments": { + "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001" + }, + "expected": { + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "300602010002010001", + "witness": [] + } + } + ], + "invalid": [ + { + "exception": "Not enough data", + "arguments": {} + }, + { + "exception": "Not enough data", + "arguments": { + "signature": "300602010002010001" + } + }, + { + "description": "Unexpected OP_RESERVED", + "exception": "Output is invalid", + "arguments": { + "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_RESERVED" + } + }, + { + "description": "Unexpected OP_DUP", + "exception": "Output is invalid", + "arguments": { + "output": "OP_DUP OP_DUP 168b992bcfc44050310b3a94bd0771136d0b28d137 OP_EQUALVERIFY" + } + }, + { + "description": "Hash too short (too many chunks)", + "exception": "Output is invalid", + "arguments": { + "output": "OP_DUP OP_DUP 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_TRUE OP_EQUALVERIFY" + } + }, + { + "description": "Non-minimally encoded (non BIP62 compliant)", + "exception": "Expected property \"output\" of type Buffer\\(Length: 25\\), got Buffer\\(Length: 26\\)", + "arguments": { + "outputHex": "76a94c14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" + } + }, + { + "exception": "Pubkey mismatch", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000002" + } + }, + { + "exception": "Input has invalid signature", + "arguments": { + "input": "ffffffffffffffffff 030000000000000000000000000000000000000000000000000000000000000001" + } + }, + { + "exception": "Input has invalid pubkey", + "arguments": { + "input": "300602010002010001 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "description": "Input has unexpected data", + "exception": "Input is invalid", + "arguments": { + "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001 ffff" + } + }, + { + "description": "H(pubkey) != H", + "exception": "Hash mismatch", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "hash": "ffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "description": "address.hash != H", + "exception": "Hash mismatch", + "arguments": { + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + "hash": "ffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "description": "address.hash != output.hash", + "exception": "Hash mismatch", + "arguments": { + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + "output": "OP_DUP OP_HASH160 ffffffffffffffffffffffffffffffffffffffff OP_EQUALVERIFY OP_CHECKSIG" + } + }, + { + "description": "output.hash != H", + "exception": "Hash mismatch", + "arguments": { + "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", + "hash": "ffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "description": "H(input.pubkey) != H", + "exception": "Hash mismatch", + "arguments": { + "hash": "ffffffffffffffffffffffffffffffffffffffff", + "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001" + } + } + ], + "dynamic": { + "depends": { + "address": [ "address", "output", "hash", "pubkey", "input" ], + "hash": [ "address", "output", "hash", "pubkey", "input" ], + "output": [ "address", "output", "hash", "pubkey", "input" ], + "pubkey": [ "input" ], + "signature": [ "input" ], + "input": [ [ "pubkey", "signature" ] ], + "witness": [ "input" ] + }, + "details": [ + { + "description": "p2pkh", + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "300602010002010001", + "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001", + "witness": [] + } + ] + } +} diff --git a/test/fixtures/p2sh.json b/test/fixtures/p2sh.json new file mode 100644 index 0000000..c87c8a8 --- /dev/null +++ b/test/fixtures/p2sh.json @@ -0,0 +1,346 @@ +{ + "valid": [ + { + "description": "p2sh-*, out (from address)", + "arguments": { + "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT" + }, + "expected": { + "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", + "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", + "redeem": null, + "input": null, + "witness": null + } + }, + { + "description": "p2sh-*, out (from hash)", + "arguments": { + "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086" + }, + "expected": { + "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", + "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", + "redeem": null, + "input": null, + "witness": null + } + }, + { + "description": "p2sh-*, out (from output)", + "arguments": { + "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL" + }, + "expected": { + "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", + "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", + "redeem": null, + "input": null, + "witness": null + } + }, + { + "description": "p2sh-p2pkh, out (from redeem)", + "arguments": { + "redeem": { + "address": "this is P2PKH context, unknown and ignored by P2SH", + "output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG" + } + }, + "expected": { + "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", + "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", + "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", + "input": null, + "witness": null + } + }, + { + "description": "p2sh-p2wpkh, out (from redeem)", + "arguments": { + "redeem": { + "hash": "this is P2WPKH context, unknown and ignored by P2SH", + "output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400" + } + }, + "expected": { + "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", + "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", + "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", + "input": null, + "witness": null + } + }, + { + "description": "p2sh-p2pk, out (from redeem)", + "arguments": { + "redeem": { + "output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG", + "pubkey": "this is P2WPKH context, unknown and ignored by P2SH" + } + }, + "expected": { + "address": "36TibC8RrPB9WrBdPoGXhHqDHJosyFVtVQ", + "hash": "3454c084887afe854e80221c69d6282926f809c4", + "output": "OP_HASH160 3454c084887afe854e80221c69d6282926f809c4 OP_EQUAL", + "input": null, + "witness": null + } + }, + { + "description": "p2sh-p2pkh, in and out (from redeem)", + "arguments": { + "redeem": { + "output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", + "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + } + }, + "expected": { + "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", + "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", + "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", + "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 76a914c30afa58ae0673b00a45b5c17dff4633780f140088ac", + "witness": [] + } + }, + { + "description": "p2sh-p2wpkh, in and out (from redeem w/ witness)", + "arguments": { + "redeem": { + "output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + ] + } + }, + "expected": { + "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", + "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", + "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", + "input": "0014c30afa58ae0673b00a45b5c17dff4633780f1400", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + ] + } + }, + { + "description": "p2sh-p2pk, in and out (from input)", + "arguments": { + "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 2103e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058ac" + }, + "expected": { + "address": "36TibC8RrPB9WrBdPoGXhHqDHJosyFVtVQ", + "hash": "3454c084887afe854e80221c69d6282926f809c4", + "output": "OP_HASH160 3454c084887afe854e80221c69d6282926f809c4 OP_EQUAL", + "redeem": { + "output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG", + "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "witness": [] + }, + "witness": [] + } + }, + { + "description": "p2sh-p2wpkh, in and out (from input AND witness)", + "arguments": { + "input": "0014c30afa58ae0673b00a45b5c17dff4633780f1400", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + ] + }, + "expected": { + "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", + "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", + "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", + "redeem": { + "output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + ] + } + } + } + ], + "invalid": [ + { + "exception": "Not enough data", + "arguments": {} + }, + { + "description": "Non-minimally encoded (non BIP62 compliant)", + "exception": "Expected property \"output\" of type Buffer\\(Length: 23\\), got Buffer\\(Length: 24\\)", + "arguments": { + "outputHex": "a94c14c286a1af0947f58d1ad787385b1c2c4a976f9e7187" + } + }, + { + "description": "Expected OP_HASH160", + "exception": "Output is invalid", + "arguments": { + "output": "OP_HASH256 ffffffffffffffffffffffffffffffffffffffff OP_EQUAL" + } + }, + { + "description": "Unexpected OP_RESERVED", + "exception": "Output is invalid", + "arguments": { + "output": "OP_HASH256 ffffffffffffffffffffffffffffffffffffff OP_EQUAL OP_RESERVED" + } + }, + { + "description": "address.hash != H", + "exception": "Hash mismatch", + "arguments": { + "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", + "hash": "ffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "description": "address.hash != output.hash", + "exception": "Hash mismatch", + "arguments": { + "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", + "output": "OP_HASH160 ffffffffffffffffffffffffffffffffffffffff OP_EQUAL" + } + }, + { + "description": "output.hash != H", + "exception": "Hash mismatch", + "arguments": { + "hash": "ffffffffffffffffffffffffffffffffffffffff", + "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL" + } + }, + { + "description": "H(redeem.output) != H", + "exception": "Hash mismatch", + "arguments": { + "hash": "ffffffffffffffffffffffffffffffffffffffff", + "redeem": { + "output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG" + } + } + }, + { + "exception": "Input too short", + "arguments": { + "input": "" + } + }, + { + "exception": "Input too short", + "arguments": { + "inputHex": "01ff02ff" + } + }, + { + "exception": "Input is invalid", + "arguments": { + "input": "OP_0 OP_0" + } + }, + { + "exception": "Redeem.input mismatch", + "arguments": { + "input": "OP_0 02ffff", + "redeem": { + "input": "OP_CHECKSIG", + "output": "ffff" + } + } + }, + { + "exception": "Redeem.output mismatch", + "arguments": { + "input": "OP_0 02ffff", + "redeem": { + "input": "OP_0", + "output": "fff3" + } + } + }, + { + "exception": "Redeem.output too short", + "arguments": { + "redeem": { + "input": "OP_0", + "output": "" + } + } + }, + { + "exception": "Redeem.output too short", + "arguments": { + "inputHex": "021000" + } + }, + { + "exception": "Hash mismatch", + "arguments": { + "hash": "ffffffffffffffffffffffffffffffffffffffff", + "redeem": { + "input": "OP_0", + "output": "ffff" + } + } + }, + { + "exception": "Empty input", + "arguments": { + "inputHex": "01ff" + } + } + ], + "dynamic": { + "depends": { + "address": [ "address", "output", "hash", "redeem.output", [ "input", "witness" ] ], + "hash": [ "address", "output", "hash", "redeem.output", [ "input", "witness" ] ], + "output": [ "address", "output", "hash", "redeem.output", [ "input", "witness" ] ], + "redeem.output": [ [ "input", "witness" ] ], + "redeem.input": [ [ "input", "witness" ] ], + "redeem.witness": [ [ "input", "witness" ] ], + "input": [ "redeem" ], + "witness": [ "redeem" ] + }, + "details": [ + { + "description": "p2sh-p2pkh", + "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", + "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", + "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", + "redeem": { + "output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", + "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", + "witness": [] + }, + "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 76a914c30afa58ae0673b00a45b5c17dff4633780f140088ac", + "witness": [] + }, + { + "description": "p2sh-p2wpkh", + "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", + "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", + "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", + "redeem": { + "output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + ] + }, + "input": "0014c30afa58ae0673b00a45b5c17dff4633780f1400", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + ] + } + ] + } +} diff --git a/test/fixtures/p2wpkh.json b/test/fixtures/p2wpkh.json new file mode 100644 index 0000000..84e0c08 --- /dev/null +++ b/test/fixtures/p2wpkh.json @@ -0,0 +1,195 @@ +{ + "valid": [ + { + "description": "output from address", + "arguments": { + "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d" + }, + "expected": { + "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727", + "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", + "signature": null, + "input": null, + "witness": null + } + }, + { + "description": "output from hash", + "arguments": { + "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727" + }, + "expected": { + "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", + "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", + "signature": null, + "input": null, + "witness": null + } + }, + { + "description": "output from output", + "arguments": { + "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727" + }, + "expected": { + "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", + "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727", + "signature": null, + "input": null, + "witness": null + } + }, + { + "description": "output from pubkey", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001" + }, + "expected": { + "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", + "signature": null, + "input": null, + "witness": null + } + }, + { + "description": "witness/output from pubkey/signature", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "300602010002010001" + }, + "expected": { + "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", + "input": "", + "witness": [ + "300602010002010001", + "030000000000000000000000000000000000000000000000000000000000000001" + ] + } + }, + { + "description": "witness/output from witness", + "arguments": { + "witness": [ + "300602010002010001", + "030000000000000000000000000000000000000000000000000000000000000001" + ] + }, + "expected": { + "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "300602010002010001", + "input": "" + } + } + ], + "invalid": [ + { + "exception": "Not enough data", + "arguments": {} + }, + { + "exception": "Not enough data", + "arguments": { + "signature": "300602010002010001" + } + }, + { + "exception": "Output is invalid", + "description": "Unexpected OP", + "arguments": { + "output": "OP_RESERVED ea6d525c0c955d90d3dbd29a81ef8bfb79003727" + } + }, + { + "exception": "Pubkey mismatch", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "witness": [ + "300602010002010001", + "030000000000000000000000000000000000000000000000000000000000000002" + ] + } + }, + { + "exception": "Hash mismatch", + "arguments": { + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "hash": "ffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "exception": "Hash mismatch", + "arguments": { + "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", + "hash": "ffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "exception": "Hash mismatch", + "arguments": { + "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", + "hash": "ffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "exception": "Hash mismatch", + "arguments": { + "hash": "ffffffffffffffffffffffffffffffffffffffff", + "witness": [ + "300602010002010001", + "030000000000000000000000000000000000000000000000000000000000000001" + ] + } + }, + { + "exception": "Input has invalid signature", + "arguments": { + "witness": [ + "ffffffffffffffffff", + "030000000000000000000000000000000000000000000000000000000000000001" + ] + } + }, + { + "exception": "Input has invalid pubkey", + "arguments": { + "witness": [ + "300602010002010001", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ] + } + } + ], + "dynamic": { + "depends": { + "address": [ "address", "output", "hash", "pubkey", "witness" ], + "hash": [ "address", "output", "hash", "pubkey", "witness" ], + "output": [ "address", "output", "hash", "pubkey", "witness" ], + "pubkey": [ "witness" ], + "signature": [ "witness" ], + "input": [ "witness" ], + "witness": [ [ "pubkey", "signature" ] ] + }, + "details": [ + { + "description": "p2wpkh", + "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", + "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", + "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", + "signature": "300602010002010001", + "input": "", + "witness": [ + "300602010002010001", + "030000000000000000000000000000000000000000000000000000000000000001" + ] + } + ] + } +} diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json new file mode 100644 index 0000000..c123d64 --- /dev/null +++ b/test/fixtures/p2wsh.json @@ -0,0 +1,326 @@ +{ + "valid": [ + { + "description": "p2wsh-*, out (from address)", + "arguments": { + "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q" + }, + "expected": { + "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", + "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", + "redeem": null, + "input": null, + "witness": null + } + }, + { + "description": "p2wsh-*, out (from hash)", + "arguments": { + "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0" + }, + "expected": { + "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", + "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", + "redeem": null, + "input": null, + "witness": null + } + }, + { + "description": "p2wsh-*, out (from output)", + "arguments": { + "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0" + }, + "expected": { + "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", + "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", + "redeem": null, + "input": null, + "witness": null + } + }, + { + "description": "p2wsh-p2pkh, out (from redeem)", + "arguments": { + "redeem": { + "address": "this is P2PKH context, unknown and ignored by p2wsh", + "output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG" + } + }, + "expected": { + "address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", + "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", + "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", + "input": null, + "witness": null + } + }, + { + "description": "p2wsh-p2wpkh, out (from redeem)", + "arguments": { + "redeem": { + "hash": "this is P2WPKH context, unknown and ignored by p2wsh", + "output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400" + } + }, + "expected": { + "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", + "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", + "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", + "input": null, + "witness": null + } + }, + { + "description": "p2wsh-p2pk, out (from redeem)", + "arguments": { + "redeem": { + "output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG", + "pubkey": "this is P2WPKH context, unknown and ignored by p2wsh" + } + }, + "expected": { + "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", + "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", + "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", + "input": null, + "witness": null + } + }, + { + "description": "p2wsh-p2pkh, in and out (from redeem, transformed to witness)", + "arguments": { + "redeem": { + "output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", + "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + } + }, + "expected": { + "address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", + "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", + "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", + "redeem": { + "input": "" + }, + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", + "76a914c30afa58ae0673b00a45b5c17dff4633780f140088ac" + ] + } + }, + { + "description": "p2wsh-p2wpkh, in and out (from redeem w/ witness)", + "arguments": { + "redeem": { + "output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + ] + } + }, + "expected": { + "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", + "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", + "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", + "0014c30afa58ae0673b00a45b5c17dff4633780f1400" + ] + } + }, + { + "description": "p2wsh-p2pk, in and out (from witness)", + "arguments": { + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "2103e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058ac" + ] + }, + "expected": { + "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", + "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", + "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", + "redeem": { + "output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG", + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501" + ] + }, + "input": "" + } + }, + { + "description": "p2wsh-p2wpkh, in and out (from witness)", + "arguments": { + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", + "0014c30afa58ae0673b00a45b5c17dff4633780f1400" + ] + }, + "expected": { + "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", + "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", + "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", + "redeem": { + "output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + ] + } + } + } + ], + "invalid": [ + { + "exception": "Not enough data", + "arguments": {} + }, + { + "description": "address.hash != H", + "exception": "Hash mismatch", + "arguments": { + "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", + "hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "description": "address.hash != output.hash", + "exception": "Hash mismatch", + "arguments": { + "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", + "output": "OP_0 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + }, + { + "description": "output.hash != H", + "exception": "Hash mismatch", + "arguments": { + "hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0" + } + }, + { + "description": "H(redeem.output) != H", + "exception": "Hash mismatch", + "arguments": { + "hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "redeem": { + "output": "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058 OP_CHECKSIG" + } + } + }, + { + "exception": "Output is invalid", + "arguments": { + "output": "OP_HASH256 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff OP_EQUAL" + } + }, + { + "exception": "Redeem.output is invalid", + "arguments": { + "redeem": { + "output": "" + } + } + }, + { + "exception": "Non push-only scriptSig", + "arguments": { + "redeem": { + "output": "OP_TRUE", + "input": "OP_HASH256" + } + } + }, + { + "exception": "Witness and redeem.output mismatch", + "arguments": { + "redeem": { + "output": "OP_TRUE", + "input": "OP_0" + }, + "witness": [ + "02ffff" + ] + } + }, + { + "exception": "Witness and redeem.witness mismatch", + "arguments": { + "redeem": { + "output": "OP_TRUE", + "witness": [ + "01" + ] + }, + "witness": [ + "00", + "02ffff" + ] + } + } + ], + "dynamic": { + "depends": { + "address": [ "address", "output", "hash", "redeem.output", "witness" ], + "hash": [ "address", "output", "hash", "redeem.output", "witness" ], + "output": [ "address", "output", "hash", "redeem.output", "witness" ], + "redeem.output": [ "witness" ], + "redeem.input": [ [ "input", "witness" ], "witness" ], + "input": [ "witness" ], + "witness": [ "redeem" ] + }, + "details": [ + { + "description": "p2wsh-p2pkh", + "disabled": [ + "redeem.input" + ], + "address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", + "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", + "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", + "redeem": { + "output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", + "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", + "witness": null + }, + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", + "76a914c30afa58ae0673b00a45b5c17dff4633780f140088ac" + ] + }, + { + "description": "p2wsh-p2wpkh", + "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", + "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", + "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", + "redeem": { + "output": "OP_0 c30afa58ae0673b00a45b5c17dff4633780f1400", + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058" + ] + }, + "input": "", + "witness": [ + "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501", + "03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058", + "0014c30afa58ae0673b00a45b5c17dff4633780f1400" + ] + } + ] + } +} diff --git a/test/payments.js b/test/payments.js new file mode 100644 index 0000000..14382e5 --- /dev/null +++ b/test/payments.js @@ -0,0 +1,65 @@ +/* global describe, it */ + +const assert = require('assert') +const u = require('./payments.utils') + +;['p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { + describe(p, function () { + const fn = require('../src/payments/' + p) + const fixtures = require('./fixtures/' + p) + + fixtures.valid.forEach(function (f, i) { + const args = u.preform(f.arguments) + + it(f.description + ' as expected', function () { + const actual = fn(args, f.options) + u.equate(actual, f.expected, f.arguments) + }) + + it(f.description + ' as expected (no validation)', function () { + const actual = fn(args, Object.assign({}, f.options, { + validate: false + })) + + u.equate(actual, f.expected, f.arguments) + }) + }) + + fixtures.invalid.forEach(function (f) { + it('throws ' + (f.description || f.exception), function () { + const args = u.preform(f.arguments) + + assert.throws(function () { + fn(args, f.options) + }, new RegExp(f.exception)) + }) + }) + + // cross-verify dynamically too + if (!fixtures.dynamic) return + const { depends, details } = fixtures.dynamic + + details.forEach(function (f) { + const detail = u.preform(f) + const disabled = {} + if (f.disabled) f.disabled.forEach(function (k) { disabled[k] = true }) + + for (let key in depends) { + if (key in disabled) continue + const dependencies = depends[key] + + dependencies.forEach(function (dependency) { + if (!Array.isArray(dependency)) dependency = [dependency] + + const args = {} + dependency.forEach(function (d) { u.from(d, detail, args) }) + const expected = u.from(key, detail) + + it(f.description + ', ' + key + ' derives from ' + JSON.stringify(dependency), function () { + u.equate(fn(args), expected) + }) + }) + } + }) + }) +}) diff --git a/test/payments.utils.js b/test/payments.utils.js new file mode 100644 index 0000000..22001e9 --- /dev/null +++ b/test/payments.utils.js @@ -0,0 +1,128 @@ +let t = require('assert') +let bscript = require('../src/script') +let bnetworks = require('../src/networks') + +function tryHex (x) { + if (Buffer.isBuffer(x)) return x.toString('hex') + if (Array.isArray(x)) return x.map(tryHex) + return x +} +function tryASM (x) { + if (Buffer.isBuffer(x)) return bscript.toASM(x) + return x +} +function asmToBuffer (x) { + if (x === '') return Buffer.alloc(0) + return bscript.fromASM(x) +} +function carryOver (a, b) { + for (let k in b) { + if (k in a && k === 'redeem') { + carryOver(a[k], b[k]) + continue + } + + // don't, the value was specified + if (k in a) continue + + // otherwise, expect match + a[k] = b[k] + } +} + +function equateBase (a, b, context) { + if ('output' in b) t.strictEqual(tryASM(a.output), tryASM(b.output), `Inequal ${context}output`) + if ('input' in b) t.strictEqual(tryASM(a.input), tryASM(b.input), `Inequal ${context}input`) + if ('witness' in b) t.deepEqual(tryHex(a.witness), tryHex(b.witness), `Inequal ${context}witness`) +} + +function equate (a, b, args) { + b = Object.assign({}, b) + carryOver(b, args) + + // by null, we mean 'undefined', but JSON + if (b.input === null) b.input = undefined + if (b.output === null) b.output = undefined + if (b.witness === null) b.witness = undefined + if (b.redeem) { + if (b.redeem.input === null) b.redeem.input = undefined + if (b.redeem.output === null) b.redeem.output = undefined + if (b.redeem.witness === null) b.redeem.witness = undefined + } + + equateBase(a, b, '') + if (b.redeem) equateBase(a.redeem, b.redeem, 'redeem.') + if (b.network) t.deepEqual(a.network, b.network, 'Inequal *.network') + + // contextual + if (b.signature === null) b.signature = undefined + if ('address' in b) t.strictEqual(a.address, b.address, 'Inequal *.address') + if ('hash' in b) t.strictEqual(tryHex(a.hash), tryHex(b.hash), 'Inequal *.hash') + if ('pubkey' in b) t.strictEqual(tryHex(a.pubkey), tryHex(b.pubkey), 'Inequal *.pubkey') + if ('signature' in b) t.strictEqual(tryHex(a.signature), tryHex(b.signature), 'Inequal signature') + if ('m' in b) t.strictEqual(a.m, b.m, 'Inequal *.m') + if ('n' in b) t.strictEqual(a.n, b.n, 'Inequal *.n') + if ('pubkeys' in b) t.deepEqual(tryHex(a.pubkeys), tryHex(b.pubkeys), 'Inequal *.pubkeys') + if ('signatures' in b) t.deepEqual(tryHex(a.signatures), tryHex(b.signatures), 'Inequal *.signatures') +} + +function preform (x) { + x = Object.assign({}, x) + + if (x.network) x.network = bnetworks[x.network] + if (typeof x.inputHex === 'string') { + x.input = Buffer.from(x.inputHex, 'hex') + delete x.inputHex + } + if (typeof x.outputHex === 'string') { + x.output = Buffer.from(x.outputHex, 'hex') + delete x.outputHex + } + if (typeof x.output === 'string') x.output = asmToBuffer(x.output) + if (typeof x.input === 'string') x.input = asmToBuffer(x.input) + if (Array.isArray(x.witness)) { + x.witness = x.witness.map(function (y) { + return Buffer.from(y, 'hex') + }) + } + + if (x.hash) x.hash = Buffer.from(x.hash, 'hex') + if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex') + if (x.signature) x.signature = Buffer.from(x.signature, 'hex') + if (x.pubkeys) x.pubkeys = x.pubkeys.map(function (y) { return Buffer.from(y, 'hex') }) + if (x.signatures) x.signatures = x.signatures.map(function (y) { return Number.isFinite(y) ? y : Buffer.from(y, 'hex') }) + if (x.redeem) { + if (typeof x.redeem.input === 'string') x.redeem.input = asmToBuffer(x.redeem.input) + if (typeof x.redeem.output === 'string') x.redeem.output = asmToBuffer(x.redeem.output) + if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(function (y) { return Buffer.from(y, 'hex') }) + x.redeem.network = bnetworks[x.redeem.network] || x.network || bnetworks.bitcoin + } + + return x +} + +function from (path, object, result) { + path = path.split('.') + result = result || {} + + let r = result + path.forEach((k, i) => { + if (i < path.length - 1) { + r[k] = r[k] || {} + + // recurse + r = r[k] + object = object[k] + } else { + r[k] = object[k] + } + }) + + return result +} + +module.exports = { + from, + equate, + preform +} From 6c957533d6dd77cb28d16c6817236028698c8d7d Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 6 Jun 2018 00:16:52 +1000 Subject: [PATCH 053/568] index: expose payments --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index 3630bb8..603c64f 100644 --- a/src/index.js +++ b/src/index.js @@ -15,5 +15,6 @@ module.exports = { crypto: require('./crypto'), networks: require('./networks'), opcodes: require('bitcoin-ops'), + payments: require('./payments'), script: script } From 8197f9d7040671375f250f7656974a1ee28709e7 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 6 Jun 2018 00:09:55 +1000 Subject: [PATCH 054/568] less strict on inputs --- test/fixtures/p2pk.json | 12 ------------ test/payments.js | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/test/fixtures/p2pk.json b/test/fixtures/p2pk.json index ff0bbcd..7ede5f4 100644 --- a/test/fixtures/p2pk.json +++ b/test/fixtures/p2pk.json @@ -66,18 +66,6 @@ "exception": "Not enough data", "arguments": {} }, - { - "exception": "Not enough data", - "arguments": { - "input": "300602010002010001" - } - }, - { - "exception": "Not enough data", - "arguments": { - "signature": "300602010002010001" - } - }, { "description": "Non-canonical signature", "exception": "Expected property \"signature\" of type \\?isCanonicalScriptSignature, got Buffer", diff --git a/test/payments.js b/test/payments.js index 14382e5..5722d88 100644 --- a/test/payments.js +++ b/test/payments.js @@ -26,7 +26,7 @@ const u = require('./payments.utils') }) fixtures.invalid.forEach(function (f) { - it('throws ' + (f.description || f.exception), function () { + it('throws ' + f.exception + (f.description ? ('for ' + f.description) : ''), function () { const args = u.preform(f.arguments) assert.throws(function () { From 38efc35fdfb0b4e183cfd0c4309a2e0586d186b3 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 27 Jun 2018 11:22:35 +1000 Subject: [PATCH 055/568] payments: dont always require output data --- src/payments/p2ms.js | 3 ++- src/payments/p2pk.js | 4 +++- src/payments/p2sh.js | 18 ++++++++++-------- src/payments/p2wsh.js | 33 ++++++++++++++++++++++----------- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index 9bce722..4fba429 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -20,7 +20,8 @@ function stacksEqual (a, b) { function p2ms (a, opts) { if ( !a.output && - !(a.pubkeys && a.m !== undefined) + !(a.pubkeys && a.m !== undefined) && + !a.signatures ) throw new TypeError('Not enough data') opts = opts || { validate: true } diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index b0408aa..eacccd6 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -11,7 +11,9 @@ let BITCOIN_NETWORK = require('../networks').bitcoin function p2pk (a, opts) { if ( !a.output && - !a.pubkey + !a.pubkey && + !a.input && + !a.signature ) throw new TypeError('Not enough data') opts = opts || { validate: true } diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index c67d921..b26119d 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -37,7 +37,7 @@ function p2sh (a, opts) { redeem: typef.maybe({ network: typef.maybe(typef.Object), - output: typef.Buffer, + output: typef.maybe(typef.Buffer), input: typef.maybe(typef.Buffer), witness: typef.maybe(typef.arrayOf(typef.Buffer)) }), @@ -86,7 +86,7 @@ function p2sh (a, opts) { return _redeem() }) lazy.prop(o, 'input', function () { - if (!a.redeem || !a.redeem.input) return + if (!a.redeem || !a.redeem.input || !a.redeem.output) return return bscript.compile([].concat( bscript.decompile(a.redeem.input), a.redeem.output @@ -124,13 +124,15 @@ function p2sh (a, opts) { // inlined to prevent 'no-inner-declarations' failing const checkRedeem = function (redeem) { // is the redeem output empty/invalid? - const decompile = bscript.decompile(redeem.output) - if (!decompile || decompile.length < 1) throw new TypeError('Redeem.output too short') + if (redeem.output) { + const decompile = bscript.decompile(redeem.output) + if (!decompile || decompile.length < 1) throw new TypeError('Redeem.output too short') - // match hash against other sources - const hash2 = bcrypto.hash160(redeem.output) - if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') - else hash = hash2 + // match hash against other sources + const hash2 = bcrypto.hash160(redeem.output) + if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + else hash = hash2 + } if (redeem.input) { const hasInput = redeem.input.length > 0 diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index 8e2e4fe..6f53484 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -40,7 +40,7 @@ function p2wsh (a, opts) { redeem: typef.maybe({ input: typef.maybe(typef.Buffer), network: typef.maybe(typef.Object), - output: typef.Buffer, + output: typef.maybe(typef.Buffer), witness: typef.maybe(typef.arrayOf(typef.Buffer)) }), input: typef.maybe(typef.BufferN(0)), @@ -83,8 +83,14 @@ function p2wsh (a, opts) { }) lazy.prop(o, 'witness', function () { // transform redeem input to witness stack? - if (a.redeem && a.redeem.input && a.redeem.input.length > 0) { - let stack = bscript.toStack(_rchunks()) + if ( + a.redeem && + a.redeem.input && + a.redeem.input.length > 0 && + a.redeem.output && + a.redeem.output.length > 0 + ) { + const stack = bscript.toStack(_rchunks()) // assign, and blank the existing input o.redeem = Object.assign({ witness: stack }, a.redeem) @@ -93,6 +99,7 @@ function p2wsh (a, opts) { } if (!a.redeem) return + if (!a.redeem.output) return if (!a.redeem.witness) return return [].concat(a.redeem.witness, a.redeem.output) }) @@ -117,7 +124,7 @@ function p2wsh (a, opts) { a.output.length !== 34 || a.output[0] !== OPS.OP_0 || a.output[1] !== 0x20) throw new TypeError('Output is invalid') - let hash2 = a.output.slice(2) + const hash2 = a.output.slice(2) if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') else hash = hash2 } @@ -129,22 +136,26 @@ function p2wsh (a, opts) { if ( a.redeem.input && a.redeem.input.length > 0 && - a.redeem.witness) throw new TypeError('Ambiguous witness source') + a.redeem.witness && + a.redeem.witness.length > 0 + ) throw new TypeError('Ambiguous witness source') // is the redeem output non-empty? - if (bscript.decompile(a.redeem.output).length === 0) throw new TypeError('Redeem.output is invalid') + if (a.redeem.output) { + if (bscript.decompile(a.redeem.output).length === 0) throw new TypeError('Redeem.output is invalid') - // match hash against other sources - let hash2 = bcrypto.sha256(a.redeem.output) - if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') - else hash = hash2 + // match hash against other sources + let hash2 = bcrypto.sha256(a.redeem.output) + if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + else hash = hash2 + } if (a.redeem.input && !bscript.isPushOnly(_rchunks())) throw new TypeError('Non push-only scriptSig') if (a.witness && a.redeem.witness && !stacksEqual(a.witness, a.redeem.witness)) throw new TypeError('Witness and redeem.witness mismatch') } if (a.witness) { - if (a.redeem && !a.redeem.output.equals(a.witness[a.witness.length - 1])) throw new TypeError('Witness and redeem.output mismatch') + if (a.redeem && a.redeem.output && !a.redeem.output.equals(a.witness[a.witness.length - 1])) throw new TypeError('Witness and redeem.output mismatch') } } From 256656f766a9e1f0019c8aecc45e94085e7c4916 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 27 Jun 2018 13:54:47 +1000 Subject: [PATCH 056/568] use payments in integration tests --- test/integration/_regtest.js | 6 +-- test/integration/addresses.js | 73 +++++++++++++------------------- test/integration/bip32.js | 6 +-- test/integration/stealth.js | 7 +-- test/integration/transactions.js | 64 +++++++++++++--------------- 5 files changed, 63 insertions(+), 93 deletions(-) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 4cdb6da..f851d2c 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -67,12 +67,8 @@ function verify (txo, callback) { }) } -// TODO: remove -const baddress = bitcoin.address -const bcrypto = bitcoin.crypto function getAddress (node, network) { - network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) + return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address } function randomAddress () { diff --git a/test/integration/addresses.js b/test/integration/addresses.js index e7c715b..412e792 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -18,18 +18,10 @@ const LITECOIN = { // deterministic RNG for testing only function rng () { return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } -// TODO: remove -const baddress = bitcoin.address -const bcrypto = bitcoin.crypto -function getAddress (node, network) { - network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) -} - describe('bitcoinjs-lib (addresses)', function () { it('can generate a random address', function () { const keyPair = bitcoin.ECPair.makeRandom({ rng: rng }) - const address = getAddress(keyPair) + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) assert.strictEqual(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64') }) @@ -38,7 +30,7 @@ describe('bitcoinjs-lib (addresses)', function () { const hash = bitcoin.crypto.sha256(Buffer.from('correct horse battery staple')) const keyPair = bitcoin.ECPair.fromPrivateKey(hash) - const address = getAddress(keyPair) + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) // Generating addresses from SHA256 hashes is not secure if the input to the hash function is predictable // Do not use with predictable inputs @@ -47,76 +39,71 @@ describe('bitcoinjs-lib (addresses)', function () { it('can import an address via WIF', function () { const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - const address = getAddress(keyPair) + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) assert.strictEqual(address, '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31') }) - it('can generate a 2-of-3 multisig P2SH address', function () { - const pubKeys = [ + it('can generate a P2SH, pay-to-multisig (2-of-3) address', function () { + const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9' - ].map(function (hex) { return Buffer.from(hex, 'hex') }) - - const redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) // 2 of 3 - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey) + ].map((hex) => Buffer.from(hex, 'hex')) + const { address } = bitcoin.payments.p2sh({ + redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }) + }) assert.strictEqual(address, '36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7') }) it('can generate a SegWit address', function () { const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - - const scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey)) - const address = bitcoin.address.fromOutputScript(scriptPubKey) + const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) assert.strictEqual(address, 'bc1qt97wqg464zrhnx23upykca5annqvwkwujjglky') }) it('can generate a SegWit address (via P2SH)', function () { const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - - const redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey)) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey) + const { address } = bitcoin.payments.p2sh({ + redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) + }) assert.strictEqual(address, '34AgLJhwXrvmkZS1o5TrcdeevMt22Nar53') }) - it('can generate a SegWit 3-of-4 multisig address', function () { - const pubKeys = [ + it('can generate a P2WSH (SegWit), pay-to-multisig (3-of-4) address', function () { + const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', '023e4740d0ba639e28963f3476157b7cf2fb7c6fdf4254f97099cf8670b505ea59', '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9' - ].map(function (hex) { return Buffer.from(hex, 'hex') }) - - const witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) // 3 of 4 - const scriptPubKey = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey) + ].map((hex) => Buffer.from(hex, 'hex')) + const { address } = bitcoin.payments.p2wsh({ + redeem: bitcoin.payments.p2ms({ m: 3, pubkeys }) + }) assert.strictEqual(address, 'bc1q75f6dv4q8ug7zhujrsp5t0hzf33lllnr3fe7e2pra3v24mzl8rrqtp3qul') }) - it('can generate a SegWit 2-of-2 multisig address (via P2SH)', function () { - const pubKeys = [ + it('can generate a P2SH(P2WSH(...)), pay-to-multisig (2-of-2) address', function () { + const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9' - ].map(function (hex) { return Buffer.from(hex, 'hex') }) - - const witnessScript = bitcoin.script.multisig.output.encode(2, pubKeys) // 2 of 2 - const redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey) + ].map((hex) => Buffer.from(hex, 'hex')) + const { address } = bitcoin.payments.p2sh({ + redeem: bitcoin.payments.p2wsh({ + redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }) + }) + }) assert.strictEqual(address, '3P4mrxQfmExfhxqjLnR2Ah4WES5EB1KBrN') }) it('can support the retrieval of transactions for an address (via 3PBP)', function (done) { const keyPair = bitcoin.ECPair.makeRandom() - const address = getAddress(keyPair) + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) dhttp({ method: 'GET', @@ -137,7 +124,7 @@ describe('bitcoinjs-lib (addresses)', function () { const testnet = bitcoin.networks.testnet const keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: rng }) const wif = keyPair.toWIF() - const address = getAddress(keyPair, testnet) + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: testnet }) assert.strictEqual(address, 'mubSzQNtZfDj1YdNP6pNDuZy6zs6GDn61L') assert.strictEqual(wif, 'cRgnQe9MUu1JznntrLaoQpB476M8PURvXVQB5R2eqms5tXnzNsrr') @@ -146,7 +133,7 @@ describe('bitcoinjs-lib (addresses)', function () { it('can generate a Litecoin address', function () { const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: rng }) const wif = keyPair.toWIF() - const address = getAddress(keyPair, LITECOIN) + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: LITECOIN }) assert.strictEqual(address, 'LZJSxZbjqJ2XVEquqfqHg1RQTDdfST5PTn') assert.strictEqual(wif, 'T7A4PUSgTDHecBxW1ZiYFrDNRih2o7M8Gf9xpoCgudPF9gDiNvuS') diff --git a/test/integration/bip32.js b/test/integration/bip32.js index c780267..e6d5721 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -5,12 +5,8 @@ const bip32 = require('bip32') const bip39 = require('bip39') const bitcoin = require('../../') -// TODO: remove -const baddress = bitcoin.address -const bcrypto = bitcoin.crypto function getAddress (node, network) { - network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) + return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address } describe('bitcoinjs-lib (BIP32)', function () { diff --git a/test/integration/stealth.js b/test/integration/stealth.js index 01a933b..813f48b 100644 --- a/test/integration/stealth.js +++ b/test/integration/stealth.js @@ -4,11 +4,8 @@ const assert = require('assert') const bitcoin = require('../../') const ecc = require('tiny-secp256k1') -// TODO: remove -const baddress = bitcoin.address -const bcrypto = bitcoin.crypto -function getAddress (node) { - return baddress.toBase58Check(bcrypto.hash160(node.publicKey), bitcoin.networks.bitcoin.pubKeyHash) +function getAddress (node, network) { + return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address } // vG = (dG \+ sha256(e * dG)G) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 3235851..991e0b6 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -5,14 +5,6 @@ const bitcoin = require('../../') const regtestUtils = require('./_regtest') const regtest = regtestUtils.network -// TODO: remove -const baddress = bitcoin.address -const bcrypto = bitcoin.crypto -function getAddress (node, network) { - network = network || bitcoin.networks.bitcoin - return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash) -} - function rng () { return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64') } @@ -59,18 +51,22 @@ describe('bitcoinjs-lib (transactions)', function () { const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }) const aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng }) + const alice1pkh = bitcoin.payments.p2pkh({ pubkey: alice1.publicKey, network: regtest }) + const alice2pkh = bitcoin.payments.p2pkh({ pubkey: alice2.publicKey, network: regtest }) + const aliceCpkh = bitcoin.payments.p2pkh({ pubkey: aliceChange.publicKey, network: regtest }) + // give Alice 2 unspent outputs - regtestUtils.faucet(getAddress(alice1, regtest), 5e4, function (err, unspent0) { + regtestUtils.faucet(alice1pkh.address, 5e4, function (err, unspent0) { if (err) return done(err) - regtestUtils.faucet(getAddress(alice2, regtest), 7e4, function (err, unspent1) { + regtestUtils.faucet(alice2pkh.address, 7e4, function (err, unspent1) { if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent0.txId, unspent0.vout) // alice1 unspent txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" - txb.addOutput(getAddress(aliceChange, regtest), 1e4) // Alice's change + txb.addOutput(aliceCpkh.address, 1e4) // Alice's change // (in)(4e4 + 2e4) - (out)(1e4 + 3e4) = (fee)2e4 = 20000, this is the miner fee // Alice signs each input with the respective private keys @@ -88,8 +84,9 @@ describe('bitcoinjs-lib (transactions)', function () { this.timeout(30000) const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) + const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: regtest }) - regtestUtils.faucet(getAddress(keyPair, regtest), 2e5, function (err, unspent) { + regtestUtils.faucet(p2pkh.address, 2e5, function (err, unspent) { if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest) @@ -150,20 +147,16 @@ describe('bitcoinjs-lib (transactions)', function () { this.timeout(30000) const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - const pubKeyHash = bitcoin.crypto.hash160(keyPair.publicKey) + const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) + const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }) - const redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash) - const redeemScriptHash = bitcoin.crypto.hash160(redeemScript) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(redeemScriptHash) - const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) - - regtestUtils.faucet(address, 5e4, function (err, unspent) { + regtestUtils.faucet(p2sh.address, 5e4, function (err, unspent) { if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - txb.sign(0, keyPair, redeemScript, null, unspent.value) + txb.sign(0, keyPair, p2sh.redeem.output, null, unspent.value) const tx = txb.build() @@ -190,22 +183,21 @@ describe('bitcoinjs-lib (transactions)', function () { bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }) ] - const pubKeys = keyPairs.map(function (x) { return x.publicKey }) + const pubkeys = keyPairs.map(x => x.publicKey) - const witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) - const redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const p2ms = bitcoin.payments.p2ms({ m: 3, pubkeys, network: regtest }) + const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest }) + const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest }) - regtestUtils.faucet(address, 6e4, function (err, unspent) { + regtestUtils.faucet(p2sh.address, 6e4, function (err, unspent) { if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout) + txb.addInput(unspent.txId, unspent.vout, null, p2sh.output) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4) - txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript) - txb.sign(0, keyPairs[2], redeemScript, null, unspent.value, witnessScript) - txb.sign(0, keyPairs[3], redeemScript, null, unspent.value, witnessScript) + txb.sign(0, keyPairs[0], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) + txb.sign(0, keyPairs[2], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) + txb.sign(0, keyPairs[3], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) const tx = txb.build() @@ -235,12 +227,14 @@ describe('bitcoinjs-lib (transactions)', function () { tx.ins.forEach(function (input, i) { const keyPair = keyPairs[i] - const prevOutScript = bitcoin.address.toOutputScript(getAddress(keyPair)) - const scriptSig = bitcoin.script.pubKeyHash.input.decode(input.script) - const ss = bitcoin.script.signature.decode(scriptSig.signature) - const hash = tx.hashForSignature(i, prevOutScript, ss.hashType) + const p2pkh = bitcoin.payments.p2pkh({ + pubkey: keyPair.publicKey, + input: input.script + }) + + const ss = bitcoin.script.signature.decode(p2pkh.signature) + const hash = tx.hashForSignature(i, p2pkh.output, ss.hashType) - assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.publicKey.toString('hex')) assert.strictEqual(keyPair.verify(hash, ss.signature), true) }) }) From 90a34ef2fef9ba8ff3d54801384f4029f78a9ac1 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 27 Jun 2018 14:35:41 +1000 Subject: [PATCH 057/568] add timeout to CTLV tests --- test/integration/cltv.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 0823b55..d508f74 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -12,6 +12,7 @@ const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZs describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // force update MTP before(function (done) { + this.timeout(30000) regtestUtils.mine(11, done) }) From 47e5a0e179d66a8fc78e62ea7ba5b3d2eaec4d32 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 5 Jun 2018 20:17:06 +1000 Subject: [PATCH 058/568] witnessScriptHash: fix null decompile leading to errors -- TODO: add test --- src/templates/witnessscripthash/input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js index 0188048..c7fb98f 100644 --- a/src/templates/witnessscripthash/input.js +++ b/src/templates/witnessscripthash/input.js @@ -18,7 +18,7 @@ function check (chunks, allowIncomplete) { const witnessScriptChunks = bscript.decompile(witnessScript) // is witnessScript a valid script? - if (witnessScriptChunks.length === 0) return false + if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)) From 1fba0c62a5b7d7eb8ca88d75a7d68c36da71d772 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 5 Jun 2018 23:15:42 +1000 Subject: [PATCH 059/568] tests: add missing context information to Sighash V1 description --- src/transaction_builder.js | 11 +++++++++-- test/fixtures/transaction_builder.json | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 3ec3d6e..2e11fc5 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -15,11 +15,18 @@ const ECPair = require('./ecpair') const Transaction = require('./transaction') function supportedType (type) { - return SIGNABLE.indexOf(type) !== -1 + return [ + btemplates.types.P2PKH, + btemplates.types.P2PK, + btemplates.types.MULTISIG + ].indexOf(type) !== -1 } function supportedP2SHType (type) { - return P2SH.indexOf(type) !== -1 + return supportedType(type) || [ + btemplates.types.P2WPKH, + btemplates.types.P2WSH + ].indexOf(type) !== -1 } function extractChunks (type, chunks, script) { diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 9393de5..329b5a7 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -909,7 +909,7 @@ "locktime": 1170 }, { - "description": "Sighash V1: P2MS 6/6", + "description": "Sighash V1: P2WSH(P2SH(P2MS 6/6))", "txHex": "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff02e6312761010000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac583e0f00000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac0800483045022100f902f491c4df15199e584790ae8c7202569a977accac0a09fa3f4f3b6ec3517602205961a951c4a12fa966da67b6fd75975b9de156b9895f8ab5f289ecaee12b9b3501473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502483045022100bd5294e145d729e9593f49079b74e6e4b8aeba63440408595ce0949d5c6450a702207f9c9fb45907fe0180d3f4bee499006007bb90894b5f824a26dfa5d3afec543303483045022100febf9409d7f3c091ddc4d296a483aae7b3d2a91d38f6ea2a153f7ff085fe7766022078d11972c74cd78f816152463a5e1a5d986dfb94b55cf5f7242e4f6d5df000ff81483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a088247304402201a0e125aed6a700e45d6c86017d5a9d2264c8079319d868f3f163f5d63cb5bfe02200887608f2322ca0d82df67316275371028b0b21750417d594117963fe23b67ec83cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000", "version": 1, "inputs": [ From 400be7114b7d2f88a1285799bee308f3182c6b49 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 27 Jun 2018 17:29:18 +1000 Subject: [PATCH 060/568] use payments in TxBuilder --- src/payments/p2ms.js | 1 + src/payments/p2pk.js | 1 + src/transaction_builder.js | 781 ++++++++++++------------- test/fixtures/transaction_builder.json | 22 +- test/integration/transactions.js | 20 +- test/transaction_builder.js | 8 +- 6 files changed, 389 insertions(+), 444 deletions(-) diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index 4fba429..a17d422 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -19,6 +19,7 @@ function stacksEqual (a, b) { // output: m [pubKeys ...] n OP_CHECKMULTISIG function p2ms (a, opts) { if ( + !a.input && !a.output && !(a.pubkeys && a.m !== undefined) && !a.signatures diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index eacccd6..9cddc81 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -10,6 +10,7 @@ let BITCOIN_NETWORK = require('../networks').bitcoin // output: {pubKey} OP_CHECKSIG function p2pk (a, opts) { if ( + !a.input && !a.output && !a.pubkey && !a.input && diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 2e11fc5..0f6b1ce 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -5,188 +5,133 @@ const bscript = require('./script') const btemplates = require('./templates') const networks = require('./networks') const ops = require('bitcoin-ops') +const payments = require('./payments') +const SCRIPT_TYPES = btemplates.types const typeforce = require('typeforce') const types = require('./types') -const scriptTypes = btemplates.types -const SIGNABLE = [btemplates.types.P2PKH, btemplates.types.P2PK, btemplates.types.MULTISIG] -const P2SH = SIGNABLE.concat([btemplates.types.P2WPKH, btemplates.types.P2WSH]) const ECPair = require('./ecpair') const Transaction = require('./transaction') -function supportedType (type) { - return [ - btemplates.types.P2PKH, - btemplates.types.P2PK, - btemplates.types.MULTISIG - ].indexOf(type) !== -1 -} +function expandInput (scriptSig, witnessStack, type, scriptPubKey) { + if (scriptSig.length === 0 && witnessStack.length === 0) return {} + if (!type) { + let ssType = btemplates.classifyInput(scriptSig, true) + let wsType = btemplates.classifyWitness(witnessStack, true) + if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined + if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined + type = ssType || wsType + } -function supportedP2SHType (type) { - return supportedType(type) || [ - btemplates.types.P2WPKH, - btemplates.types.P2WSH - ].indexOf(type) !== -1 -} - -function extractChunks (type, chunks, script) { - let pubKeys = [] - let signatures = [] switch (type) { - case scriptTypes.P2PKH: - // if (redeemScript) throw new Error('Nonstandard... P2SH(P2PKH)') - pubKeys = chunks.slice(1) - signatures = chunks.slice(0, 1) - break + case SCRIPT_TYPES.P2WPKH: { + const { output, pubkey, signature } = payments.p2wpkh({ witness: witnessStack }) - case scriptTypes.P2PK: - pubKeys[0] = script ? btemplates.pubKey.output.decode(script) : undefined - signatures = chunks.slice(0, 1) - break - - case scriptTypes.MULTISIG: - if (script) { - const multisig = btemplates.multisig.output.decode(script) - pubKeys = multisig.pubKeys + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2WPKH, + pubkeys: [pubkey], + signatures: [signature] } + } - signatures = chunks.slice(1).map(function (chunk) { - return chunk.length === 0 ? undefined : chunk - }) - break + case SCRIPT_TYPES.P2PKH: { + const { output, pubkey, signature } = payments.p2pkh({ input: scriptSig }) + + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2PKH, + pubkeys: [pubkey], + signatures: [signature] + } + } + + case SCRIPT_TYPES.P2PK: { + const { signature } = payments.p2pk({ input: scriptSig }) + + return { + prevOutType: SCRIPT_TYPES.P2PK, + pubkeys: [undefined], + signatures: [signature] + } + } + + case SCRIPT_TYPES.MULTISIG: { + const { pubkeys, signatures } = payments.p2ms({ + input: scriptSig, + output: scriptPubKey + }, { allowIncomplete: true }) + + return { + prevOutType: SCRIPT_TYPES.MULTISIG, + pubkeys: pubkeys, + signatures: signatures + } + } + } + + if (type === SCRIPT_TYPES.P2SH) { + const { output, redeem } = payments.p2sh({ + input: scriptSig, + witness: witnessStack + }) + + const outputType = btemplates.classifyOutput(redeem.output) + const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output) + if (!expanded.prevOutType) return {} + + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2SH, + redeemScript: redeem.output, + redeemScriptType: expanded.prevOutType, + witnessScript: expanded.witnessScript, + witnessScriptType: expanded.witnessScriptType, + + pubkeys: expanded.pubkeys, + signatures: expanded.signatures + } + } + + if (type === SCRIPT_TYPES.P2WSH) { + const { output, redeem } = payments.p2wsh({ + input: scriptSig, + witness: witnessStack + }) + const outputType = btemplates.classifyOutput(redeem.output) + let expanded + if (outputType === SCRIPT_TYPES.P2WPKH) { + expanded = expandInput(redeem.input, redeem.witness, outputType) + } else { + expanded = expandInput(bscript.compile(redeem.witness), [], outputType, redeem.output) + } + if (!expanded.prevOutType) return {} + + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2WSH, + witnessScript: redeem.output, + witnessScriptType: expanded.prevOutType, + + pubkeys: expanded.pubkeys, + signatures: expanded.signatures + } } return { - pubKeys: pubKeys, - signatures: signatures + prevOutType: SCRIPT_TYPES.NONSTANDARD, + prevOutScript: scriptSig } } -function expandInput (scriptSig, witnessStack) { - if (scriptSig.length === 0 && witnessStack.length === 0) return {} - - let prevOutScript - let prevOutType - let scriptType - let script - let redeemScript - let witnessScript - let witnessScriptType - let redeemScriptType - let witness = false - let p2wsh = false - let p2sh = false - let witnessProgram - let chunks - - const scriptSigChunks = bscript.decompile(scriptSig) || [] - const sigType = btemplates.classifyInput(scriptSigChunks, true) - if (sigType === scriptTypes.P2SH) { - p2sh = true - redeemScript = scriptSigChunks[scriptSigChunks.length - 1] - redeemScriptType = btemplates.classifyOutput(redeemScript) - prevOutScript = btemplates.scriptHash.output.encode(bcrypto.hash160(redeemScript)) - prevOutType = scriptTypes.P2SH - script = redeemScript - } - - const classifyWitness = btemplates.classifyWitness(witnessStack, true) - if (classifyWitness === scriptTypes.P2WSH) { - witnessScript = witnessStack[witnessStack.length - 1] - witnessScriptType = btemplates.classifyOutput(witnessScript) - p2wsh = true - witness = true - if (scriptSig.length === 0) { - prevOutScript = btemplates.witnessScriptHash.output.encode(bcrypto.sha256(witnessScript)) - prevOutType = scriptTypes.P2WSH - if (redeemScript !== undefined) { - throw new Error('Redeem script given when unnecessary') - } - // bare witness - } else { - if (!redeemScript) { - throw new Error('No redeemScript provided for P2WSH, but scriptSig non-empty') - } - witnessProgram = btemplates.witnessScriptHash.output.encode(bcrypto.sha256(witnessScript)) - if (!redeemScript.equals(witnessProgram)) { - throw new Error('Redeem script didn\'t match witnessScript') - } - } - - if (!supportedType(btemplates.classifyOutput(witnessScript))) { - throw new Error('unsupported witness script') - } - - script = witnessScript - scriptType = witnessScriptType - chunks = witnessStack.slice(0, -1) - } else if (classifyWitness === scriptTypes.P2WPKH) { - witness = true - const key = witnessStack[witnessStack.length - 1] - const keyHash = bcrypto.hash160(key) - if (scriptSig.length === 0) { - prevOutScript = btemplates.witnessPubKeyHash.output.encode(keyHash) - prevOutType = scriptTypes.P2WPKH - if (typeof redeemScript !== 'undefined') { - throw new Error('Redeem script given when unnecessary') - } - } else { - if (!redeemScript) { - throw new Error('No redeemScript provided for P2WPKH, but scriptSig wasn\'t empty') - } - witnessProgram = btemplates.witnessPubKeyHash.output.encode(keyHash) - if (!redeemScript.equals(witnessProgram)) { - throw new Error('Redeem script did not have the right witness program') - } - } - - scriptType = scriptTypes.P2PKH - chunks = witnessStack - } else if (redeemScript) { - if (!supportedP2SHType(redeemScriptType)) { - throw new Error('Bad redeemscript!') - } - - script = redeemScript - scriptType = redeemScriptType - chunks = scriptSigChunks.slice(0, -1) - } else { - prevOutType = scriptType = btemplates.classifyInput(scriptSig) - chunks = scriptSigChunks - } - - const expanded = extractChunks(scriptType, chunks, script) - - const result = { - pubKeys: expanded.pubKeys, - signatures: expanded.signatures, - prevOutScript: prevOutScript, - prevOutType: prevOutType, - signType: scriptType, - signScript: script, - witness: Boolean(witness) - } - - if (p2sh) { - result.redeemScript = redeemScript - result.redeemScriptType = redeemScriptType - } - - if (p2wsh) { - result.witnessScript = witnessScript - result.witnessScriptType = witnessScriptType - } - - return result -} // could be done in expandInput, but requires the original Transaction for hashForSignature function fixMultisigOrder (input, transaction, vin) { - if (input.redeemScriptType !== scriptTypes.MULTISIG || !input.redeemScript) return - if (input.pubKeys.length === input.signatures.length) return + if (input.redeemScriptType !== SCRIPT_TYPES.MULTISIG || !input.redeemScript) return + if (input.pubkeys.length === input.signatures.length) return const unmatched = input.signatures.concat() - input.signatures = input.pubKeys.map(function (pubKey) { + input.signatures = input.pubkeys.map(function (pubKey) { const keyPair = ECPair.fromPublicKey(pubKey) let match @@ -213,265 +158,262 @@ function fixMultisigOrder (input, transaction, vin) { }) } -function expandOutput (script, scriptType, ourPubKey) { +function expandOutput (script, ourPubKey) { typeforce(types.Buffer, script) + const type = btemplates.classifyOutput(script) - const scriptChunks = bscript.decompile(script) || [] - if (!scriptType) { - scriptType = btemplates.classifyOutput(script) - } + switch (type) { + case SCRIPT_TYPES.P2PKH: { + if (!ourPubKey) return { type } - let pubKeys = [] - - switch (scriptType) { - // does our hash160(pubKey) match the output scripts? - case scriptTypes.P2PKH: - if (!ourPubKey) break - - const pkh1 = scriptChunks[2] + // does our hash160(pubKey) match the output scripts? + const pkh1 = payments.p2pkh({ output: script }).hash const pkh2 = bcrypto.hash160(ourPubKey) - if (pkh1.equals(pkh2)) pubKeys = [ourPubKey] - break + if (!pkh1.equals(pkh2)) return { type } - // does our hash160(pubKey) match the output scripts? - case scriptTypes.P2WPKH: - if (!ourPubKey) break + return { + type, + pubkeys: [ourPubKey], + signatures: [undefined] + } + } - const wpkh1 = scriptChunks[1] + case SCRIPT_TYPES.P2WPKH: { + if (!ourPubKey) return { type } + + // does our hash160(pubKey) match the output scripts? + const wpkh1 = payments.p2wpkh({ output: script }).hash const wpkh2 = bcrypto.hash160(ourPubKey) - if (wpkh1.equals(wpkh2)) pubKeys = [ourPubKey] - break + if (!wpkh1.equals(wpkh2)) return { type } - case scriptTypes.P2PK: - pubKeys = scriptChunks.slice(0, 1) - break + return { + type, + pubkeys: [ourPubKey], + signatures: [undefined] + } + } - case scriptTypes.MULTISIG: - pubKeys = scriptChunks.slice(1, -2) - break + case SCRIPT_TYPES.P2PK: { + const p2pk = payments.p2pk({ output: script }) + return { + type, + pubkeys: [p2pk.pubkey], + signatures: [undefined] + } + } - default: return { scriptType: scriptType } + case SCRIPT_TYPES.MULTISIG: { + const p2ms = payments.p2ms({ output: script }) + return { + type, + pubkeys: p2ms.pubkeys, + signatures: p2ms.pubkeys.map(() => undefined) + } + } } - return { - pubKeys: pubKeys, - scriptType: scriptType, - signatures: pubKeys.map(function () { return undefined }) - } + return { type } } -function checkP2SHInput (input, redeemScriptHash) { - if (input.prevOutType) { - if (input.prevOutType !== scriptTypes.P2SH) throw new Error('PrevOutScript must be P2SH') - - const chunks = bscript.decompile(input.prevOutScript) - if (!chunks) throw new Error('Invalid prevOutScript') - if (!chunks[1].equals(redeemScriptHash)) throw new Error('Inconsistent hash160(redeemScript)') - } -} - -function checkP2WSHInput (input, witnessScriptHash) { - if (input.prevOutType) { - if (input.prevOutType !== scriptTypes.P2WSH) throw new Error('PrevOutScript must be P2WSH') - - const chunks = bscript.decompile(input.prevOutScript) - if (!chunks) throw new Error('Invalid witnessScript') - if (!chunks[1].equals(witnessScriptHash)) throw new Error('Inconsistent sha256(witnessScript)') - } -} - -function prepareInput (input, kpPubKey, redeemScript, witnessValue, witnessScript) { - let expanded - let prevOutType - let prevOutScript - - let p2sh = false - let p2shType - let redeemScriptHash - - let witness = false - let p2wsh = false - let witnessType - let witnessScriptHash - - let signType - let signScript - +function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScript) { if (redeemScript && witnessScript) { - redeemScriptHash = bcrypto.hash160(redeemScript) - witnessScriptHash = bcrypto.sha256(witnessScript) - checkP2SHInput(input, redeemScriptHash) + const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }) + const p2wshAlt = payments.p2wsh({ output: redeemScript }) + const p2sh = payments.p2sh({ redeem: { output: redeemScript } }) + const p2shAlt = payments.p2sh({ redeem: p2wsh }) - if (!redeemScript.equals(btemplates.witnessScriptHash.output.encode(witnessScriptHash))) throw new Error('Witness script inconsistent with redeem script') + // enforces P2SH(P2WSH(...)) + if (!p2wsh.hash.equals(p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript') + if (!p2sh.hash.equals(p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript') - expanded = expandOutput(witnessScript, undefined, kpPubKey) - if (!expanded.pubKeys) throw new Error(expanded.scriptType + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') - - prevOutType = btemplates.types.P2SH - prevOutScript = btemplates.scriptHash.output.encode(redeemScriptHash) - p2sh = witness = p2wsh = true - p2shType = btemplates.types.P2WSH - signType = witnessType = expanded.scriptType - signScript = witnessScript - } else if (redeemScript) { - redeemScriptHash = bcrypto.hash160(redeemScript) - checkP2SHInput(input, redeemScriptHash) - - expanded = expandOutput(redeemScript, undefined, kpPubKey) - if (!expanded.pubKeys) throw new Error(expanded.scriptType + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')') - - prevOutType = btemplates.types.P2SH - prevOutScript = btemplates.scriptHash.output.encode(redeemScriptHash) - p2sh = true - signType = p2shType = expanded.scriptType - signScript = redeemScript - witness = signType === btemplates.types.P2WPKH - } else if (witnessScript) { - witnessScriptHash = bcrypto.sha256(witnessScript) - checkP2WSHInput(input, witnessScriptHash) - - expanded = expandOutput(witnessScript, undefined, kpPubKey) - if (!expanded.pubKeys) throw new Error(expanded.scriptType + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') - - prevOutType = btemplates.types.P2WSH - prevOutScript = btemplates.witnessScriptHash.output.encode(witnessScriptHash) - witness = p2wsh = true - signType = witnessType = expanded.scriptType - signScript = witnessScript - } else if (input.prevOutType) { - // embedded scripts are not possible without a redeemScript - if (input.prevOutType === scriptTypes.P2SH) { - throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript') + const expanded = expandOutput(p2wsh.redeem.output, ourPubKey) + if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') + if (input.signatures && input.signatures.some(x => x)) { + expanded.signatures = input.signatures } - if (input.prevOutType === scriptTypes.P2WSH) { - throw new Error('PrevOutScript is ' + input.prevOutType + ', requires witnessScript') - } + return { + redeemScript: redeemScript, + redeemScriptType: SCRIPT_TYPES.P2WSH, - prevOutType = input.prevOutType - prevOutScript = input.prevOutScript - expanded = expandOutput(input.prevOutScript, input.prevOutType, kpPubKey) - if (!expanded.pubKeys) return + witnessScript: witnessScript, + witnessScriptType: expanded.type, - witness = (input.prevOutType === scriptTypes.P2WPKH) - signType = prevOutType - signScript = prevOutScript - } else { - prevOutScript = btemplates.pubKeyHash.output.encode(bcrypto.hash160(kpPubKey)) - expanded = expandOutput(prevOutScript, scriptTypes.P2PKH, kpPubKey) + prevOutType: SCRIPT_TYPES.P2SH, + prevOutScript: p2sh.output, - prevOutType = scriptTypes.P2PKH - witness = false - signType = prevOutType - signScript = prevOutScript - } + hasWitness: true, + signScript: witnessScript, + signType: expanded.type, - if (signType === scriptTypes.P2WPKH) { - signScript = btemplates.pubKeyHash.output.encode(btemplates.witnessPubKeyHash.output.decode(signScript)) - } - - if (p2sh) { - input.redeemScript = redeemScript - input.redeemScriptType = p2shType - } - - if (p2wsh) { - input.witnessScript = witnessScript - input.witnessScriptType = witnessType - } - - input.pubKeys = expanded.pubKeys - input.signatures = expanded.signatures - input.signScript = signScript - input.signType = signType - input.prevOutScript = prevOutScript - input.prevOutType = prevOutType - input.witness = witness -} - -function buildStack (type, signatures, pubKeys, allowIncomplete) { - if (type === scriptTypes.P2PKH) { - if (signatures.length === 1 && Buffer.isBuffer(signatures[0]) && pubKeys.length === 1) return btemplates.pubKeyHash.input.encodeStack(signatures[0], pubKeys[0]) - } else if (type === scriptTypes.P2PK) { - if (signatures.length === 1 && Buffer.isBuffer(signatures[0])) return btemplates.pubKey.input.encodeStack(signatures[0]) - } else if (type === scriptTypes.MULTISIG) { - if (signatures.length > 0) { - signatures = signatures.map(function (signature) { - return signature || ops.OP_0 - }) - if (!allowIncomplete) { - // remove blank signatures - signatures = signatures.filter(function (x) { return x !== ops.OP_0 }) - } - - return btemplates.multisig.input.encodeStack(signatures) - } - } else { - throw new Error('Not yet supported') - } - - if (!allowIncomplete) throw new Error('Not enough signatures provided') - return [] -} - -function buildInput (input, allowIncomplete) { - let scriptType = input.prevOutType - let sig = [] - let witness = [] - - if (supportedType(scriptType)) { - sig = buildStack(scriptType, input.signatures, input.pubKeys, allowIncomplete) - } - - let p2sh = false - if (scriptType === btemplates.types.P2SH) { - // We can remove this error later when we have a guarantee prepareInput - // rejects unsignable scripts - it MUST be signable at this point. - if (!allowIncomplete && !supportedP2SHType(input.redeemScriptType)) { - throw new Error('Impossible to sign this type') - } - - if (supportedType(input.redeemScriptType)) { - sig = buildStack(input.redeemScriptType, input.signatures, input.pubKeys, allowIncomplete) - } - - // If it wasn't SIGNABLE, it's witness, defer to that - if (input.redeemScriptType) { - p2sh = true - scriptType = input.redeemScriptType + pubkeys: expanded.pubkeys, + signatures: expanded.signatures } } - switch (scriptType) { - // P2WPKH is a special case of P2PKH - case btemplates.types.P2WPKH: - witness = buildStack(btemplates.types.P2PKH, input.signatures, input.pubKeys, allowIncomplete) - break + if (redeemScript) { + const p2sh = payments.p2sh({ redeem: { output: redeemScript } }) - case btemplates.types.P2WSH: - // We can remove this check later - if (!allowIncomplete && !supportedType(input.witnessScriptType)) { - throw new Error('Impossible to sign this type') - } + if (input.prevOutScript) { + let p2shAlt + try { + p2shAlt = payments.p2sh({ output: input.prevOutScript }) + } catch (e) { throw new Error('PrevOutScript must be P2SH') } + if (!p2sh.hash.equals(p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript') + } - if (supportedType(input.witnessScriptType)) { - witness = buildStack(input.witnessScriptType, input.signatures, input.pubKeys, allowIncomplete) - witness.push(input.witnessScript) - scriptType = input.witnessScriptType - } + const expanded = expandOutput(p2sh.redeem.output, ourPubKey) + if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')') + if (input.signatures && input.signatures.some(x => x)) { + expanded.signatures = input.signatures + } - break + let signScript = redeemScript + if (expanded.type === SCRIPT_TYPES.P2WPKH) { + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output + } + + return { + redeemScript: redeemScript, + redeemScriptType: expanded.type, + + prevOutType: SCRIPT_TYPES.P2SH, + prevOutScript: p2sh.output, + + hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, + signScript: signScript, + signType: expanded.type, + + pubkeys: expanded.pubkeys, + signatures: expanded.signatures + } } - // append redeemScript if necessary - if (p2sh) { - sig.push(input.redeemScript) + if (witnessScript) { + const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }) + + if (input.prevOutScript) { + const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }) + if (!p2wsh.hash.equals(p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript') + } + + const expanded = expandOutput(p2wsh.redeem.output, ourPubKey) + if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') + if (input.signatures && input.signatures.some(x => x)) { + expanded.signatures = input.signatures + } + + return { + witnessScript: witnessScript, + witnessScriptType: expanded.type, + + prevOutType: SCRIPT_TYPES.P2WSH, + prevOutScript: p2wsh.output, + + hasWitness: true, + signScript: witnessScript, + signType: expanded.type, + + pubkeys: expanded.pubkeys, + signatures: expanded.signatures + } } + if (input.prevOutType && input.prevOutScript) { + // embedded scripts are not possible without extra information + if (input.prevOutType === SCRIPT_TYPES.P2SH) throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript') + if (input.prevOutType === SCRIPT_TYPES.P2WSH) throw new Error('PrevOutScript is ' + input.prevOutType + ', requires witnessScript') + if (!input.prevOutScript) throw new Error('PrevOutScript is missing') + + const expanded = expandOutput(input.prevOutScript, ourPubKey) + if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported (' + bscript.toASM(input.prevOutScript) + ')') + if (input.signatures && input.signatures.some(x => x)) { + expanded.signatures = input.signatures + } + + return { + prevOutType: expanded.type, + prevOutScript: input.prevOutScript, + + hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, + signScript: input.prevOutScript, + signType: expanded.type, + + pubkeys: expanded.pubkeys, + signatures: expanded.signatures + } + } + + const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output return { - type: scriptType, - script: bscript.compile(sig), - witness: witness + prevOutType: SCRIPT_TYPES.P2PKH, + prevOutScript: prevOutScript, + + hasWitness: false, + signScript: prevOutScript, + signType: SCRIPT_TYPES.P2PKH, + + pubkeys: [ourPubKey], + signatures: [undefined] + } +} + +function build (type, input, allowIncomplete) { + const pubkeys = input.pubkeys || [] + let signatures = input.signatures || [] + + switch (type) { + case SCRIPT_TYPES.P2PKH: { + if (pubkeys.length === 0) break + if (signatures.length === 0) break + + return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] }) + } + case SCRIPT_TYPES.P2WPKH: { + if (pubkeys.length === 0) break + if (signatures.length === 0) break + + return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] }) + } + case SCRIPT_TYPES.P2PK: { + if (pubkeys.length === 0) break + if (signatures.length === 0) break + + return payments.p2pk({ signature: signatures[0] }) + } + case SCRIPT_TYPES.MULTISIG: { + if (allowIncomplete) { + signatures = signatures.map(x => x || ops.OP_0) + } else { + signatures = signatures.filter(x => x) + } + + return payments.p2ms({ signatures }, { allowIncomplete }) + } + case SCRIPT_TYPES.P2SH: { + const redeem = build(input.redeemScriptType, input, allowIncomplete) + if (!redeem) return + + return payments.p2sh({ + redeem: { + output: redeem.output || input.redeemScript, + input: redeem.input, + witness: redeem.witness + } + }) + } + case SCRIPT_TYPES.P2WSH: { + const redeem = build(input.witnessScriptType, input, allowIncomplete) + if (!redeem) return + + return payments.p2wsh({ + redeem: { + output: input.witnessScript, + input: redeem.input, + witness: redeem.witness + } + }) + } } } @@ -590,15 +532,14 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) if (!input.prevOutScript && options.prevOutScript) { let prevOutType - if (!input.pubKeys && !input.signatures) { + if (!input.pubkeys && !input.signatures) { const expanded = expandOutput(options.prevOutScript) - - if (expanded.pubKeys) { - input.pubKeys = expanded.pubKeys + if (expanded.pubkeys) { + input.pubkeys = expanded.pubkeys input.signatures = expanded.signatures } - prevOutType = expanded.scriptType + prevOutType = expanded.type } input.prevOutScript = options.prevOutScript @@ -638,20 +579,19 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) { } const tx = this.__tx.clone() - // Create script signatures from inputs - this.__inputs.forEach(function (input, i) { - const scriptType = input.witnessScriptType || input.redeemScriptType || input.prevOutType - if (!scriptType && !allowIncomplete) throw new Error('Transaction is not complete') - const result = buildInput(input, allowIncomplete) - // skip if no result - if (!allowIncomplete) { - if (!supportedType(result.type) && result.type !== btemplates.types.P2WPKH) { - throw new Error(result.type + ' not supported') - } + // create script signatures from inputs + this.__inputs.forEach(function (input, i) { + if (!input.prevOutType && !allowIncomplete) throw new Error('Transaction is not complete') + + const result = build(input.prevOutType, input, allowIncomplete) + if (!result) { + if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) throw new Error('Unknown input type') + if (!allowIncomplete) throw new Error('Not enough information') + return } - tx.setInputScript(i, result.script) + tx.setInputScript(i, result.input) tx.setWitness(i, result.witness) }) @@ -666,15 +606,15 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) { } function canSign (input) { - return input.prevOutScript !== undefined && - input.signScript !== undefined && - input.pubKeys !== undefined && + return input.signScript !== undefined && + input.signType !== undefined && + input.pubkeys !== undefined && input.signatures !== undefined && - input.signatures.length === input.pubKeys.length && - input.pubKeys.length > 0 && + input.signatures.length === input.pubkeys.length && + input.pubkeys.length > 0 && ( - input.witness === false || - (input.witness === true && input.value !== undefined) + input.hasWitness === false || + input.value !== undefined ) } @@ -693,7 +633,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy throw new Error('Inconsistent redeemScript') } - const kpPubKey = keyPair.publicKey || keyPair.getPublicKey() + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey() if (!canSign(input)) { if (witnessValue !== undefined) { if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue') @@ -701,28 +641,33 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy input.value = witnessValue } - if (!canSign(input)) prepareInput(input, kpPubKey, redeemScript, witnessValue, witnessScript) + if (!canSign(input)) { + const prepared = prepareInput(input, ourPubKey, redeemScript, witnessValue, witnessScript) + + // updates inline + Object.assign(input, prepared) + } + if (!canSign(input)) throw Error(input.prevOutType + ' not supported') } // ready to sign let signatureHash - if (input.witness) { + if (input.hasWitness) { signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType) } else { signatureHash = this.__tx.hashForSignature(vin, input.signScript, hashType) } // enforce in order signing of public keys - const signed = input.pubKeys.some(function (pubKey, i) { - if (!kpPubKey.equals(pubKey)) return false + const signed = input.pubkeys.some(function (pubKey, i) { + if (!ourPubKey.equals(pubKey)) return false if (input.signatures[i]) throw new Error('Signature already exists') - if (kpPubKey.length !== 33 && ( - input.signType === scriptTypes.P2WPKH || - input.redeemScriptType === scriptTypes.P2WSH || - input.prevOutType === scriptTypes.P2WSH - )) throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH') + // TODO: add tests + if (ourPubKey.length !== 33 && input.hasWitness) { + throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH') + } const signature = keyPair.sign(signatureHash) input.signatures[i] = bscript.signature.encode(signature, hashType) diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 329b5a7..973f641 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -395,7 +395,7 @@ }, { "description": "Transaction w/ P2WPKH -> P2WPKH", - "txHex": "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff011027000000000000160014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c502483045022100a8fc5e4c6d7073474eff2af5d756966e75be0cdfbba299518526080ce8b584be02200f26d41082764df89e3c815b8eaf51034a3b68a25f1be51208f54222c1bb6c1601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", + "txHex": "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff011027000000000000160014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c502483045022100b4a9d46ea4d38d6b3ea098911c9f72c0ae6ebc72408e6be7880a6b22a4b3e4da02207996107d0e6437f80363f96f502a38f275156f7501ea51f67899ba78a0c129c101210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", "version": 1, "inputs": [ { @@ -833,8 +833,8 @@ ] }, { - "description": "Sighash V1: ALL", - "txHex": "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000484730440220691a19d365c8d75f921346c70271506bde136f13a4b566dd796902c262e2ec6d02202b00c4aa030eedf294552bdfc163936d2f4e91c59e7798c4471250cf07cb859501eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff0230f45e13000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac00e9a435000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac0002483045022100fddd014889f18d489b5400bfa8cb0a32301a768d934b1a0e2b55398119f26cab02207676c64c16ffa7ffaaf8e16b3b74e916687eebdfdb36b9b7997e838384d464640121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000", + "description": "SIGHASH V0+V1, (P2PKH, P2WPKH) -> 2x P2PKH", + "txHex": "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000484730440220691a19d365c8d75f921346c70271506bde136f13a4b566dd796902c262e2ec6d02202b00c4aa030eedf294552bdfc163936d2f4e91c59e7798c4471250cf07cb859501eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff0230f45e13000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac00e9a435000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402200a7b08cccedf608e279410091acbd7e990e19a8edf401c3698763d2920de5871022060462ed172a02ecef73ebc19811d8fc72ed68f4419742df70241ad0a5a6a36410121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000", "version": 1, "inputs": [ { @@ -877,7 +877,7 @@ "locktime": 17 }, { - "description": "Sighash V1: ALL 2", + "description": "SIGHASH V0+V1, P2SH(P2WPKH) -> P2PKH", "txHex": "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000", "version": 1, "inputs": [ @@ -1313,8 +1313,8 @@ "locktime": 0 }, { - "description": "P2WPKH", - "txHex": "0100000000010133defbe3e28860007ff3e21222774c220cb35d554fa3e3796d25bf8ee983e1080000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac0248304502210097c3006f0b390982eb47f762b2853773c6cedf83668a22d710f4c13c4fd6b15502205e26ef16a81fc818a37f3a34fc6d0700e61100ea6c6773907c9c046042c440340121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b00000000", + "description": "P2WPKH -> P2PKH", + "txHex": "0100000000010133defbe3e28860007ff3e21222774c220cb35d554fa3e3796d25bf8ee983e1080000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac02483045022100834f56825e880ab7926164458e10582d9fd8df005396b7e51a1efb8db277204e02206a3610b7101c3242643ac9c9d3487c2d28ffdad19ec26a7f81fc100bdac625f10121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b00000000", "version": 1, "inputs": [ { @@ -1340,7 +1340,7 @@ "locktime": 0 }, { - "description": "P2SH(P2WPKH)", + "description": "P2SH(P2WPKH) -> P2PKH", "txHex": "010000000001015df9a0b9ade2d835881704e0f53b51a4b19ecfc794ea1f3555783dd7f68659ce0000000017160014851a33a5ef0d4279bd5854949174e2c65b1d4500ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac02483045022100cb3929c128fec5108071b662e5af58e39ac8708882753a421455ca80462956f6022030c0f4738dd1a13fc7a34393002d25c6e8a6399f29c7db4b98f53a9475d94ca20121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b00000000", "version": 1, "inputs": [ @@ -1872,7 +1872,7 @@ }, { "description": "Incomplete transaction, known prevTxScript, thereby throws for missing signatures", - "exception": "Not enough signatures provided", + "exception": "Not enough information", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -1925,7 +1925,7 @@ }, { "description": "Complete transaction w/ non-standard inputs", - "exception": "nonstandard not supported", + "exception": "Unknown input type", "txHex": "010000000100000000171a0000e028f2000000000050178500000000000d0000000e00000000000000201ff691b2263260e71f363d1db51ff3100d285956a40cc0e4f8c8c2c4a80559b1ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" } ], @@ -2149,7 +2149,7 @@ }, { "description": "Inconsistent RedeemScript hash", - "exception": "Inconsistent hash160", + "exception": "Redeem script inconsistent with prevOutScript", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -2174,7 +2174,7 @@ }, { "description": "Inconsistent WitnessScript hash", - "exception": "Inconsistent sha256", + "exception": "Witness script inconsistent with prevOutScript", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 991e0b6..a4a1747 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -103,7 +103,7 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) - it('can create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', function (done) { this.timeout(30000) const keyPairs = [ @@ -112,21 +112,19 @@ describe('bitcoinjs-lib (transactions)', function () { bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }) ] - const pubKeys = keyPairs.map(function (x) { return x.publicKey }) + const pubkeys = keyPairs.map(x => x.publicKey) + const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys: pubkeys, network: regtest }) + const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest }) - const redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) - - regtestUtils.faucet(address, 2e4, function (err, unspent) { + regtestUtils.faucet(p2sh.address, 2e4, function (err, unspent) { if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) - txb.sign(0, keyPairs[0], redeemScript) - txb.sign(0, keyPairs[2], redeemScript) + txb.sign(0, keyPairs[0], p2sh.redeem.output) + txb.sign(0, keyPairs[2], p2sh.redeem.output) const tx = txb.build() // build and broadcast to the Bitcoin RegTest network @@ -143,7 +141,7 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) - it('can create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', function (done) { this.timeout(30000) const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) @@ -174,7 +172,7 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) - it('can create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', function (done) { this.timeout(50000) const keyPairs = [ diff --git a/test/transaction_builder.js b/test/transaction_builder.js index e4b5e60..e307398 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -149,7 +149,6 @@ describe('TransactionBuilder', function () { txb.__inputs.forEach(function (i) { assert.strictEqual(i.prevOutType, 'scripthash') assert.strictEqual(i.redeemScriptType, 'multisig') - assert.strictEqual(i.signType, 'multisig') }) }) @@ -537,14 +536,15 @@ describe('TransactionBuilder', function () { txb.addInput('a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', 1, 0xffffffff, scriptPubKey) txb.addOutput(scriptPubKey, 99000) txb.sign(0, keyPair, redeemScript, null, 100000, witnessScript) + // 2-of-2 signed only once const tx = txb.buildIncomplete() + // Only input is segwit, so txid should be accurate with the final tx assert.equal(tx.getId(), 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') + const txHex = tx.toHex() - const newTxb = TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) - // input should have the key 'witness' set to true - assert.equal(newTxb.__inputs[0].witness, true) + TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) }) it('should handle badly pre-filled OP_0s', function () { From f83f3c0f293858dddfcb5d1dd0c36a89ecef2bde Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 2 Jul 2018 15:45:51 +1000 Subject: [PATCH 061/568] README: no docs --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 14ee958..8025ea5 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ Mistakes and bugs happen, but with your help in resolving and reporting [issues] - Standardized, using [standard](http://github.com/standard/standard) and Node `Buffer`'s throughout, and - Friendly, with a strong and helpful community, ready to answer questions. +## Documentation +Presently, we do not have any formal documentation other than our [examples](#Examples), please [ask for help](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new) if our examples aren't enough to guide you. + ## Installation ``` bash From 6cacea6f31aa18cce61bed562d730c55ac3709d3 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 3 Jul 2018 21:43:34 +1000 Subject: [PATCH 062/568] rm templates export, rename to classify --- src/address.js | 22 +- src/{templates/index.js => classify.js} | 32 +- src/index.js | 4 - src/transaction_builder.js | 16 +- test/classify.js | 157 ++++++++ test/templates.js | 514 ------------------------ 6 files changed, 188 insertions(+), 557 deletions(-) rename src/{templates/index.js => classify.js} (71%) create mode 100644 test/classify.js delete mode 100644 test/templates.js diff --git a/src/address.js b/src/address.js index 13fa69c..e71bd46 100644 --- a/src/address.js +++ b/src/address.js @@ -2,10 +2,10 @@ const Buffer = require('safe-buffer').Buffer const bech32 = require('bech32') const bs58check = require('bs58check') const bscript = require('./script') -const btemplates = require('./templates') const networks = require('./networks') const typeforce = require('typeforce') const types = require('./types') +const payments = require('./payments') function fromBase58Check (address) { const payload = bs58check.decode(address) @@ -48,15 +48,15 @@ function toBech32 (data, version, prefix) { return bech32.encode(prefix, words) } -function fromOutputScript (outputScript, network) { +function fromOutputScript (output, network) { network = network || networks.bitcoin - if (btemplates.pubKeyHash.output.check(outputScript)) return toBase58Check(bscript.compile(outputScript).slice(3, 23), network.pubKeyHash) - if (btemplates.scriptHash.output.check(outputScript)) return toBase58Check(bscript.compile(outputScript).slice(2, 22), network.scriptHash) - if (btemplates.witnessPubKeyHash.output.check(outputScript)) return toBech32(bscript.compile(outputScript).slice(2, 22), 0, network.bech32) - if (btemplates.witnessScriptHash.output.check(outputScript)) return toBech32(bscript.compile(outputScript).slice(2, 34), 0, network.bech32) + try { return payments.p2pkh({ output, network }).address } catch (e) {} + try { return payments.p2sh({ output, network }).address } catch (e) {} + try { return payments.p2wpkh({ output, network }).address } catch (e) {} + try { return payments.p2wsh({ output, network }).address } catch (e) {} - throw new Error(bscript.toASM(outputScript) + ' has no matching Address') + throw new Error(bscript.toASM(output) + ' has no matching Address') } function toOutputScript (address, network) { @@ -68,8 +68,8 @@ function toOutputScript (address, network) { } catch (e) {} if (decode) { - if (decode.version === network.pubKeyHash) return btemplates.pubKeyHash.output.encode(decode.hash) - if (decode.version === network.scriptHash) return btemplates.scriptHash.output.encode(decode.hash) + if (decode.version === network.pubKeyHash) return payments.p2pkh({ hash: decode.hash }).output + if (decode.version === network.scriptHash) return payments.p2sh({ hash: decode.hash }).output } else { try { decode = fromBech32(address) @@ -78,8 +78,8 @@ function toOutputScript (address, network) { if (decode) { if (decode.prefix !== network.bech32) throw new Error(address + ' has an invalid prefix') if (decode.version === 0) { - if (decode.data.length === 20) return btemplates.witnessPubKeyHash.output.encode(decode.data) - if (decode.data.length === 32) return btemplates.witnessScriptHash.output.encode(decode.data) + if (decode.data.length === 20) return payments.p2wpkh({ hash: decode.data }).output + if (decode.data.length === 32) return payments.p2wsh({ hash: decode.data }).output } } } diff --git a/src/templates/index.js b/src/classify.js similarity index 71% rename from src/templates/index.js rename to src/classify.js index 94ce996..0b98fa6 100644 --- a/src/templates/index.js +++ b/src/classify.js @@ -1,12 +1,12 @@ -const decompile = require('../script').decompile -const multisig = require('./multisig') -const nullData = require('./nulldata') -const pubKey = require('./pubkey') -const pubKeyHash = require('./pubkeyhash') -const scriptHash = require('./scripthash') -const witnessPubKeyHash = require('./witnesspubkeyhash') -const witnessScriptHash = require('./witnessscripthash') -const witnessCommitment = require('./witnesscommitment') +const decompile = require('./script').decompile +const multisig = require('./templates/multisig') +const nullData = require('./templates/nulldata') +const pubKey = require('./templates/pubkey') +const pubKeyHash = require('./templates/pubkeyhash') +const scriptHash = require('./templates/scripthash') +const witnessPubKeyHash = require('./templates/witnesspubkeyhash') +const witnessScriptHash = require('./templates/witnessscripthash') +const witnessCommitment = require('./templates/witnesscommitment') const types = { MULTISIG: 'multisig', @@ -63,16 +63,8 @@ function classifyWitness (script, allowIncomplete) { } module.exports = { - classifyInput: classifyInput, - classifyOutput: classifyOutput, - classifyWitness: classifyWitness, - multisig: multisig, - nullData: nullData, - pubKey: pubKey, - pubKeyHash: pubKeyHash, - scriptHash: scriptHash, - witnessPubKeyHash: witnessPubKeyHash, - witnessScriptHash: witnessScriptHash, - witnessCommitment: witnessCommitment, + input: classifyInput, + output: classifyOutput, + witness: classifyWitness, types: types } diff --git a/src/index.js b/src/index.js index 603c64f..213e98a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,4 @@ const script = require('./script') -const templates = require('./templates') -for (let key in templates) { - script[key] = templates[key] -} module.exports = { Block: require('./block'), diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 0f6b1ce..55c6e21 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -2,13 +2,13 @@ const Buffer = require('safe-buffer').Buffer const baddress = require('./address') const bcrypto = require('./crypto') const bscript = require('./script') -const btemplates = require('./templates') const networks = require('./networks') const ops = require('bitcoin-ops') const payments = require('./payments') -const SCRIPT_TYPES = btemplates.types const typeforce = require('typeforce') const types = require('./types') +const classify = require('./classify') +const SCRIPT_TYPES = classify.types const ECPair = require('./ecpair') const Transaction = require('./transaction') @@ -16,8 +16,8 @@ const Transaction = require('./transaction') function expandInput (scriptSig, witnessStack, type, scriptPubKey) { if (scriptSig.length === 0 && witnessStack.length === 0) return {} if (!type) { - let ssType = btemplates.classifyInput(scriptSig, true) - let wsType = btemplates.classifyWitness(witnessStack, true) + let ssType = classify.input(scriptSig, true) + let wsType = classify.witness(witnessStack, true) if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined type = ssType || wsType @@ -76,7 +76,7 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) { witness: witnessStack }) - const outputType = btemplates.classifyOutput(redeem.output) + const outputType = classify.output(redeem.output) const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output) if (!expanded.prevOutType) return {} @@ -98,7 +98,7 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) { input: scriptSig, witness: witnessStack }) - const outputType = btemplates.classifyOutput(redeem.output) + const outputType = classify.output(redeem.output) let expanded if (outputType === SCRIPT_TYPES.P2WPKH) { expanded = expandInput(redeem.input, redeem.witness, outputType) @@ -160,7 +160,7 @@ function fixMultisigOrder (input, transaction, vin) { function expandOutput (script, ourPubKey) { typeforce(types.Buffer, script) - const type = btemplates.classifyOutput(script) + const type = classify.output(script) switch (type) { case SCRIPT_TYPES.P2PKH: { @@ -543,7 +543,7 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) } input.prevOutScript = options.prevOutScript - input.prevOutType = prevOutType || btemplates.classifyOutput(options.prevOutScript) + input.prevOutType = prevOutType || classify.output(options.prevOutScript) } const vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig) diff --git a/test/classify.js b/test/classify.js new file mode 100644 index 0000000..f56647e --- /dev/null +++ b/test/classify.js @@ -0,0 +1,157 @@ +/* global describe, it */ + +const assert = require('assert') +const bscript = require('../src/script') +const classify = require('../src/classify') + +const fixtures = require('./fixtures/templates.json') + +const multisig = require('../src/templates/multisig') +const nullData = require('../src/templates/nulldata') +const pubKey = require('../src/templates/pubkey') +const pubKeyHash = require('../src/templates/pubkeyhash') +const scriptHash = require('../src/templates/scripthash') +const witnessPubKeyHash = require('../src/templates/witnesspubkeyhash') +const witnessScriptHash = require('../src/templates/witnessscripthash') +const witnessCommitment = require('../src/templates/witnesscommitment') + +const tmap = { + pubKey, + pubKeyHash, + scriptHash, + witnessPubKeyHash, + witnessScriptHash, + multisig, + nullData, + witnessCommitment +} + +describe('classify', function () { + describe('input', function () { + fixtures.valid.forEach(function (f) { + if (!f.input) return + + it('classifies ' + f.input + ' as ' + f.type, function () { + const input = bscript.fromASM(f.input) + const type = classify.input(input) + + assert.strictEqual(type, f.type) + }) + }) + + fixtures.valid.forEach(function (f) { + if (!f.input) return + if (!f.typeIncomplete) return + + it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, function () { + const input = bscript.fromASM(f.input) + const type = classify.input(input, true) + + assert.strictEqual(type, f.typeIncomplete) + }) + }) + }) + + describe('classifyOutput', function () { + fixtures.valid.forEach(function (f) { + if (!f.output) return + + it('classifies ' + f.output + ' as ' + f.type, function () { + const output = bscript.fromASM(f.output) + const type = classify.output(output) + + assert.strictEqual(type, f.type) + }) + }) + }) + + ;[ + 'pubKey', + 'pubKeyHash', + 'scriptHash', + 'witnessPubKeyHash', + 'witnessScriptHash', + 'multisig', + 'nullData', + 'witnessCommitment' + ].forEach(function (name) { + const inputType = tmap[name].input + const outputType = tmap[name].output + + describe(name + '.input.check', function () { + fixtures.valid.forEach(function (f) { + if (name.toLowerCase() === classify.types.P2WPKH) return + if (name.toLowerCase() === classify.types.P2WSH) return + const expected = name.toLowerCase() === f.type.toLowerCase() + + if (inputType && f.input) { + const input = bscript.fromASM(f.input) + + it('returns ' + expected + ' for ' + f.input, function () { + assert.strictEqual(inputType.check(input), expected) + }) + + if (f.typeIncomplete) { + const expectedIncomplete = name.toLowerCase() === f.typeIncomplete + + it('returns ' + expected + ' for ' + f.input, function () { + assert.strictEqual(inputType.check(input, true), expectedIncomplete) + }) + } + } + }) + + if (!(fixtures.invalid[name])) return + + fixtures.invalid[name].inputs.forEach(function (f) { + if (!f.input && !f.inputHex) return + + it('returns false for ' + f.description + ' (' + (f.input || f.inputHex) + ')', function () { + let input + + if (f.input) { + input = bscript.fromASM(f.input) + } else { + input = Buffer.from(f.inputHex, 'hex') + } + + assert.strictEqual(inputType.check(input), false) + }) + }) + }) + + describe(name + '.output.check', function () { + fixtures.valid.forEach(function (f) { + const expected = name.toLowerCase() === f.type + + if (outputType && f.output) { + it('returns ' + expected + ' for ' + f.output, function () { + const output = bscript.fromASM(f.output) + + if (name.toLowerCase() === 'nulldata' && f.type === classify.types.WITNESS_COMMITMENT) return + if (name.toLowerCase() === 'witnesscommitment' && f.type === classify.types.NULLDATA) return + assert.strictEqual(outputType.check(output), expected) + }) + } + }) + + if (!(fixtures.invalid[name])) return + + fixtures.invalid[name].outputs.forEach(function (f) { + if (!f.output && !f.outputHex) return + + it('returns false for ' + f.description + ' (' + (f.output || f.outputHex) + ')', function () { + let output + + if (f.output) { + output = bscript.fromASM(f.output) + } else { + output = Buffer.from(f.outputHex, 'hex') + } + + assert.strictEqual(outputType.check(output), false) + }) + }) + }) + }) +}) diff --git a/test/templates.js b/test/templates.js deleted file mode 100644 index 7058c51..0000000 --- a/test/templates.js +++ /dev/null @@ -1,514 +0,0 @@ -/* global describe, it */ - -const assert = require('assert') -const bcrypto = require('../src/crypto') -const bscript = require('../src/script') -const btemplates = require('../src/templates') -const ops = require('bitcoin-ops') - -const fixtures = require('./fixtures/templates.json') - -function fromHex (x) { return Buffer.from(x, 'hex') } -function toHex (x) { return x.toString('hex') } - -describe('script-templates', function () { - describe('classifyInput', function () { - fixtures.valid.forEach(function (f) { - if (!f.input) return - - it('classifies ' + f.input + ' as ' + f.type, function () { - const input = bscript.fromASM(f.input) - const type = btemplates.classifyInput(input) - - assert.strictEqual(type, f.type) - }) - }) - - fixtures.valid.forEach(function (f) { - if (!f.input) return - if (!f.typeIncomplete) return - - it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, function () { - const input = bscript.fromASM(f.input) - const type = btemplates.classifyInput(input, true) - - assert.strictEqual(type, f.typeIncomplete) - }) - }) - }) - - describe('classifyOutput', function () { - fixtures.valid.forEach(function (f) { - if (!f.output) return - - it('classifies ' + f.output + ' as ' + f.type, function () { - const output = bscript.fromASM(f.output) - const type = btemplates.classifyOutput(output) - - assert.strictEqual(type, f.type) - }) - }) - }) - - ;[ - 'pubKey', - 'pubKeyHash', - 'scriptHash', - 'witnessPubKeyHash', - 'witnessScriptHash', - 'multisig', - 'nullData', - 'witnessCommitment' - ].forEach(function (name) { - const inputType = btemplates[name].input - const outputType = btemplates[name].output - - describe(name + '.input.check', function () { - fixtures.valid.forEach(function (f) { - if (name.toLowerCase() === btemplates.types.P2WPKH) return - if (name.toLowerCase() === btemplates.types.P2WSH) return - const expected = name.toLowerCase() === f.type.toLowerCase() - - if (inputType && f.input) { - const input = bscript.fromASM(f.input) - - it('returns ' + expected + ' for ' + f.input, function () { - assert.strictEqual(inputType.check(input), expected) - }) - - if (f.typeIncomplete) { - const expectedIncomplete = name.toLowerCase() === f.typeIncomplete - - it('returns ' + expected + ' for ' + f.input, function () { - assert.strictEqual(inputType.check(input, true), expectedIncomplete) - }) - } - } - }) - - if (!(fixtures.invalid[name])) return - - fixtures.invalid[name].inputs.forEach(function (f) { - if (!f.input && !f.inputHex) return - - it('returns false for ' + f.description + ' (' + (f.input || f.inputHex) + ')', function () { - let input - - if (f.input) { - input = bscript.fromASM(f.input) - } else { - input = Buffer.from(f.inputHex, 'hex') - } - - assert.strictEqual(inputType.check(input), false) - }) - }) - }) - - describe(name + '.output.check', function () { - fixtures.valid.forEach(function (f) { - const expected = name.toLowerCase() === f.type - - if (outputType && f.output) { - it('returns ' + expected + ' for ' + f.output, function () { - const output = bscript.fromASM(f.output) - - if (name.toLowerCase() === 'nulldata' && f.type === btemplates.types.WITNESS_COMMITMENT) return - if (name.toLowerCase() === 'witnesscommitment' && f.type === btemplates.types.NULLDATA) return - assert.strictEqual(outputType.check(output), expected) - }) - } - }) - - if (!(fixtures.invalid[name])) return - - fixtures.invalid[name].outputs.forEach(function (f) { - if (!f.output && !f.outputHex) return - - it('returns false for ' + f.description + ' (' + (f.output || f.outputHex) + ')', function () { - let output - - if (f.output) { - output = bscript.fromASM(f.output) - } else { - output = Buffer.from(f.outputHex, 'hex') - } - - assert.strictEqual(outputType.check(output), false) - }) - }) - }) - }) - - describe('pubKey.input', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'pubkey') return - - const signature = Buffer.from(f.signature, 'hex') - const input = btemplates.pubKey.input.encode(signature) - - it('encodes to ' + f.input, function () { - assert.strictEqual(bscript.toASM(input), f.input) - }) - - it('decodes to ' + f.signature, function () { - assert.deepEqual(btemplates.pubKey.input.decode(input), signature) - }) - }) - }) - - describe('pubKey.output', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'pubkey') return - - const pubKey = Buffer.from(f.pubKey, 'hex') - const output = btemplates.pubKey.output.encode(pubKey) - - it('encodes to ' + f.output, function () { - assert.strictEqual(bscript.toASM(output), f.output) - }) - - it('decodes to ' + f.pubKey, function () { - assert.deepEqual(btemplates.pubKey.output.decode(output), pubKey) - }) - }) - }) - - describe('pubKeyHash.input', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'pubkeyhash') return - - const pubKey = Buffer.from(f.pubKey, 'hex') - const signature = Buffer.from(f.signature, 'hex') - const input = btemplates.pubKeyHash.input.encode(signature, pubKey) - - it('encodes to ' + f.input, function () { - assert.strictEqual(bscript.toASM(input), f.input) - }) - - it('decodes to original arguments', function () { - assert.deepEqual(btemplates.pubKeyHash.input.decode(input), { - signature: signature, - pubKey: pubKey - }) - }) - }) - }) - - describe('pubKeyHash.output', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'pubkeyhash') return - - const pubKey = Buffer.from(f.pubKey, 'hex') - const pubKeyHash = bcrypto.hash160(pubKey) - const output = btemplates.pubKeyHash.output.encode(pubKeyHash) - - it('encodes to ' + f.output, function () { - assert.strictEqual(bscript.toASM(output), f.output) - }) - - it('decodes to ' + pubKeyHash.toString('hex'), function () { - assert.deepEqual(btemplates.pubKeyHash.output.decode(output), pubKeyHash) - }) - }) - - fixtures.invalid.pubKeyHash.outputs.forEach(function (f) { - if (!f.hash) return - const hash = Buffer.from(f.hash, 'hex') - - it('throws on ' + f.exception, function () { - assert.throws(function () { - btemplates.pubKeyHash.output.encode(hash) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('multisig.input', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'multisig' && f.typeIncomplete !== 'multisig') return - const allowIncomplete = f.typeIncomplete !== undefined - - const signatures = f.signatures.map(function (signature) { - return signature ? Buffer.from(signature, 'hex') : ops.OP_0 - }) - - const input = btemplates.multisig.input.encode(signatures) - - it('encodes to ' + f.input, function () { - assert.strictEqual(bscript.toASM(input), f.input) - }) - - it('decodes to ' + signatures.map(function (x) { return x === ops.OP_0 ? 'OP_0' : x.toString('hex') }), function () { - assert.deepEqual(btemplates.multisig.input.decode(input, allowIncomplete), signatures) - }) - }) - - fixtures.invalid.multisig.inputs.forEach(function (f) { - if (!f.output) return - const output = bscript.fromASM(f.output) - - it('throws on ' + f.exception, function () { - const signatures = f.signatures.map(function (signature) { - return signature ? Buffer.from(signature, 'hex') : ops.OP_0 - }) - - assert.throws(function () { - btemplates.multisig.input.encode(signatures, output) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('multisig.output', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'multisig') return - - const pubKeys = f.pubKeys.map(function (p) { return Buffer.from(p, 'hex') }) - const m = pubKeys.length - - const output = btemplates.multisig.output.encode(m, pubKeys) - - it('encodes ' + f.output, function () { - assert.strictEqual(bscript.toASM(output), f.output) - }) - - it('decodes to original arguments', function () { - assert.deepEqual(btemplates.multisig.output.decode(output), { - m: m, - pubKeys: pubKeys - }) - }) - }) - - fixtures.invalid.multisig.outputs.forEach(function (f) { - if (!f.pubKeys) return - const pubKeys = f.pubKeys.map(function (p) { - return Buffer.from(p, 'hex') - }) - - it('throws on ' + f.exception, function () { - assert.throws(function () { - btemplates.multisig.output.encode(f.m, pubKeys) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('scriptHash.input', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'scripthash') return - - const redeemScriptSig = bscript.fromASM(f.redeemScriptSig) - const redeemScript = bscript.fromASM(f.redeemScript) - const input = btemplates.scriptHash.input.encode(redeemScriptSig, redeemScript) - - it('encodes to ' + f.output, function () { - if (f.input) { - assert.strictEqual(bscript.toASM(input), f.input) - } else { - assert.strictEqual(input.toString('hex'), f.inputHex) - } - }) - - it('decodes to original arguments', function () { - assert.deepEqual(btemplates.scriptHash.input.decode(input), { - redeemScriptSig: redeemScriptSig, - redeemScript: redeemScript - }) - }) - }) - }) - - describe('scriptHash.output', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'scripthash') return - if (!f.output) return - - const redeemScript = bscript.fromASM(f.redeemScript) - const scriptHash = bcrypto.hash160(redeemScript) - const output = btemplates.scriptHash.output.encode(scriptHash) - - it('encodes to ' + f.output, function () { - assert.strictEqual(bscript.toASM(output), f.output) - }) - - it('decodes to ' + scriptHash.toString('hex'), function () { - assert.deepEqual(btemplates.scriptHash.output.decode(output), scriptHash) - }) - }) - - fixtures.invalid.scriptHash.outputs.forEach(function (f) { - if (!f.hash) return - const hash = Buffer.from(f.hash, 'hex') - - it('throws on ' + f.exception, function () { - assert.throws(function () { - btemplates.scriptHash.output.encode(hash) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('witnessPubKeyHash.input', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'pubkeyhash' && f.type !== 'witnesspubkeyhash') return - if (!f.inputStack) return - - const pubKey = Buffer.from(f.pubKey, 'hex') - const signature = Buffer.from(f.signature, 'hex') - - it('encodes to ' + f.input, function () { - const inputStack = btemplates.witnessPubKeyHash.input.encodeStack(signature, pubKey) - - assert.deepEqual(inputStack.map(toHex), f.inputStack) - }) - - it('decodes to original arguments', function () { - const fInputStack = f.inputStack.map(fromHex) - - assert.deepEqual(btemplates.witnessPubKeyHash.input.decodeStack(fInputStack), { - signature: signature, - pubKey: pubKey - }) - }) - }) - }) - - describe('witnessPubKeyHash.output', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'witnesspubkeyhash') return - if (!f.output) return - - const pubKey = Buffer.from(f.pubKey, 'hex') - const pubKeyHash = bcrypto.hash160(pubKey) - const output = btemplates.witnessPubKeyHash.output.encode(pubKeyHash) - - it('encodes to ' + f.output, function () { - assert.strictEqual(bscript.toASM(output), f.output) - }) - - it('decodes to ' + pubKeyHash.toString('hex'), function () { - assert.deepEqual(btemplates.witnessPubKeyHash.output.decode(output), pubKeyHash) - }) - }) - - fixtures.invalid.witnessPubKeyHash.outputs.forEach(function (f) { - if (!f.hash) return - const hash = Buffer.from(f.hash, 'hex') - - it('throws on ' + f.exception, function () { - assert.throws(function () { - btemplates.witnessPubKeyHash.output.encode(hash) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('witnessScriptHash.input', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'witnessscripthash') return - if (!f.inputStack || !f.witnessData) return - - const witnessData = f.witnessData.map(fromHex) - const witnessScript = bscript.fromASM(f.witnessScript || f.redeemScript) - - it('encodes to ' + f.input, function () { - const inputStack = btemplates.witnessScriptHash.input.encodeStack(witnessData, witnessScript) - - assert.deepEqual(inputStack.map(toHex), f.inputStack) - }) - - it('decodes to original arguments', function () { - const result = btemplates.witnessScriptHash.input.decodeStack(f.inputStack.map(fromHex)) - - assert.deepEqual(result.witnessData.map(toHex), f.witnessData) - assert.strictEqual(bscript.toASM(result.witnessScript), f.witnessScript) - }) - }) - }) - - describe('witnessScriptHash.output', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'witnessscripthash') return - if (!f.output) return - - const witnessScriptPubKey = bscript.fromASM(f.witnessScript) - const scriptHash = bcrypto.hash256(witnessScriptPubKey) - const output = btemplates.witnessScriptHash.output.encode(scriptHash) - - it('encodes to ' + f.output, function () { - assert.strictEqual(bscript.toASM(output), f.output) - }) - - it('decodes to ' + scriptHash.toString('hex'), function () { - assert.deepEqual(btemplates.witnessScriptHash.output.decode(output), scriptHash) - }) - }) - - fixtures.invalid.witnessScriptHash.outputs.forEach(function (f) { - if (!f.hash) return - const hash = Buffer.from(f.hash, 'hex') - - it('throws on ' + f.exception, function () { - assert.throws(function () { - btemplates.witnessScriptHash.output.encode(hash) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('witnessCommitment.output', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'witnesscommitment') return - if (!f.scriptPubKey) return - - const commitment = Buffer.from(f.witnessCommitment, 'hex') - const scriptPubKey = btemplates.witnessCommitment.output.encode(commitment) - - it('encodes to ' + f.scriptPubKey, function () { - assert.strictEqual(bscript.toASM(scriptPubKey), f.scriptPubKey) - }) - - it('decodes to ' + commitment.toString('hex'), function () { - assert.deepEqual(btemplates.witnessCommitment.output.decode(scriptPubKey), commitment) - }) - }) - - fixtures.invalid.witnessCommitment.outputs.forEach(function (f) { - if (f.commitment) { - const hash = Buffer.from(f.commitment, 'hex') - it('throws on bad encode data', function () { - assert.throws(function () { - btemplates.witnessCommitment.output.encode(hash) - }, new RegExp(f.exception)) - }) - } - - if (f.scriptPubKeyHex) { - it('.decode throws on ' + f.description, function () { - assert.throws(function () { - btemplates.witnessCommitment.output.decode(Buffer.from(f.scriptPubKeyHex, 'hex')) - }, new RegExp(f.exception)) - }) - } - }) - }) - - describe('nullData.output', function () { - fixtures.valid.forEach(function (f) { - if (f.type !== 'nulldata') return - - const data = f.data.map(function (x) { return Buffer.from(x, 'hex') }) - const output = btemplates.nullData.output.encode(data) - - it('encodes to ' + f.output, function () { - assert.strictEqual(bscript.toASM(output), f.output) - }) - - it('decodes to ' + f.data, function () { - assert.deepEqual(btemplates.nullData.output.decode(output), data) - }) - }) - }) -}) From eb01d35aa492b4dc9a91e775661f8006573af1a2 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 3 Jul 2018 21:52:25 +1000 Subject: [PATCH 063/568] tests: rm last use of templates for txbuilder --- test/transaction_builder.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index e307398..34ec9da 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -4,8 +4,8 @@ const assert = require('assert') const baddress = require('../src/address') const bcrypto = require('../src/crypto') const bscript = require('../src/script') -const btemplates = require('../src/templates') const ops = require('bitcoin-ops') +const payments = require('../src/payments') const ECPair = require('../src/ecpair') const Transaction = require('../src/transaction') @@ -468,10 +468,17 @@ describe('TransactionBuilder', function () { const scriptSig = tx.ins[i].script // ignore OP_0 on the front, ignore redeemScript - const signatures = bscript.decompile(scriptSig).slice(1, -1).filter(function (x) { return x !== ops.OP_0 }) + const signatures = bscript.decompile(scriptSig) + .slice(1, -1) + .filter(x => x !== ops.OP_0) // rebuild/replace the scriptSig without them - const replacement = btemplates.scriptHash.input.encode(btemplates.multisig.input.encode(signatures), redeemScript) + const replacement = payments.p2sh({ + redeem: payments.p2ms({ + output: redeemScript, + signatures + }, { allowIncomplete: true }) + }).input assert.strictEqual(bscript.toASM(replacement), sign.scriptSigFiltered) tx.ins[i].script = replacement From 0d0f1d0847eebedab55f0dc7d3bc51b3100d86aa Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 3 Jul 2018 22:06:44 +1000 Subject: [PATCH 064/568] payments: rm dependence on address export --- src/payments/p2pkh.js | 35 ++++++++++++++++++++++------------- src/payments/p2sh.js | 15 ++++++++++++--- src/payments/p2wpkh.js | 42 +++++++++++++++++++++++++++--------------- src/payments/p2wsh.js | 41 ++++++++++++++++++++++++++--------------- 4 files changed, 87 insertions(+), 46 deletions(-) diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index 9d0733d..140f707 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -1,12 +1,12 @@ -let lazy = require('./lazy') -let typef = require('typeforce') -let OPS = require('bitcoin-ops') -let ecc = require('tiny-secp256k1') +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') +const ecc = require('tiny-secp256k1') -let baddress = require('../address') -let bcrypto = require('../crypto') -let bscript = require('../script') -let BITCOIN_NETWORK = require('../networks').bitcoin +const bcrypto = require('../crypto') +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin +const bs58check = require('bs58check') // input: {signature} {pubkey} // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG @@ -31,15 +31,24 @@ function p2pkh (a, opts) { input: typef.maybe(typef.Buffer) }, a) - let _address = lazy.value(function () { return baddress.fromBase58Check(a.address) }) - let _chunks = lazy.value(function () { return bscript.decompile(a.input) }) + const _address = lazy.value(function () { + const payload = bs58check.decode(a.address) + const version = payload.readUInt8(0) + const hash = payload.slice(1) + return { version, hash } + }) + const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) - let network = a.network || BITCOIN_NETWORK - let o = { network } + const network = a.network || BITCOIN_NETWORK + const o = { network } lazy.prop(o, 'address', function () { if (!o.hash) return - return baddress.toBase58Check(o.hash, network.pubKeyHash) + + const payload = Buffer.allocUnsafe(21) + payload.writeUInt8(network.pubKeyHash, 0) + o.hash.copy(payload, 1) + return bs58check.encode(payload) }) lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(3, 23) diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index b26119d..9b28a3a 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -2,10 +2,10 @@ const lazy = require('./lazy') const typef = require('typeforce') const OPS = require('bitcoin-ops') -const baddress = require('../address') const bcrypto = require('../crypto') const bscript = require('../script') const BITCOIN_NETWORK = require('../networks').bitcoin +const bs58check = require('bs58check') function stacksEqual (a, b) { if (a.length !== b.length) return false @@ -48,7 +48,12 @@ function p2sh (a, opts) { const network = a.network || BITCOIN_NETWORK const o = { network } - const _address = lazy.value(function () { return baddress.fromBase58Check(a.address) }) + const _address = lazy.value(function () { + const payload = bs58check.decode(a.address) + const version = payload.readUInt8(0) + const hash = payload.slice(1) + return { version, hash } + }) const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) const _redeem = lazy.value(function () { const chunks = _chunks() @@ -63,7 +68,11 @@ function p2sh (a, opts) { // output dependents lazy.prop(o, 'address', function () { if (!o.hash) return - return baddress.toBase58Check(o.hash, network.scriptHash) + + const payload = Buffer.allocUnsafe(21) + payload.writeUInt8(network.scriptHash, 0) + o.hash.copy(payload, 1) + return bs58check.encode(payload) }) lazy.prop(o, 'hash', function () { // in order of least effort diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index f2bdeb4..bd010f7 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -1,14 +1,14 @@ -let lazy = require('./lazy') -let typef = require('typeforce') -let OPS = require('bitcoin-ops') -let ecc = require('tiny-secp256k1') +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') +const ecc = require('tiny-secp256k1') -let baddress = require('../address') -let bcrypto = require('../crypto') -let bscript = require('../script') -let BITCOIN_NETWORK = require('../networks').bitcoin +const bcrypto = require('../crypto') +const bech32 = require('bech32') +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin -let EMPTY_BUFFER = Buffer.alloc(0) +const EMPTY_BUFFER = Buffer.alloc(0) // witness: {signature} {pubKey} // input: <> @@ -34,14 +34,26 @@ function p2wpkh (a, opts) { witness: typef.maybe(typef.arrayOf(typef.Buffer)) }, a) - let _address = lazy.value(function () { return baddress.fromBech32(a.address) }) + const _address = lazy.value(function () { + const result = bech32.decode(a.address) + const version = result.words.shift() + const data = bech32.fromWords(result.words) + return { + version, + prefix: result.prefix, + data: Buffer.from(data) + } + }) - let network = a.network || BITCOIN_NETWORK - let o = { network } + const network = a.network || BITCOIN_NETWORK + const o = { network } lazy.prop(o, 'address', function () { if (!o.hash) return - return baddress.toBech32(o.hash, 0x00, network.bech32) + + const words = bech32.toWords(o.hash) + words.unshift(0x00) + return bech32.encode(network.bech32, words) }) lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(2, 22) @@ -86,7 +98,7 @@ function p2wpkh (a, opts) { } if (a.pubkey) { - let pkh = bcrypto.hash160(a.pubkey) + const pkh = bcrypto.hash160(a.pubkey) if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') else hash = pkh } @@ -113,7 +125,7 @@ function p2wpkh (a, opts) { if (a.signature && !a.signature.equals(a.witness[0])) throw new TypeError('Signature mismatch') if (a.pubkey && !a.pubkey.equals(a.witness[1])) throw new TypeError('Pubkey mismatch') - let pkh = bcrypto.hash160(a.witness[1]) + const pkh = bcrypto.hash160(a.witness[1]) if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') } } diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index 6f53484..e1adcc5 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -1,13 +1,13 @@ -let lazy = require('./lazy') -let typef = require('typeforce') -let OPS = require('bitcoin-ops') +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') -let baddress = require('../address') -let bcrypto = require('../crypto') -let bscript = require('../script') -let BITCOIN_NETWORK = require('../networks').bitcoin +const bech32 = require('bech32') +const bcrypto = require('../crypto') +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin -let EMPTY_BUFFER = Buffer.alloc(0) +const EMPTY_BUFFER = Buffer.alloc(0) function stacksEqual (a, b) { if (a.length !== b.length) return false @@ -47,19 +47,30 @@ function p2wsh (a, opts) { witness: typef.maybe(typef.arrayOf(typef.Buffer)) }, a) - let _address = lazy.value(function () { return baddress.fromBech32(a.address) }) - let _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input) }) + const _address = lazy.value(function () { + const result = bech32.decode(a.address) + const version = result.words.shift() + const data = bech32.fromWords(result.words) + return { + version, + prefix: result.prefix, + data: Buffer.from(data) + } + }) + const _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input) }) - let network = a.network || BITCOIN_NETWORK - let o = { network } + const network = a.network || BITCOIN_NETWORK + const o = { network } lazy.prop(o, 'address', function () { if (!o.hash) return - return baddress.toBech32(o.hash, 0x00, network.bech32) + const words = bech32.toWords(o.hash) + words.unshift(0x00) + return bech32.encode(network.bech32, words) }) lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(2) - if (a.address) return baddress.fromBech32(a.address).data + if (a.address) return _address().data if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output) }) lazy.prop(o, 'output', function () { @@ -145,7 +156,7 @@ function p2wsh (a, opts) { if (bscript.decompile(a.redeem.output).length === 0) throw new TypeError('Redeem.output is invalid') // match hash against other sources - let hash2 = bcrypto.sha256(a.redeem.output) + const hash2 = bcrypto.sha256(a.redeem.output) if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') else hash = hash2 } From 4454e2925abd9976a560ab859959c50e85212833 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 3 Jul 2018 22:54:24 +1000 Subject: [PATCH 065/568] tests/integration: rm use of templates --- test/integration/csv.js | 59 ++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/test/integration/csv.js b/test/integration/csv.js index ac0b919..e0ad02f 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -12,6 +12,7 @@ const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZs describe('bitcoinjs-lib (transactions w/ CSV)', function () { // force update MTP before(function (done) { + this.timeout(30000) regtestUtils.mine(11, done) }) @@ -44,12 +45,15 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // 5 blocks from now const sequence = bip68.encode({ blocks: 5 }) - const redeemScript = csvCheckSigOutput(alice, bob, sequence) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const p2sh = bitcoin.payments.p2sh({ + redeem: { + output: csvCheckSigOutput(alice, bob, sequence) + }, + network: regtest + }) // fund the P2SH(CSV) address - regtestUtils.faucet(address, 1e5, function (err, unspent) { + regtestUtils.faucet(p2sh.address, 1e5, function (err, unspent) { if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest) @@ -58,11 +62,18 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // {Alice's signature} OP_TRUE const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ], redeemScript) + const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem.output, + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]) + } + }).input tx.setInputScript(0, redeemScriptSig) // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently @@ -92,12 +103,15 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // two hours after confirmation const sequence = bip68.encode({ seconds: 7168 }) - const redeemScript = csvCheckSigOutput(alice, bob, sequence) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const p2sh = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + output: csvCheckSigOutput(alice, bob, sequence) + } + }) // fund the P2SH(CSV) address - regtestUtils.faucet(address, 2e4, function (err, unspent) { + regtestUtils.faucet(p2sh.address, 2e4, function (err, unspent) { if (err) return done(err) const txb = new bitcoin.TransactionBuilder(regtest) @@ -106,12 +120,19 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // {Alice's signature} OP_TRUE const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ], redeemScript) + const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem.output, + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]) + } + }).input tx.setInputScript(0, redeemScriptSig) regtestUtils.broadcast(tx.toHex(), function (err) { From a9090dc0cc6f44824de459e1698fcca4b68b824e Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 3 Jul 2018 23:00:00 +1000 Subject: [PATCH 066/568] templates: rm dead code --- src/templates/multisig/input.js | 51 +---------------------- src/templates/multisig/output.js | 37 +--------------- src/templates/pubkey/input.js | 27 +----------- src/templates/pubkey/output.js | 20 +-------- src/templates/pubkeyhash/input.js | 40 +----------------- src/templates/pubkeyhash/output.js | 26 +----------- src/templates/scripthash/input.js | 39 +---------------- src/templates/scripthash/output.js | 20 +-------- src/templates/witnesspubkeyhash/input.js | 29 +------------ src/templates/witnesspubkeyhash/output.js | 18 +------- src/templates/witnessscripthash/input.js | 27 +----------- src/templates/witnessscripthash/output.js | 20 +-------- 12 files changed, 12 insertions(+), 342 deletions(-) diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js index 318e911..a66f05f 100644 --- a/src/templates/multisig/input.js +++ b/src/templates/multisig/input.js @@ -1,9 +1,6 @@ // OP_0 [signatures ...] -const Buffer = require('safe-buffer').Buffer const bscript = require('../../script') -const p2mso = require('./output') -const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function partialSignature (value) { @@ -23,50 +20,4 @@ function check (script, allowIncomplete) { } check.toJSON = function () { return 'multisig input' } -const EMPTY_BUFFER = Buffer.allocUnsafe(0) - -function encodeStack (signatures, scriptPubKey) { - typeforce([partialSignature], signatures) - - if (scriptPubKey) { - const scriptData = p2mso.decode(scriptPubKey) - - if (signatures.length < scriptData.m) { - throw new TypeError('Not enough signatures provided') - } - - if (signatures.length > scriptData.pubKeys.length) { - throw new TypeError('Too many signatures provided') - } - } - - return [].concat(EMPTY_BUFFER, signatures.map(function (sig) { - if (sig === OPS.OP_0) { - return EMPTY_BUFFER - } - return sig - })) -} - -function encode (signatures, scriptPubKey) { - return bscript.compile(encodeStack(signatures, scriptPubKey)) -} - -function decodeStack (stack, allowIncomplete) { - typeforce(typeforce.Array, stack) - typeforce(check, stack, allowIncomplete) - return stack.slice(1) -} - -function decode (buffer, allowIncomplete) { - const stack = bscript.decompile(buffer) - return decodeStack(stack, allowIncomplete) -} - -module.exports = { - check: check, - decode: decode, - decodeStack: decodeStack, - encode: encode, - encodeStack: encodeStack -} +module.exports = { check } diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.js index 34b9ff4..5c9d81f 100644 --- a/src/templates/multisig/output.js +++ b/src/templates/multisig/output.js @@ -2,7 +2,6 @@ const bscript = require('../../script') const types = require('../../types') -const typeforce = require('typeforce') const OPS = require('bitcoin-ops') const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 @@ -27,38 +26,4 @@ function check (script, allowIncomplete) { } check.toJSON = function () { return 'multi-sig output' } -function encode (m, pubKeys) { - typeforce({ - m: types.Number, - pubKeys: [bscript.isCanonicalPubKey] - }, { - m: m, - pubKeys: pubKeys - }) - - const n = pubKeys.length - if (n < m) throw new TypeError('Not enough pubKeys provided') - - return bscript.compile([].concat( - OP_INT_BASE + m, - pubKeys, - OP_INT_BASE + n, - OPS.OP_CHECKMULTISIG - )) -} - -function decode (buffer, allowIncomplete) { - const chunks = bscript.decompile(buffer) - typeforce(check, chunks, allowIncomplete) - - return { - m: chunks[0] - OP_INT_BASE, - pubKeys: chunks.slice(1, -2) - } -} - -module.exports = { - check: check, - decode: decode, - encode: encode -} +module.exports = { check } diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js index cd5ffd3..ec21155 100644 --- a/src/templates/pubkey/input.js +++ b/src/templates/pubkey/input.js @@ -1,7 +1,6 @@ // {signature} const bscript = require('../../script') -const typeforce = require('typeforce') function check (script) { const chunks = bscript.decompile(script) @@ -11,30 +10,6 @@ function check (script) { } check.toJSON = function () { return 'pubKey input' } -function encodeStack (signature) { - typeforce(bscript.isCanonicalScriptSignature, signature) - return [signature] -} - -function encode (signature) { - return bscript.compile(encodeStack(signature)) -} - -function decodeStack (stack) { - typeforce(typeforce.Array, stack) - typeforce(check, stack) - return stack[0] -} - -function decode (buffer) { - const stack = bscript.decompile(buffer) - return decodeStack(stack) -} - module.exports = { - check: check, - decode: decode, - decodeStack: decodeStack, - encode: encode, - encodeStack: encodeStack + check: check } diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.js index 2d34b4f..b25c8c1 100644 --- a/src/templates/pubkey/output.js +++ b/src/templates/pubkey/output.js @@ -1,7 +1,6 @@ // {pubKey} OP_CHECKSIG const bscript = require('../../script') -const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { @@ -13,21 +12,4 @@ function check (script) { } check.toJSON = function () { return 'pubKey output' } -function encode (pubKey) { - typeforce(bscript.isCanonicalPubKey, pubKey) - - return bscript.compile([pubKey, OPS.OP_CHECKSIG]) -} - -function decode (buffer) { - const chunks = bscript.decompile(buffer) - typeforce(check, chunks) - - return chunks[0] -} - -module.exports = { - check: check, - decode: decode, - encode: encode -} +module.exports = { check } diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js index 2f633e1..f5bc452 100644 --- a/src/templates/pubkeyhash/input.js +++ b/src/templates/pubkeyhash/input.js @@ -1,7 +1,6 @@ // {signature} {pubKey} const bscript = require('../../script') -const typeforce = require('typeforce') function check (script) { const chunks = bscript.decompile(script) @@ -12,41 +11,4 @@ function check (script) { } check.toJSON = function () { return 'pubKeyHash input' } -function encodeStack (signature, pubKey) { - typeforce({ - signature: bscript.isCanonicalScriptSignature, - pubKey: bscript.isCanonicalPubKey - }, { - signature: signature, - pubKey: pubKey - }) - - return [signature, pubKey] -} - -function encode (signature, pubKey) { - return bscript.compile(encodeStack(signature, pubKey)) -} - -function decodeStack (stack) { - typeforce(typeforce.Array, stack) - typeforce(check, stack) - - return { - signature: stack[0], - pubKey: stack[1] - } -} - -function decode (buffer) { - const stack = bscript.decompile(buffer) - return decodeStack(stack) -} - -module.exports = { - check: check, - decode: decode, - decodeStack: decodeStack, - encode: encode, - encodeStack: encodeStack -} +module.exports = { check } diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.js index c496275..fbb6ed1 100644 --- a/src/templates/pubkeyhash/output.js +++ b/src/templates/pubkeyhash/output.js @@ -1,8 +1,6 @@ // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG const bscript = require('../../script') -const types = require('../../types') -const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { @@ -17,26 +15,4 @@ function check (script) { } check.toJSON = function () { return 'pubKeyHash output' } -function encode (pubKeyHash) { - typeforce(types.Hash160bit, pubKeyHash) - - return bscript.compile([ - OPS.OP_DUP, - OPS.OP_HASH160, - pubKeyHash, - OPS.OP_EQUALVERIFY, - OPS.OP_CHECKSIG - ]) -} - -function decode (buffer) { - typeforce(check, buffer) - - return buffer.slice(3, 23) -} - -module.exports = { - check: check, - decode: decode, - encode: encode -} +module.exports = { check } diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js index fdea2a1..5164370 100644 --- a/src/templates/scripthash/input.js +++ b/src/templates/scripthash/input.js @@ -2,7 +2,6 @@ const Buffer = require('safe-buffer').Buffer const bscript = require('../../script') -const typeforce = require('typeforce') const p2ms = require('../multisig/') const p2pk = require('../pubkey/') @@ -46,40 +45,4 @@ function check (script, allowIncomplete) { } check.toJSON = function () { return 'scriptHash input' } -function encodeStack (redeemScriptStack, redeemScript) { - const serializedScriptPubKey = bscript.compile(redeemScript) - - return [].concat(redeemScriptStack, serializedScriptPubKey) -} - -function encode (redeemScriptSig, redeemScript) { - const redeemScriptStack = bscript.decompile(redeemScriptSig) - - return bscript.compile(encodeStack(redeemScriptStack, redeemScript)) -} - -function decodeStack (stack) { - typeforce(typeforce.Array, stack) - typeforce(check, stack) - - return { - redeemScriptStack: stack.slice(0, -1), - redeemScript: stack[stack.length - 1] - } -} - -function decode (buffer) { - const stack = bscript.decompile(buffer) - const result = decodeStack(stack) - result.redeemScriptSig = bscript.compile(result.redeemScriptStack) - delete result.redeemScriptStack - return result -} - -module.exports = { - check: check, - decode: decode, - decodeStack: decodeStack, - encode: encode, - encodeStack: encodeStack -} +module.exports = { check } diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.js index 68ee237..b5b6e7a 100644 --- a/src/templates/scripthash/output.js +++ b/src/templates/scripthash/output.js @@ -1,8 +1,6 @@ // OP_HASH160 {scriptHash} OP_EQUAL const bscript = require('../../script') -const types = require('../../types') -const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { @@ -15,20 +13,4 @@ function check (script) { } check.toJSON = function () { return 'scriptHash output' } -function encode (scriptHash) { - typeforce(types.Hash160bit, scriptHash) - - return bscript.compile([OPS.OP_HASH160, scriptHash, OPS.OP_EQUAL]) -} - -function decode (buffer) { - typeforce(check, buffer) - - return buffer.slice(2, 22) -} - -module.exports = { - check: check, - decode: decode, - encode: encode -} +module.exports = { check } diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.js index 23de02a..488e5e6 100644 --- a/src/templates/witnesspubkeyhash/input.js +++ b/src/templates/witnesspubkeyhash/input.js @@ -1,7 +1,6 @@ // {signature} {pubKey} const bscript = require('../../script') -const typeforce = require('typeforce') function isCompressedCanonicalPubKey (pubKey) { return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33 @@ -16,30 +15,4 @@ function check (script) { } check.toJSON = function () { return 'witnessPubKeyHash input' } -function encodeStack (signature, pubKey) { - typeforce({ - signature: bscript.isCanonicalScriptSignature, - pubKey: isCompressedCanonicalPubKey - }, { - signature: signature, - pubKey: pubKey - }) - - return [signature, pubKey] -} - -function decodeStack (stack) { - typeforce(typeforce.Array, stack) - typeforce(check, stack) - - return { - signature: stack[0], - pubKey: stack[1] - } -} - -module.exports = { - check: check, - decodeStack: decodeStack, - encodeStack: encodeStack -} +module.exports = { check } diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.js index 65ec47a..08af3bc 100644 --- a/src/templates/witnesspubkeyhash/output.js +++ b/src/templates/witnesspubkeyhash/output.js @@ -1,8 +1,6 @@ // OP_0 {pubKeyHash} const bscript = require('../../script') -const types = require('../../types') -const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { @@ -14,20 +12,6 @@ function check (script) { } check.toJSON = function () { return 'Witness pubKeyHash output' } -function encode (pubKeyHash) { - typeforce(types.Hash160bit, pubKeyHash) - - return bscript.compile([OPS.OP_0, pubKeyHash]) -} - -function decode (buffer) { - typeforce(check, buffer) - - return buffer.slice(2) -} - module.exports = { - check: check, - decode: decode, - encode: encode + check } diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js index c7fb98f..072a289 100644 --- a/src/templates/witnessscripthash/input.js +++ b/src/templates/witnessscripthash/input.js @@ -36,29 +36,4 @@ function check (chunks, allowIncomplete) { } check.toJSON = function () { return 'witnessScriptHash input' } -function encodeStack (witnessData, witnessScript) { - typeforce({ - witnessData: [types.Buffer], - witnessScript: types.Buffer - }, { - witnessData: witnessData, - witnessScript: witnessScript - }) - - return [].concat(witnessData, witnessScript) -} - -function decodeStack (stack) { - typeforce(typeforce.Array, stack) - typeforce(check, stack) - return { - witnessData: stack.slice(0, -1), - witnessScript: stack[stack.length - 1] - } -} - -module.exports = { - check: check, - decodeStack: decodeStack, - encodeStack: encodeStack -} +module.exports = { check } diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.js index d5b3a21..c9fc21a 100644 --- a/src/templates/witnessscripthash/output.js +++ b/src/templates/witnessscripthash/output.js @@ -1,8 +1,6 @@ // OP_0 {scriptHash} const bscript = require('../../script') -const types = require('../../types') -const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { @@ -14,20 +12,4 @@ function check (script) { } check.toJSON = function () { return 'Witness scriptHash output' } -function encode (scriptHash) { - typeforce(types.Hash256bit, scriptHash) - - return bscript.compile([OPS.OP_0, scriptHash]) -} - -function decode (buffer) { - typeforce(check, buffer) - - return buffer.slice(2) -} - -module.exports = { - check: check, - decode: decode, - encode: encode -} +module.exports = { check } From 4901d41fa94c8886d2c8dc743acf348743fe93cd Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 11 Jul 2018 16:19:32 +1000 Subject: [PATCH 067/568] tests/integration: change tests to not use templates --- test/integration/bip32.js | 10 ++--- test/integration/cltv.js | 68 ++++++++++++++++++++------------ test/integration/crypto.js | 13 ++---- test/integration/transactions.js | 2 +- 4 files changed, 51 insertions(+), 42 deletions(-) diff --git a/test/integration/bip32.js b/test/integration/bip32.js index e6d5721..3d2f12c 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -76,12 +76,10 @@ describe('bitcoinjs-lib (BIP32)', function () { const path = "m/49'/1'/0'/0/0" const child = root.derivePath(path) - const keyhash = bitcoin.crypto.hash160(child.publicKey) - const scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash) - const addressBytes = bitcoin.crypto.hash160(scriptSig) - const outputScript = bitcoin.script.scriptHash.output.encode(addressBytes) - const address = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.testnet) - + const { address } = bitcoin.payments.p2sh({ + redeem: bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network: bitcoin.networks.testnet }), + network: bitcoin.networks.testnet + }) assert.equal(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2') }) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index d508f74..626c7e9 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -46,8 +46,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // 3 hours ago const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) // fund the P2SH(CLTV) address regtestUtils.faucet(address, 1e5, function (err, unspent) { @@ -61,10 +60,15 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // {Alice's signature} OP_TRUE const tx = txb.buildIncomplete() const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ], redeemScript) + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]), + output: redeemScript + } + }).input tx.setInputScript(0, redeemScriptSig) regtestUtils.broadcast(tx.toHex(), function (err) { @@ -90,8 +94,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // 5 blocks from now const lockTime = bip65.encode({ blocks: height + 5 }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) // fund the P2SH(CLTV) address regtestUtils.faucet(address, 1e5, function (err, unspent) { @@ -105,10 +108,15 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // {Alice's signature} OP_TRUE const tx = txb.buildIncomplete() const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ], redeemScript) + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]), + output: redeemScript + } + }).input tx.setInputScript(0, redeemScriptSig) // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently @@ -139,8 +147,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // two hours ago const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) // fund the P2SH(CLTV) address regtestUtils.faucet(address, 2e5, function (err, unspent) { @@ -154,11 +161,16 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // {Alice's signature} {Bob's signature} OP_FALSE const tx = txb.buildIncomplete() const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_FALSE - ], redeemScript) + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_FALSE + ]), + output: redeemScript + } + }).input tx.setInputScript(0, redeemScriptSig) regtestUtils.broadcast(tx.toHex(), function (err) { @@ -181,8 +193,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // two hours from now const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) - const address = bitcoin.address.fromOutputScript(scriptPubKey, regtest) + const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) // fund the P2SH(CLTV) address regtestUtils.faucet(address, 2e4, function (err, unspent) { @@ -196,11 +207,16 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // {Alice's signature} OP_TRUE const tx = txb.buildIncomplete() const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.script.scriptHash.input.encode([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ], redeemScript) + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]), + output: redeemScript + } + }).input tx.setInputScript(0, redeemScriptSig) regtestUtils.broadcast(tx.toHex(), function (err) { diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 78a6ab0..611b306 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -18,16 +18,11 @@ describe('bitcoinjs-lib (crypto)', function () { const tx = bitcoin.Transaction.fromHex('01000000020b668015b32a6178d8524cfef6dc6fc0a4751915c2e9b2ed2d2eab02424341c8000000006a47304402205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022024bf5f506968f5f23f1835574d5afe0e9021b4a5b65cf9742332d5e4acb68f41012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffffa95fa69f11dc1cbb77ef64f25a95d4b12ebda57d19d843333819d95c9172ff89000000006b48304502205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022100832176b59e8f50c56631acbc824bcba936c9476c559c42a4468be98975d07562012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffff02b000eb04000000001976a91472956eed9a8ecb19ae7e3ebd7b06cae4668696a788ac303db000000000001976a9146c0bd55dd2592287cd9992ce3ba3fc1208fb76da88ac00000000') tx.ins.forEach(function (input, vin) { - const script = input.script - const scriptChunks = bitcoin.script.decompile(script) + const { output: prevOutput, pubkey, signature } = bitcoin.payments.p2pkh({ input: input.script }) - assert(bitcoin.script.pubKeyHash.input.check(scriptChunks), 'Expected pubKeyHash script') - const prevOutScript = bitcoin.address.toOutputScript('1ArJ9vRaQcoQ29mTWZH768AmRwzb6Zif1z') - const scriptSignature = bitcoin.script.signature.decode(scriptChunks[0]) - const publicKey = bitcoin.ECPair.fromPublicKey(scriptChunks[1]) - - const m = tx.hashForSignature(vin, prevOutScript, scriptSignature.hashType) - assert(publicKey.verify(m, scriptSignature.signature), 'Invalid m') + const scriptSignature = bitcoin.script.signature.decode(signature) + const m = tx.hashForSignature(vin, prevOutput, scriptSignature.hashType) + assert(bitcoin.ECPair.fromPublicKey(pubkey).verify(m, scriptSignature.signature), 'Invalid m') // store the required information input.signature = scriptSignature.signature diff --git a/test/integration/transactions.js b/test/integration/transactions.js index a4a1747..5bb1342 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -91,7 +91,7 @@ describe('bitcoinjs-lib (transactions)', function () { const txb = new bitcoin.TransactionBuilder(regtest) const data = Buffer.from('bitcoinjs-lib', 'utf8') - const dataScript = bitcoin.script.nullData.output.encode([data]) + const dataScript = require('../../src/templates/nulldata').output.encode([data]) txb.addInput(unspent.txId, unspent.vout) txb.addOutput(dataScript, 1000) From 35e0956ed9e93ee0767dd6c28993a7f164050cd9 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 3 Jul 2018 22:33:48 +1000 Subject: [PATCH 068/568] payments/p2ms: add const to p2ms --- src/payments/p2ms.js | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index a17d422..15de44e 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -1,11 +1,11 @@ -let lazy = require('./lazy') -let typef = require('typeforce') -let OPS = require('bitcoin-ops') -let ecc = require('tiny-secp256k1') +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') +const ecc = require('tiny-secp256k1') -let bscript = require('../script') -let BITCOIN_NETWORK = require('../networks').bitcoin -let OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin +const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 function stacksEqual (a, b) { if (a.length !== b.length) return false @@ -41,8 +41,8 @@ function p2ms (a, opts) { input: typef.maybe(typef.Buffer) }, a) - let network = a.network || BITCOIN_NETWORK - let o = { network } + const network = a.network || BITCOIN_NETWORK + const o = { network } let chunks let decoded = false @@ -50,10 +50,8 @@ function p2ms (a, opts) { if (decoded) return decoded = true chunks = bscript.decompile(output) - let om = chunks[0] - OP_INT_BASE - let on = chunks[chunks.length - 2] - OP_INT_BASE - o.m = om - o.n = on + o.m = chunks[0] - OP_INT_BASE + o.n = chunks[chunks.length - 2] - OP_INT_BASE o.pubkeys = chunks.slice(1, -2) } From 0d9619aeeddaf35c599db4719c2c468bebedce34 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sat, 14 Jul 2018 20:13:50 +1000 Subject: [PATCH 069/568] payments/p2wpkh: fix exception messages --- src/payments/p2wpkh.js | 6 +++--- test/fixtures/p2wpkh.json | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index bd010f7..b11a0bb 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -118,9 +118,9 @@ function p2wpkh (a, opts) { } if (a.witness) { - if (a.witness.length !== 2) throw new TypeError('Input is invalid') - if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Input has invalid signature') - if (!ecc.isPoint(a.witness[1])) throw new TypeError('Input has invalid pubkey') + if (a.witness.length !== 2) throw new TypeError('Witness is invalid') + if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Witness has invalid signature') + if (!ecc.isPoint(a.witness[1])) throw new TypeError('Witness has invalid pubkey') if (a.signature && !a.signature.equals(a.witness[0])) throw new TypeError('Signature mismatch') if (a.pubkey && !a.pubkey.equals(a.witness[1])) throw new TypeError('Pubkey mismatch') diff --git a/test/fixtures/p2wpkh.json b/test/fixtures/p2wpkh.json index 84e0c08..3a53525 100644 --- a/test/fixtures/p2wpkh.json +++ b/test/fixtures/p2wpkh.json @@ -148,7 +148,13 @@ } }, { - "exception": "Input has invalid signature", + "exception": "Witness is invalid", + "arguments": { + "witness": [] + } + }, + { + "exception": "Witness has invalid signature", "arguments": { "witness": [ "ffffffffffffffffff", @@ -157,7 +163,7 @@ } }, { - "exception": "Input has invalid pubkey", + "exception": "Witness has invalid pubkey", "arguments": { "witness": [ "300602010002010001", From 7104bb412ed4b765af892f789012410a234cbd7a Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sat, 14 Jul 2018 20:24:11 +1000 Subject: [PATCH 070/568] tests/payments: add missing tests --- src/payments/p2pkh.js | 4 ++-- src/payments/p2sh.js | 2 +- src/payments/p2wpkh.js | 22 +++++++++++----------- test/fixtures/p2pkh.json | 18 ++++++++++++++++++ test/fixtures/p2sh.json | 33 +++++++++++++++++++++++++++++++++ test/fixtures/p2wpkh.json | 19 +++++++++++++++++++ 6 files changed, 84 insertions(+), 14 deletions(-) diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index 140f707..08a4329 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -87,9 +87,9 @@ function p2pkh (a, opts) { if (opts.validate) { let hash if (a.address) { - if (_address().version !== network.pubKeyHash) throw new TypeError('Network mismatch') + if (_address().version !== network.pubKeyHash) throw new TypeError('Invalid version or Network mismatch') if (_address().hash.length !== 20) throw new TypeError('Invalid address') - else hash = _address().hash + hash = _address().hash } if (a.hash) { diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index 9b28a3a..7b95a45 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -109,7 +109,7 @@ function p2sh (a, opts) { if (opts.validate) { let hash if (a.address) { - if (_address().version !== network.scriptHash) throw new TypeError('Network mismatch') + if (_address().version !== network.scriptHash) throw new TypeError('Invalid version or Network mismatch') if (_address().hash.length !== 20) throw new TypeError('Invalid address') else hash = _address().hash } diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index b11a0bb..ba42ba1 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -90,17 +90,11 @@ function p2wpkh (a, opts) { if (opts.validate) { let hash if (a.address) { - if (network && network.bech32 !== _address().prefix) throw new TypeError('Network mismatch') - if (_address().version !== 0x00) throw new TypeError('Invalid version') - if (_address().data.length !== 20) throw new TypeError('Invalid data') - if (hash && !hash.equals(_address().data)) throw new TypeError('Hash mismatch') - else hash = _address().data - } - - if (a.pubkey) { - const pkh = bcrypto.hash160(a.pubkey) - if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') - else hash = pkh + if (network && network.bech32 !== _address().prefix) throw new TypeError('Invalid prefix or Network mismatch') + if (_address().version !== 0x00) throw new TypeError('Invalid address version') + if (_address().data.length !== 20) throw new TypeError('Invalid address data') + // if (hash && !hash.equals(_address().data)) throw new TypeError('Hash mismatch') + hash = _address().data } if (a.hash) { @@ -117,6 +111,12 @@ function p2wpkh (a, opts) { else hash = a.output.slice(2) } + if (a.pubkey) { + const pkh = bcrypto.hash160(a.pubkey) + if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + else hash = pkh + } + if (a.witness) { if (a.witness.length !== 2) throw new TypeError('Witness is invalid') if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Witness has invalid signature') diff --git a/test/fixtures/p2pkh.json b/test/fixtures/p2pkh.json index 7d47152..d16b181 100644 --- a/test/fixtures/p2pkh.json +++ b/test/fixtures/p2pkh.json @@ -121,6 +121,24 @@ "outputHex": "76a94c14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" } }, + { + "exception": "Invalid version or Network mismatch", + "arguments": { + "address": "3LRW7jeCvQCRdPF8S3yUCfRAx4eqXFmdcr" + } + }, + { + "exception": "Invalid address", + "arguments": { + "address": "111111111111111111117K4nzc" + } + }, + { + "exception": "Invalid address", + "arguments": { + "address": "111111111111111111111111133izVn" + } + }, { "exception": "Pubkey mismatch", "arguments": { diff --git a/test/fixtures/p2sh.json b/test/fixtures/p2sh.json index c87c8a8..44611a5 100644 --- a/test/fixtures/p2sh.json +++ b/test/fixtures/p2sh.json @@ -227,6 +227,24 @@ } } }, + { + "exception": "Invalid version or Network mismatch", + "arguments": { + "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh" + } + }, + { + "exception": "Invalid address", + "arguments": { + "address": "TYPjCiGbKiwP6r12cdkmVjySbQr7mb3r" + } + }, + { + "exception": "Invalid address", + "arguments": { + "address": "EDaBpuERpLssFzbCV1kgy8tKJsHrcwmzY7HDMF2" + } + }, { "exception": "Input too short", "arguments": { @@ -280,6 +298,21 @@ "inputHex": "021000" } }, + { + "exception": "Witness and redeem.witness mismatch", + "arguments": { + "witness": [ + "3045ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "redeem": { + "witness": [ + "3045dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "030000000000000000000000000000000000000000000000000000000000000001" + ] + } + } + }, { "exception": "Hash mismatch", "arguments": { diff --git a/test/fixtures/p2wpkh.json b/test/fixtures/p2wpkh.json index 3a53525..b0aa4ff 100644 --- a/test/fixtures/p2wpkh.json +++ b/test/fixtures/p2wpkh.json @@ -116,6 +116,18 @@ ] } }, + { + "exception": "Invalid prefix or Network mismatch", + "arguments": { + "address": "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7" + } + }, + { + "exception": "Invalid address version", + "arguments": { + "address": "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx" + } + }, { "exception": "Hash mismatch", "arguments": { @@ -137,6 +149,13 @@ "hash": "ffffffffffffffffffffffffffffffffffffffff" } }, + { + "exception": "Hash mismatch", + "arguments": { + "hash": "ffffffffffffffffffffffffffffffffffffffff", + "pubkey": "030000000000000000000000000000000000000000000000000000000000000001" + } + }, { "exception": "Hash mismatch", "arguments": { From 8aa4c8fdd44f3db4bb7ced71a9dd191322f7b452 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 3 Jul 2018 22:33:04 +1000 Subject: [PATCH 071/568] payments: prepare tests for null data --- test/payments.js | 5 +++-- test/payments.utils.js | 18 +++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/test/payments.js b/test/payments.js index 5722d88..6a4e733 100644 --- a/test/payments.js +++ b/test/payments.js @@ -9,14 +9,15 @@ const u = require('./payments.utils') const fixtures = require('./fixtures/' + p) fixtures.valid.forEach(function (f, i) { - const args = u.preform(f.arguments) - it(f.description + ' as expected', function () { + const args = u.preform(f.arguments) const actual = fn(args, f.options) + u.equate(actual, f.expected, f.arguments) }) it(f.description + ' as expected (no validation)', function () { + const args = u.preform(f.arguments) const actual = fn(args, Object.assign({}, f.options, { validate: false })) diff --git a/test/payments.utils.js b/test/payments.utils.js index 22001e9..2dbed3d 100644 --- a/test/payments.utils.js +++ b/test/payments.utils.js @@ -7,6 +7,12 @@ function tryHex (x) { if (Array.isArray(x)) return x.map(tryHex) return x } + +function fromHex (x) { + if (typeof x === 'string') return Buffer.from(x, 'hex') + if (Array.isArray(x)) return x.map(fromHex) + return x +} function tryASM (x) { if (Buffer.isBuffer(x)) return bscript.toASM(x) return x @@ -64,6 +70,7 @@ function equate (a, b, args) { if ('n' in b) t.strictEqual(a.n, b.n, 'Inequal *.n') if ('pubkeys' in b) t.deepEqual(tryHex(a.pubkeys), tryHex(b.pubkeys), 'Inequal *.pubkeys') if ('signatures' in b) t.deepEqual(tryHex(a.signatures), tryHex(b.signatures), 'Inequal *.signatures') + if ('data' in b) t.deepEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data') } function preform (x) { @@ -80,21 +87,18 @@ function preform (x) { } if (typeof x.output === 'string') x.output = asmToBuffer(x.output) if (typeof x.input === 'string') x.input = asmToBuffer(x.input) - if (Array.isArray(x.witness)) { - x.witness = x.witness.map(function (y) { - return Buffer.from(y, 'hex') - }) - } + if (Array.isArray(x.witness)) x.witness = x.witness.map(fromHex) + if (x.data) x.data = x.data.map(fromHex) if (x.hash) x.hash = Buffer.from(x.hash, 'hex') if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex') if (x.signature) x.signature = Buffer.from(x.signature, 'hex') - if (x.pubkeys) x.pubkeys = x.pubkeys.map(function (y) { return Buffer.from(y, 'hex') }) + if (x.pubkeys) x.pubkeys = x.pubkeys.map(fromHex) if (x.signatures) x.signatures = x.signatures.map(function (y) { return Number.isFinite(y) ? y : Buffer.from(y, 'hex') }) if (x.redeem) { if (typeof x.redeem.input === 'string') x.redeem.input = asmToBuffer(x.redeem.input) if (typeof x.redeem.output === 'string') x.redeem.output = asmToBuffer(x.redeem.output) - if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(function (y) { return Buffer.from(y, 'hex') }) + if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(fromHex) x.redeem.network = bnetworks[x.redeem.network] || x.network || bnetworks.bitcoin } From d886e76abd74c7133a2fab93426cfc93e87098a4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 3 Jul 2018 22:33:22 +1000 Subject: [PATCH 072/568] tests/payments: add null data tests --- test/fixtures/p2data.json | 63 +++++++++++++++++++++++++++++++++++++++ test/payments.js | 2 +- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/p2data.json diff --git a/test/fixtures/p2data.json b/test/fixtures/p2data.json new file mode 100644 index 0000000..b0486cd --- /dev/null +++ b/test/fixtures/p2data.json @@ -0,0 +1,63 @@ +{ + "valid": [ + { + "description": "output from output", + "arguments": { + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + }, + "expected": { + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ], + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4", + "input": null, + "witness": null + } + }, + { + "description": "output from data", + "arguments": { + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ] + }, + "expected": { + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ], + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4", + "input": null, + "witness": null + } + } + ], + "invalid": [ + { + "exception": "Not enough data", + "arguments": {} + } + ], + "dynamic": { + "depends": { + "data": [ "data", "output" ], + "output": [ "output", "data" ] + }, + "details": [ + { + "description": "p2data", + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ], + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" + }, + { + "description": "p2data", + "data": [ + "a3b147dbe4a85579fc4b5a1811e76620560e0726", + "7e62b9a0d6858f9127735cadd82f67e06c24dbc4" + ], + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e0726 7e62b9a0d6858f9127735cadd82f67e06c24dbc4" + } + ] + } +} diff --git a/test/payments.js b/test/payments.js index 6a4e733..3645ea7 100644 --- a/test/payments.js +++ b/test/payments.js @@ -3,7 +3,7 @@ const assert = require('assert') const u = require('./payments.utils') -;['p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { +;['p2data', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { describe(p, function () { const fn = require('../src/payments/' + p) const fixtures = require('./fixtures/' + p) From 44c13665c8f436c32d6a9036c585acfff75829fb Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 3 Jul 2018 22:33:35 +1000 Subject: [PATCH 073/568] payments: add p2data --- src/payments/index.js | 11 ++----- src/payments/p2data.js | 56 ++++++++++++++++++++++++++++++++ test/integration/transactions.js | 5 ++- 3 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 src/payments/p2data.js diff --git a/src/payments/index.js b/src/payments/index.js index 9e869f5..de3bec1 100644 --- a/src/payments/index.js +++ b/src/payments/index.js @@ -1,3 +1,4 @@ +const p2data = require('./p2data') const p2ms = require('./p2ms') const p2pk = require('./p2pk') const p2pkh = require('./p2pkh') @@ -5,15 +6,7 @@ const p2sh = require('./p2sh') const p2wpkh = require('./p2wpkh') const p2wsh = require('./p2wsh') -module.exports = { - p2ms: p2ms, - p2pk: p2pk, - p2pkh: p2pkh, - p2sh: p2sh, - p2wpkh: p2wpkh, - p2wsh: p2wsh -} +module.exports = { p2data, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } // TODO -// OP_RETURN // witness commitment diff --git a/src/payments/p2data.js b/src/payments/p2data.js new file mode 100644 index 0000000..c636c80 --- /dev/null +++ b/src/payments/p2data.js @@ -0,0 +1,56 @@ +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') + +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin + +function stacksEqual (a, b) { + if (a.length !== b.length) return false + + return a.every(function (x, i) { + return x.equals(b[i]) + }) +} + +// output: OP_RETURN ... +function p2data (a, opts) { + if ( + !a.data && + !a.output + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + typef({ + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + data: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a) + + const network = a.network || BITCOIN_NETWORK + const o = { network } + + lazy.prop(o, 'output', function () { + if (!a.data) return + return bscript.compile([OPS.OP_RETURN].concat(a.data)) + }) + lazy.prop(o, 'data', function () { + if (!a.output) return + return bscript.decompile(a.output).slice(1) + }) + + // extended validation + if (opts.validate) { + if (a.output) { + const chunks = bscript.decompile(a.output) + if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') + if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') + + if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch') + } + } + + return Object.assign(o, a) +} + +module.exports = p2data diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 5bb1342..cec0839 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -91,10 +91,9 @@ describe('bitcoinjs-lib (transactions)', function () { const txb = new bitcoin.TransactionBuilder(regtest) const data = Buffer.from('bitcoinjs-lib', 'utf8') - const dataScript = require('../../src/templates/nulldata').output.encode([data]) - + const p2data = bitcoin.payments.p2data({ data: [data] }) txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(dataScript, 1000) + txb.addOutput(p2data.output, 1000) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) txb.sign(0, keyPair) From de90fea0ac0e21c9a00b8bbfff2c334cc3521649 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 11 Jul 2018 17:27:44 +1000 Subject: [PATCH 074/568] payments: rename p2data to embed --- src/payments/embed.js | 56 +++++++++++++++++++++++ src/payments/index.js | 4 +- src/payments/p2data.js | 56 ----------------------- test/fixtures/{p2data.json => embed.json} | 4 +- test/integration/transactions.js | 4 +- test/payments.js | 2 +- 6 files changed, 63 insertions(+), 63 deletions(-) create mode 100644 src/payments/embed.js rename test/fixtures/{p2data.json => embed.json} (96%) diff --git a/src/payments/embed.js b/src/payments/embed.js new file mode 100644 index 0000000..c636c80 --- /dev/null +++ b/src/payments/embed.js @@ -0,0 +1,56 @@ +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') + +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin + +function stacksEqual (a, b) { + if (a.length !== b.length) return false + + return a.every(function (x, i) { + return x.equals(b[i]) + }) +} + +// output: OP_RETURN ... +function p2data (a, opts) { + if ( + !a.data && + !a.output + ) throw new TypeError('Not enough data') + opts = opts || { validate: true } + + typef({ + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + data: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a) + + const network = a.network || BITCOIN_NETWORK + const o = { network } + + lazy.prop(o, 'output', function () { + if (!a.data) return + return bscript.compile([OPS.OP_RETURN].concat(a.data)) + }) + lazy.prop(o, 'data', function () { + if (!a.output) return + return bscript.decompile(a.output).slice(1) + }) + + // extended validation + if (opts.validate) { + if (a.output) { + const chunks = bscript.decompile(a.output) + if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') + if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') + + if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch') + } + } + + return Object.assign(o, a) +} + +module.exports = p2data diff --git a/src/payments/index.js b/src/payments/index.js index de3bec1..d445466 100644 --- a/src/payments/index.js +++ b/src/payments/index.js @@ -1,4 +1,4 @@ -const p2data = require('./p2data') +const embed = require('./embed') const p2ms = require('./p2ms') const p2pk = require('./p2pk') const p2pkh = require('./p2pkh') @@ -6,7 +6,7 @@ const p2sh = require('./p2sh') const p2wpkh = require('./p2wpkh') const p2wsh = require('./p2wsh') -module.exports = { p2data, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } +module.exports = { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } // TODO // witness commitment diff --git a/src/payments/p2data.js b/src/payments/p2data.js index c636c80..e69de29 100644 --- a/src/payments/p2data.js +++ b/src/payments/p2data.js @@ -1,56 +0,0 @@ -const lazy = require('./lazy') -const typef = require('typeforce') -const OPS = require('bitcoin-ops') - -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin - -function stacksEqual (a, b) { - if (a.length !== b.length) return false - - return a.every(function (x, i) { - return x.equals(b[i]) - }) -} - -// output: OP_RETURN ... -function p2data (a, opts) { - if ( - !a.data && - !a.output - ) throw new TypeError('Not enough data') - opts = opts || { validate: true } - - typef({ - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - data: typef.maybe(typef.arrayOf(typef.Buffer)) - }, a) - - const network = a.network || BITCOIN_NETWORK - const o = { network } - - lazy.prop(o, 'output', function () { - if (!a.data) return - return bscript.compile([OPS.OP_RETURN].concat(a.data)) - }) - lazy.prop(o, 'data', function () { - if (!a.output) return - return bscript.decompile(a.output).slice(1) - }) - - // extended validation - if (opts.validate) { - if (a.output) { - const chunks = bscript.decompile(a.output) - if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') - if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') - - if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch') - } - } - - return Object.assign(o, a) -} - -module.exports = p2data diff --git a/test/fixtures/p2data.json b/test/fixtures/embed.json similarity index 96% rename from test/fixtures/p2data.json rename to test/fixtures/embed.json index b0486cd..ccc0e70 100644 --- a/test/fixtures/p2data.json +++ b/test/fixtures/embed.json @@ -44,14 +44,14 @@ }, "details": [ { - "description": "p2data", + "description": "embed", "data": [ "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" ], "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" }, { - "description": "p2data", + "description": "embed", "data": [ "a3b147dbe4a85579fc4b5a1811e76620560e0726", "7e62b9a0d6858f9127735cadd82f67e06c24dbc4" diff --git a/test/integration/transactions.js b/test/integration/transactions.js index cec0839..5358461 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -91,9 +91,9 @@ describe('bitcoinjs-lib (transactions)', function () { const txb = new bitcoin.TransactionBuilder(regtest) const data = Buffer.from('bitcoinjs-lib', 'utf8') - const p2data = bitcoin.payments.p2data({ data: [data] }) + const embed = bitcoin.payments.embed({ data: [data] }) txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(p2data.output, 1000) + txb.addOutput(embed.output, 1000) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) txb.sign(0, keyPair) diff --git a/test/payments.js b/test/payments.js index 3645ea7..3af6699 100644 --- a/test/payments.js +++ b/test/payments.js @@ -3,7 +3,7 @@ const assert = require('assert') const u = require('./payments.utils') -;['p2data', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { +;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { describe(p, function () { const fn = require('../src/payments/' + p) const fixtures = require('./fixtures/' + p) From 5481bde49c3e45c1a6302e94d3c7964bc04660b1 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 13 Jul 2018 13:09:41 +1000 Subject: [PATCH 075/568] templates: rm unused nulldata code --- src/templates/nulldata.js | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js index 2ad09d7..d42fd71 100644 --- a/src/templates/nulldata.js +++ b/src/templates/nulldata.js @@ -1,8 +1,6 @@ // OP_RETURN {data} const bscript = require('../script') -const types = require('../types') -const typeforce = require('typeforce') const OPS = require('bitcoin-ops') function check (script) { @@ -13,22 +11,4 @@ function check (script) { } check.toJSON = function () { return 'null data output' } -function encode (data) { - typeforce([types.Buffer], data) - - return bscript.compile([OPS.OP_RETURN].concat(data)) -} - -function decode (buffer) { - typeforce(check, buffer) - - return bscript.decompile(buffer).slice(1) -} - -module.exports = { - output: { - check: check, - decode: decode, - encode: encode - } -} +module.exports = { output: { check: check } } From 14d9636b6e6fe8131c3854c017ee14d2c3d73d8b Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 15:22:03 +1000 Subject: [PATCH 076/568] testing/p2wsh: add missing test case coverage --- src/payments/p2wsh.js | 6 +++--- test/fixtures/p2wpkh.json | 10 ++++++++-- test/fixtures/p2wsh.json | 39 +++++++++++++++++++++++++++++++++++++++ test/payments.utils.js | 2 +- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index e1adcc5..c84d822 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -119,9 +119,9 @@ function p2wsh (a, opts) { if (opts.validate) { let hash if (a.address) { - if (_address().prefix !== network.bech32) throw new TypeError('Network mismatch') - if (_address().version !== 0x00) throw new TypeError('Invalid version') - if (_address().data.length !== 32) throw new TypeError('Invalid data') + if (_address().prefix !== network.bech32) throw new TypeError('Invalid prefix or Network mismatch') + if (_address().version !== 0x00) throw new TypeError('Invalid address version') + if (_address().data.length !== 32) throw new TypeError('Invalid address data') else hash = _address().data } diff --git a/test/fixtures/p2wpkh.json b/test/fixtures/p2wpkh.json index b0aa4ff..7341907 100644 --- a/test/fixtures/p2wpkh.json +++ b/test/fixtures/p2wpkh.json @@ -119,13 +119,19 @@ { "exception": "Invalid prefix or Network mismatch", "arguments": { - "address": "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7" + "address": "foo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqs30dvv" } }, { "exception": "Invalid address version", "arguments": { - "address": "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx" + "address": "bc1pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5us4ke" + } + }, + { + "exception": "Invalid address data", + "arguments": { + "address": "bc1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmql8k8" } }, { diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json index c123d64..9579f64 100644 --- a/test/fixtures/p2wsh.json +++ b/test/fixtures/p2wsh.json @@ -268,6 +268,45 @@ "02ffff" ] } + }, + { + "exception": "Ambiguous witness source", + "arguments": { + "redeem": { + "output": "OP_TRUE", + "input": "01", + "witness": [ + "01" + ] + } + } + }, + { + "exception": "Network mismatch", + "arguments": { + "network": "bitcoin", + "redeem": { + "network": "testnet" + } + } + }, + { + "exception": "Invalid prefix or Network mismatch", + "arguments": { + "address": "foo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqs30dvv" + } + }, + { + "exception": "Invalid address version", + "arguments": { + "address": "bc1pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5us4ke" + } + }, + { + "exception": "Invalid address data", + "arguments": { + "address": "bc1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmql8k8" + } } ], "dynamic": { diff --git a/test/payments.utils.js b/test/payments.utils.js index 2dbed3d..6a7af32 100644 --- a/test/payments.utils.js +++ b/test/payments.utils.js @@ -99,7 +99,7 @@ function preform (x) { if (typeof x.redeem.input === 'string') x.redeem.input = asmToBuffer(x.redeem.input) if (typeof x.redeem.output === 'string') x.redeem.output = asmToBuffer(x.redeem.output) if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(fromHex) - x.redeem.network = bnetworks[x.redeem.network] || x.network || bnetworks.bitcoin + if (x.redeem.network) x.redeem.network = bnetworks[x.redeem.network] } return x From b9f2fdd00657e0f8a845ebd13d9ccc019975cb72 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 16:22:06 +1000 Subject: [PATCH 077/568] CHANGELOG: add v3.2.1, v3.3.0, v3.3.1, v3.3.2 --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bd846c..32fac0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +# 3.3.2 +__fixed__ +- Fixed `decodeStack` arbitrarily supporting non-Array arguments (#942) + +# 3.3.1 +__changed__ +- Increased the `TransactionBuilder` `maximumFeeRate` from 1000 to 2500 satoshis/byte. (#931) + +# 3.3.0 +__added__ +- Added `ECSignature.prototype.toRSBuffer`/`ECSignature.fromRSBuffer` (#915) +- Added support to `TransactionBuilder` for 64-byte signatures via `.sign` (#915) +- Added support to `TransactionBuilder` for the `.publicKey` standard as an alternative to `.getPublicKey()` (#915) + +# 3.2.1 +__fixed__ +- Fixed `script.scripthash.input.check` recursion (#898) +- Fixed `TransactionBuilder` sometimes ignoring witness value (#901) +- Fixed `script.witnessScriptHash.input` implementation (previously used the P2SH impl.) (#911) + # 3.2.0 __added__ - Added `address.fromBech32/toBech32` (#846) From 08a99c11330998f2b12424d3941decd006e362a4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 16:37:32 +1000 Subject: [PATCH 078/568] CHANGELOG: add 4.0.0 --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32fac0c..96d166d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ +# 4.0.0 +__added__ +- Added [`bip32`](https://github.com/bitcoinjs/bip32) dependency as a primary export (#1073) +- Added `ECPair.fromPrivateKey` (#1070) +- Added `payments` export, with support for `p2pkh`, `p2pk`, `p2ms`, `p2sh`, `p2wpkh`, `p2wsh` and `embed` payment types (#1096, #1119) +- Added `script.signature.encode/decode` for script signatures (#459) + +__changed__ +- `ECPair.prototype.sign` now returns a 64-byte signature `Buffer`, not an `ECSignature` object (#1084) +- `ECPair` (and all ECDSA code) now uses [`tiny-secp256k1`](http://github.com/bitcoinjs/tiny-secp256k1), which uses the [`libsecp256k1` library](https://github.com/bitcoin-core/secp256k1) (#1070) +- `TransactionBuilder` internal variables are now `__` prefixed to discourage public usage (#1038) +- `TransactionBuilder` now defaults to version 2 transaction versions (#1036) +- `script.decompile` now returns `Buffer` or `null`, if decompilation failed (#1039) + +__fixed__ +- Fixed `TransactionBuilder` rejecting uncompressed public keys to comply with BIP143 (#987) + +__removed__ +- Removed Node 4/5 LTS support (#1080) +- Removed `ECPair.fromPublicKeyBuffer`, use `ECPair.fromPublicKey` (#1070) +- Removed `ECPair.prototype.getAddress`, use `payments.p2pkh` instead (#1085) +- Removed `ECPair.prototype.getPrivateKey`, use `ECPair.prototype.privateKey` property (#1070) +- Removed `ECPair.prototype.getPublicKey`, use `ECPair.prototype.publicKey` property (#1070) +- Removed `ECSignature`, use `script.signature.encode/decode` instead (#459) +- Removed `HDNode`, use `bip32` export instead (#1073) +- Removed `bufferutils` (#1035) +- Removed `networks.litecoin`, BYO non-Bitcoin networks instead (#1095) +- Removed `script.isCanonicalSignature`, use `script.isCanonicalScriptSignature` instead (#1094) +- Removed `script.*.input/output/check` functions (`templates`) (previously added in #681, #682) (#1119) +- Removed dependency `bigi`, uses `bn.js` internally now (via `tiny-secp256k1`) (#1070, #1112) +- Removed public access to `ECPair` constructor, use exported functions `ECPair.fromPrivateKey`, `ECPair.fromWIF`, `ECPair.makeRandom`, or `ECPair.fromPublicKey` (#1070) + # 3.3.2 __fixed__ - Fixed `decodeStack` arbitrarily supporting non-Array arguments (#942) From a8a9b63d33155ad9aad96389a9077898039ee672 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 16:47:09 +1000 Subject: [PATCH 079/568] CHANGELOG: add missing getNetwork removal --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96d166d..80a0e2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ __removed__ - Removed `ECPair.prototype.getAddress`, use `payments.p2pkh` instead (#1085) - Removed `ECPair.prototype.getPrivateKey`, use `ECPair.prototype.privateKey` property (#1070) - Removed `ECPair.prototype.getPublicKey`, use `ECPair.prototype.publicKey` property (#1070) +- Removed `ECPair.prototype.getNetwork`, use `ECPair.prototype.network` property (#1070) - Removed `ECSignature`, use `script.signature.encode/decode` instead (#459) - Removed `HDNode`, use `bip32` export instead (#1073) - Removed `bufferutils` (#1035) From 7727a62ae6f7803eed440ce3190d1b3f5c7885c9 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 16:42:35 +1000 Subject: [PATCH 080/568] 4.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6947a2..d3ba738 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "3.3.2", + "version": "4.0.0", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "engines": { From 7484b84589a9f4c2904038cc7c0b6daa376f97ac Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 16:54:07 +1000 Subject: [PATCH 081/568] travis: rm Node 6 --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 94e6343..3c3eff4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,14 @@ sudo: false language: node_js node_js: - - "6" - - "7" - "8" - "9" - "10" matrix: include: - - node_js: "6" + - node_js: "8" env: TEST_SUITE=standard - - node_js: "6" + - node_js: "8" env: TEST_SUITE=coverage env: - TEST_SUITE=unit From 0bdf6beda15a435c11825a84bf57e30442ca6cb8 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 16:54:29 +1000 Subject: [PATCH 082/568] package: bump to Node 8 and above --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6947a2..b59c693 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "engines": { - "node": ">=4.0.0" + "node": ">=8.0.0" }, "keywords": [ "bitcoinjs", From 51792c3528914473560789286c2c858536028558 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 17:06:18 +1000 Subject: [PATCH 083/568] tests/integration: add bare witness output tests --- README.md | 1 + test/integration/transactions.js | 89 ++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/README.md b/README.md index 8025ea5..6deeb02 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). - [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L87) - [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L109) - [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L149) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2WPKH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L174) - [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L184) - [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L17) - [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L24) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 5358461..73b83c5 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -171,6 +171,95 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input (via a P2SH(P2WPKH) transaction)', function (done) { + this.timeout(30000) + + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) + const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) + + // prepare a P2SH(P2WPKH) faucet transaction, as Bitcoin-core doesn't support bare P2WPKH outputs (...yet) + const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }) + + regtestUtils.faucet(p2sh.address, 10e4, function (err, unspent) { + if (err) return done(err) + + const txvb = new bitcoin.TransactionBuilder(regtest) + txvb.addInput(unspent.txId, unspent.vout) + txvb.addOutput(p2wpkh.address, 6e4) // funds a P2WPKH address + txvb.sign(0, keyPair, p2sh.redeem.output, null, unspent.value) + const txv = txvb.build() + + // build and broadcast (the via transaction) to the Bitcoin RegTest network + regtestUtils.broadcast(txv.toHex(), function (err) { + if (err) return done(err) + + // XXX: build the Transaction w/ a P2WPKH input + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(txv.getId(), 0, null, p2wpkh.output) // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + txb.sign(0, keyPair, null, null, 6e4) // NOTE: no redeem script + const tx = txb.build() + + // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network + regtestUtils.broadcast(tx.toHex(), function (err) { + if (err) return done(err) + + regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4 + }, done) + }) + }) + }) + }) + + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input (via a P2SH(P2PK) transaction)', function (done) { + this.timeout(30000) + + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) + const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest }) + const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }) + + // prepare a P2SH(P2PK) faucet transaction, as Bitcoin-core doesn't support bare P2WSH outputs (...yet) + const p2sh = bitcoin.payments.p2sh({ redeem: p2pk, network: regtest }) + + regtestUtils.faucet(p2sh.address, 10e4, function (err, unspent) { + if (err) return done(err) + + const txvb = new bitcoin.TransactionBuilder(regtest) + txvb.addInput(unspent.txId, unspent.vout) + txvb.addOutput(p2wsh.address, 6e4) // funds a P2WPKH address + txvb.sign(0, keyPair, p2sh.redeem.output) + const txv = txvb.build() + + // build and broadcast (the via transaction) to the Bitcoin RegTest network + regtestUtils.broadcast(txv.toHex(), function (err) { + if (err) return done(err) + + // XXX: build the Transaction w/ a P2WSH input + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(txv.getId(), 0, null, p2wsh.output) // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + txb.sign(0, keyPair, null, null, 6e4, p2wsh.redeem.output) // NOTE: provide a witnessScript! + const tx = txb.build() + + // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network + regtestUtils.broadcast(tx.toHex(), function (err) { + if (err) return done(err) + + regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4 + }, done) + }) + }) + }) + }) + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', function (done) { this.timeout(50000) From 5e91c83e0a25e3c957c9e47072d431ed88962cc0 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 17:51:30 +1000 Subject: [PATCH 084/568] tests/fixtures: amend TxBuilder P2WPKH fixtures --- test/fixtures/transaction_builder.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 973f641..6896f30 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -394,8 +394,8 @@ ] }, { - "description": "Transaction w/ P2WPKH -> P2WPKH", - "txHex": "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff011027000000000000160014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c502483045022100b4a9d46ea4d38d6b3ea098911c9f72c0ae6ebc72408e6be7880a6b22a4b3e4da02207996107d0e6437f80363f96f502a38f275156f7501ea51f67899ba78a0c129c101210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", + "description": "P2WPKH -> P2WPKH", + "txHex": "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff011027000000000000160014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c502483045022100a8fc5e4c6d7073474eff2af5d756966e75be0cdfbba299518526080ce8b584be02200f26d41082764df89e3c815b8eaf51034a3b68a25f1be51208f54222c1bb6c1601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", "version": 1, "inputs": [ { @@ -834,7 +834,7 @@ }, { "description": "SIGHASH V0+V1, (P2PKH, P2WPKH) -> 2x P2PKH", - "txHex": "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000484730440220691a19d365c8d75f921346c70271506bde136f13a4b566dd796902c262e2ec6d02202b00c4aa030eedf294552bdfc163936d2f4e91c59e7798c4471250cf07cb859501eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff0230f45e13000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac00e9a435000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402200a7b08cccedf608e279410091acbd7e990e19a8edf401c3698763d2920de5871022060462ed172a02ecef73ebc19811d8fc72ed68f4419742df70241ad0a5a6a36410121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000", + "txHex": "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000484730440220691a19d365c8d75f921346c70271506bde136f13a4b566dd796902c262e2ec6d02202b00c4aa030eedf294552bdfc163936d2f4e91c59e7798c4471250cf07cb859501eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff0230f45e13000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac00e9a435000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac0002483045022100fddd014889f18d489b5400bfa8cb0a32301a768d934b1a0e2b55398119f26cab02207676c64c16ffa7ffaaf8e16b3b74e916687eebdfdb36b9b7997e838384d464640121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000", "version": 1, "inputs": [ { @@ -1314,7 +1314,7 @@ }, { "description": "P2WPKH -> P2PKH", - "txHex": "0100000000010133defbe3e28860007ff3e21222774c220cb35d554fa3e3796d25bf8ee983e1080000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac02483045022100834f56825e880ab7926164458e10582d9fd8df005396b7e51a1efb8db277204e02206a3610b7101c3242643ac9c9d3487c2d28ffdad19ec26a7f81fc100bdac625f10121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b00000000", + "txHex": "0100000000010133defbe3e28860007ff3e21222774c220cb35d554fa3e3796d25bf8ee983e1080000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac0248304502210097c3006f0b390982eb47f762b2853773c6cedf83668a22d710f4c13c4fd6b15502205e26ef16a81fc818a37f3a34fc6d0700e61100ea6c6773907c9c046042c440340121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b00000000", "version": 1, "inputs": [ { From 74756ed5975f8674d1e4d29c8669fc6571ed68af Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 17 Jul 2018 17:29:29 +1000 Subject: [PATCH 085/568] TransactionBuilder: fix P2WPKH not using a P2PKH signScript for P2WPKH --- src/transaction_builder.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 55c6e21..c4724d0 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -233,10 +233,10 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri } return { - redeemScript: redeemScript, + redeemScript, redeemScriptType: SCRIPT_TYPES.P2WSH, - witnessScript: witnessScript, + witnessScript, witnessScriptType: expanded.type, prevOutType: SCRIPT_TYPES.P2SH, @@ -274,14 +274,14 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri } return { - redeemScript: redeemScript, + redeemScript, redeemScriptType: expanded.type, prevOutType: SCRIPT_TYPES.P2SH, prevOutScript: p2sh.output, hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, - signScript: signScript, + signScript, signType: expanded.type, pubkeys: expanded.pubkeys, @@ -304,7 +304,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri } return { - witnessScript: witnessScript, + witnessScript, witnessScriptType: expanded.type, prevOutType: SCRIPT_TYPES.P2WSH, @@ -331,12 +331,17 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri expanded.signatures = input.signatures } + let signScript = input.prevOutScript + if (expanded.type === SCRIPT_TYPES.P2WPKH) { + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output + } + return { prevOutType: expanded.type, prevOutScript: input.prevOutScript, hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, - signScript: input.prevOutScript, + signScript, signType: expanded.type, pubkeys: expanded.pubkeys, From 936812451306f042784713e9b18a68d5728c70c5 Mon Sep 17 00:00:00 2001 From: Kyubum Choi <kyubum.choi@streami.co> Date: Wed, 18 Jul 2018 11:26:34 +0900 Subject: [PATCH 086/568] fix links of README.md --- README.md | 72 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 6deeb02..413bf5d 100644 --- a/README.md +++ b/README.md @@ -82,43 +82,45 @@ The below examples are implemented as integration tests, they should be very eas Otherwise, pull requests are appreciated. Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). -- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L30) -- [Generate an address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L37) -- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L48) -- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L55) -- [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L69) -- [Generate a SegWit P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L78) -- [Generate a SegWit 3-of-4 multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L88) -- [Generate a SegWit 2-of-2 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L103) -- [Support the retrieval of transactions for an address (3rd party blockchain)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L117) -- [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L136) -- [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L146) -- [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L21) -- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L36) -- [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L55) -- [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L87) -- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L109) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L149) +- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L22) +- [Generate an address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L29) +- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L40) +- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L47) +- [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L60) +- [Generate a SegWit P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L67) +- [Generate a SegWit 3-of-4 multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L76) +- [Generate a SegWit 2-of-2 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L90) +- [Support the retrieval of transactions for an address (3rd party blockchain)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L104) +- [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L123) +- [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L133) +- [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L13) +- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L28) +- [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L47) +- [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L83) +- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L105) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L143) - [Create (and broadcast via 3PBP) a Transaction with a SegWit P2WPKH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L174) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L184) -- [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L17) -- [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L24) -- [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L35) -- [Create a BIP32, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L44) -- [Create a BIP44, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L59) -- [Create a BIP49, bitcoin testnet, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L75) -- [Use BIP39 to generate BIP32 addresses](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L92) -- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L42) -- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L83) -- [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L135) -- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L177) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2PK input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L218) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L263) +- [Verify a Transaction signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L304) +- [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L12) +- [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L20) +- [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L31) +- [Create a BIP32, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L40) +- [Create a BIP44, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L55) +- [Create a BIP49, bitcoin testnet, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L71) +- [Use BIP39 to generate BIP32 addresses](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L86) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L43) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L88) +- [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L144) +- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L190) - [Recover a private key from duplicate R values](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L14) -- [Recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L73) -- [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L75) -- [Generate a single-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L94) -- [Recover parent recipient.d, if a derived private key is leaked (and nonce was revealed)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L110) -- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L127) -- [Generate a dual-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L150) +- [Recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L68) +- [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L72) +- [Generate a single-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L91) +- [Recover parent recipient.d, if a derived private key is leaked (and nonce was revealed)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L107) +- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L124) +- [Generate a dual-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L147) If you have a use case that you feel could be listed here, please [ask for it](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new)! From 732df833466e1094d9a0c436f1acd6b1fa4c972c Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 18 Jul 2018 14:00:53 +1000 Subject: [PATCH 087/568] tests/integration: simplify the bare witness examples --- test/integration/_regtest.js | 45 ++++++++++++---- test/integration/transactions.js | 92 +++++++++++--------------------- 2 files changed, 68 insertions(+), 69 deletions(-) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index f851d2c..0f39fad 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -4,6 +4,7 @@ const dhttp = require('dhttp/200') const APIPASS = process.env.APIPASS || 'satoshi' const APIURL = 'https://api.dcousens.cloud/1' +const NETWORK = bitcoin.networks.testnet function broadcast (txHex, callback) { dhttp({ @@ -42,6 +43,31 @@ function faucet (address, value, callback) { }) } +function faucetComplex (output, value, callback) { + const keyPair = bitcoin.ECPair.makeRandom({ network: NETWORK }) + const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: NETWORK }) + + faucet(p2pkh.address, value * 2, (err, unspent) => { + if (err) return callback(err) + + const txvb = new bitcoin.TransactionBuilder(NETWORK) + txvb.addInput(unspent.txId, unspent.vout, null, p2pkh.output) + txvb.addOutput(output, value) + txvb.sign(0, keyPair) + const txv = txvb.build() + + broadcast(txv.toHex(), function (err) { + if (err) return callback(err) + + return callback(null, { + txId: txv.getId(), + vout: 0, + value + }) + }) + }) +} + function fetch (txId, callback) { dhttp({ method: 'GET', @@ -78,14 +104,15 @@ function randomAddress () { } module.exports = { - broadcast: broadcast, - faucet: faucet, - fetch: fetch, - height: height, - mine: mine, - network: bitcoin.networks.testnet, - unspents: unspents, - verify: verify, - randomAddress: randomAddress, + broadcast, + faucet, + faucetComplex, + fetch, + height, + mine, + network: NETWORK, + unspents, + verify, + randomAddress, RANDOM_ADDRESS: randomAddress() } diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 73b83c5..ef95d53 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -171,91 +171,63 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input (via a P2SH(P2WPKH) transaction)', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', function (done) { this.timeout(30000) const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) - // prepare a P2SH(P2WPKH) faucet transaction, as Bitcoin-core doesn't support bare P2WPKH outputs (...yet) - const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }) - - regtestUtils.faucet(p2sh.address, 10e4, function (err, unspent) { + regtestUtils.faucetComplex(p2wpkh.address, 5e4, function (err, unspent) { if (err) return done(err) - const txvb = new bitcoin.TransactionBuilder(regtest) - txvb.addInput(unspent.txId, unspent.vout) - txvb.addOutput(p2wpkh.address, 6e4) // funds a P2WPKH address - txvb.sign(0, keyPair, p2sh.redeem.output, null, unspent.value) - const txv = txvb.build() + // XXX: build the Transaction w/ a P2WPKH input + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, null, p2wpkh.output) // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + txb.sign(0, keyPair, null, null, unspent.value) // NOTE: no redeem script + const tx = txb.build() - // build and broadcast (the via transaction) to the Bitcoin RegTest network - regtestUtils.broadcast(txv.toHex(), function (err) { + // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network + regtestUtils.broadcast(tx.toHex(), function (err) { if (err) return done(err) - // XXX: build the Transaction w/ a P2WPKH input - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(txv.getId(), 0, null, p2wpkh.output) // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - txb.sign(0, keyPair, null, null, 6e4) // NOTE: no redeem script - const tx = txb.build() - - // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) - - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 2e4 - }, done) - }) + regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4 + }, done) }) }) }) - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input (via a P2SH(P2PK) transaction)', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', function (done) { this.timeout(30000) const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest }) const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }) - // prepare a P2SH(P2PK) faucet transaction, as Bitcoin-core doesn't support bare P2WSH outputs (...yet) - const p2sh = bitcoin.payments.p2sh({ redeem: p2pk, network: regtest }) - - regtestUtils.faucet(p2sh.address, 10e4, function (err, unspent) { + regtestUtils.faucetComplex(p2wsh.address, 5e4, function (err, unspent) { if (err) return done(err) - const txvb = new bitcoin.TransactionBuilder(regtest) - txvb.addInput(unspent.txId, unspent.vout) - txvb.addOutput(p2wsh.address, 6e4) // funds a P2WPKH address - txvb.sign(0, keyPair, p2sh.redeem.output) - const txv = txvb.build() + // XXX: build the Transaction w/ a P2WSH input + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, null, p2wsh.output) // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + txb.sign(0, keyPair, null, null, 5e4, p2wsh.redeem.output) // NOTE: provide a witnessScript! + const tx = txb.build() - // build and broadcast (the via transaction) to the Bitcoin RegTest network - regtestUtils.broadcast(txv.toHex(), function (err) { + // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network + regtestUtils.broadcast(tx.toHex(), function (err) { if (err) return done(err) - // XXX: build the Transaction w/ a P2WSH input - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(txv.getId(), 0, null, p2wsh.output) // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - txb.sign(0, keyPair, null, null, 6e4, p2wsh.redeem.output) // NOTE: provide a witnessScript! - const tx = txb.build() - - // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) - - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 2e4 - }, done) - }) + regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4 + }, done) }) }) }) From b2d3a2cf3024364eb8e5645188658320e9914a62 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 20 Jul 2018 16:28:14 +1000 Subject: [PATCH 088/568] testing: add payments tests for each standard payment type --- test/integration/payments.js | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 test/integration/payments.js diff --git a/test/integration/payments.js b/test/integration/payments.js new file mode 100644 index 0000000..0a164f9 --- /dev/null +++ b/test/integration/payments.js @@ -0,0 +1,71 @@ +/* global describe, it */ + +const bitcoin = require('../../') + +const regtestUtils = require('./_regtest') +const NETWORK = regtestUtils.network +const keyPairs = [ + bitcoin.ECPair.makeRandom({ network: NETWORK }), + bitcoin.ECPair.makeRandom({ network: NETWORK }) +] + +function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) { + regtestUtils.faucetComplex(prevOutput, 5e4, (err, unspent) => { + if (err) return done(err) + + const txb = new bitcoin.TransactionBuilder(NETWORK) + txb.addInput(unspent.txId, unspent.vout, null, prevOutput) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + + if (depends.signatures) { + keyPairs.forEach((keyPair) => { + txb.sign(0, keyPair, redeemScript, null, unspent.value, witnessScript) + }) + } else if (depends.signature) { + txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript) + } + + regtestUtils.broadcast(txb.build().toHex(), done) + }) +} + +;['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach((k) => { + const fixtures = require('../fixtures/' + k) + const { depends } = fixtures.dynamic + const fn = bitcoin.payments[k] + + const base = {} + if (depends.pubkey) base.pubkey = keyPairs[0].publicKey + if (depends.pubkeys) base.pubkeys = keyPairs.map(x => x.publicKey) + if (depends.m) base.m = base.pubkeys.length + + const { output } = fn(base) + if (!output) throw new TypeError('Missing output') + + describe('bitcoinjs-lib (payments - ' + k + ')', () => { + it('can broadcast as an output, and be spent as an input', (done) => { + buildAndSign(depends, output, null, null, done) + }) + + it('can (as P2SH(' + k + ')) broadcast as an output, and be spent as an input', (done) => { + const p2sh = bitcoin.payments.p2sh({ redeem: { output }, network: NETWORK }) + buildAndSign(depends, p2sh.output, p2sh.redeem.output, null, done) + }) + + it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', (done) => { + if (k === 'p2wpkh') return done() // skip P2WSH(P2WPKH) + + const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) + buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output, done) + }) + + it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', (done) => { + if (k === 'p2wpkh') return done() // skip P2SH(P2WSH(P2WPKH)) + + const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) + const p2sh = bitcoin.payments.p2sh({ redeem: { output: p2wsh.output }, network: NETWORK }) + + buildAndSign(depends, p2sh.output, p2sh.redeem.output, p2wsh.redeem.output, done) + }) + }) +}) From faf3645361bb468f4544725911278aee46655937 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 20 Jul 2018 17:02:27 +1000 Subject: [PATCH 089/568] tests/integration: allow more time --- test/integration/payments.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/integration/payments.js b/test/integration/payments.js index 0a164f9..d657f3c 100644 --- a/test/integration/payments.js +++ b/test/integration/payments.js @@ -42,7 +42,9 @@ function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) { const { output } = fn(base) if (!output) throw new TypeError('Missing output') - describe('bitcoinjs-lib (payments - ' + k + ')', () => { + describe('bitcoinjs-lib (payments - ' + k + ')', function () { + this.timeout(30000) + it('can broadcast as an output, and be spent as an input', (done) => { buildAndSign(depends, output, null, null, done) }) From de0259a820cb759c0147da85dc6be79eafaa4830 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 20 Jul 2018 17:30:18 +1000 Subject: [PATCH 090/568] tests/integration/payments: enable failing P2SH(P2WSH(P2WPKH)) tests --- src/transaction_builder.js | 14 ++++++++++++-- test/integration/payments.js | 4 ---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index c4724d0..4545107 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -232,6 +232,11 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri expanded.signatures = input.signatures } + let signScript = witnessScript + if (expanded.type === SCRIPT_TYPES.P2WPKH) { + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output + } + return { redeemScript, redeemScriptType: SCRIPT_TYPES.P2WSH, @@ -243,7 +248,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri prevOutScript: p2sh.output, hasWitness: true, - signScript: witnessScript, + signScript, signType: expanded.type, pubkeys: expanded.pubkeys, @@ -303,6 +308,11 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri expanded.signatures = input.signatures } + let signScript = witnessScript + if (expanded.type === SCRIPT_TYPES.P2WPKH) { + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output + } + return { witnessScript, witnessScriptType: expanded.type, @@ -311,7 +321,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri prevOutScript: p2wsh.output, hasWitness: true, - signScript: witnessScript, + signScript, signType: expanded.type, pubkeys: expanded.pubkeys, diff --git a/test/integration/payments.js b/test/integration/payments.js index d657f3c..c996f55 100644 --- a/test/integration/payments.js +++ b/test/integration/payments.js @@ -55,15 +55,11 @@ function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) { }) it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', (done) => { - if (k === 'p2wpkh') return done() // skip P2WSH(P2WPKH) - const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output, done) }) it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', (done) => { - if (k === 'p2wpkh') return done() // skip P2SH(P2WSH(P2WPKH)) - const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) const p2sh = bitcoin.payments.p2sh({ redeem: { output: p2wsh.output }, network: NETWORK }) From 31ab9bfc03b26fe73454d8c3b2b58cd23caee629 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 20 Jul 2018 17:35:34 +1000 Subject: [PATCH 091/568] tests/integration: throw verbose error if unspent is missing --- test/integration/_regtest.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 0f39fad..059cec2 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -38,7 +38,10 @@ function faucet (address, value, callback) { unspents(address, function (err, results) { if (err) return callback(err) - callback(null, results.filter(x => x.txId === txId).pop()) + const unspents = results.filter(x => x.txId === txId) + if (unspents.length === 0) return callback(new Error('Missing unspent')) + + callback(null, unspents.pop()) }) }) } From ca4c9ca64cf878fb703a0248e226c0076a06c654 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 23 Jul 2018 10:37:31 +1000 Subject: [PATCH 092/568] add note about P2WPKH in P2WSH as a consensus fail --- test/integration/payments.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/integration/payments.js b/test/integration/payments.js index c996f55..d319f60 100644 --- a/test/integration/payments.js +++ b/test/integration/payments.js @@ -54,6 +54,9 @@ function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) { buildAndSign(depends, p2sh.output, p2sh.redeem.output, null, done) }) + // NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail + if (k === 'p2wpkh') return + it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', (done) => { const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output, done) From 079d83d887ef56ca4902f151a4c90ed01902623c Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 23 Jul 2018 10:41:01 +1000 Subject: [PATCH 093/568] txbuilder: note consensus issue --- src/transaction_builder.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 4545107..8706841 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -233,9 +233,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri } let signScript = witnessScript - if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output - } + if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure') return { redeemScript, @@ -309,9 +307,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri } let signScript = witnessScript - if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output - } + if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2WSH(P2WPKH) is a consensus failure') return { witnessScript, From d9e8a13cdece685db6b115349e7c6fb0248fe568 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 23 Jul 2018 15:53:08 +1000 Subject: [PATCH 094/568] bump dependencies to 1.0.0 versions --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index beae8cb..7c594b9 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ ], "dependencies": { "bech32": "^1.1.2", - "bip32": "^0.1.0", + "bip32": "^1.0.0", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", "bs58check": "^2.0.0", @@ -41,7 +41,7 @@ "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", "safe-buffer": "^5.1.1", - "tiny-secp256k1": "^0.2.2", + "tiny-secp256k1": "^1.0.0", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" From ffa5ad7c2833c5e5b8d50491f87d3432c86bea9f Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 23 Jul 2018 16:24:15 +1000 Subject: [PATCH 095/568] bump .travis to use the LTS notation --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3c3eff4..397dc23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,14 @@ sudo: false language: node_js node_js: - - "8" + - "lts/*" - "9" - "10" matrix: include: - - node_js: "8" + - node_js: "lts/*" env: TEST_SUITE=standard - - node_js: "8" + - node_js: "lts/*" env: TEST_SUITE=coverage env: - TEST_SUITE=unit From 94f0ae85e3fd9323cc6cdbfb719a161f00d238b4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 23 Jul 2018 16:54:10 +1000 Subject: [PATCH 096/568] tests/integration: fix ecurve import, as removed --- test/integration/crypto.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 611b306..864f4e7 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -7,9 +7,6 @@ const bip32 = require('bip32') const crypto = require('crypto') const tinysecp = require('tiny-secp256k1') -const ecurve = require('ecurve') -const secp256k1 = ecurve.getCurveByName('secp256k1') - describe('bitcoinjs-lib (crypto)', function () { it('can recover a private key from duplicate R values', function () { this.timeout(30000) @@ -29,8 +26,7 @@ describe('bitcoinjs-lib (crypto)', function () { input.z = new BN(m) }) - // finally, run the tasks, then on to the math - const n = new BN(secp256k1.n.toString()) + const n = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16) for (var i = 0; i < tx.ins.length; ++i) { for (var j = i + 1; j < tx.ins.length; ++j) { From 29fc0676960c46c9bd091fa306cfc401c8b8629e Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 23 Jul 2018 17:07:15 +1000 Subject: [PATCH 097/568] CHANGELOG: add 4.0.1 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80a0e2f..10d3eb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 4.0.1 +__fixed__ +- Fixed `tiny-secp256k1` dependency version (used `ecurve`) (#1139) +- Fixed `TransactionBuilder` throwing when trying to sign `P2WSH(P2WPKH)` (#1135) + # 4.0.0 __added__ - Added [`bip32`](https://github.com/bitcoinjs/bip32) dependency as a primary export (#1073) From 3bb3b9f3972a22ddb165c517b6b3cd7719855b21 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 23 Jul 2018 17:05:06 +1000 Subject: [PATCH 098/568] 4.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c594b9..b9be434 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "4.0.0", + "version": "4.0.1", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "engines": { From 16195cc6ce8144beb90b1c210d37bbce357a5bdb Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 23 Jul 2018 17:45:01 +1000 Subject: [PATCH 099/568] explicitly import mocha variables --- test/address.js | 3 +-- test/bitcoin.core.js | 3 +-- test/block.js | 3 +-- test/bufferutils.js | 3 +-- test/classify.js | 3 +-- test/crypto.js | 3 +-- test/ecpair.js | 2 +- test/integration/addresses.js | 3 +-- test/integration/bip32.js | 3 +-- test/integration/blocks.js | 2 +- test/integration/cltv.js | 3 +-- test/integration/crypto.js | 3 +-- test/integration/csv.js | 3 +-- test/integration/payments.js | 3 +-- test/integration/stealth.js | 3 +-- test/integration/transactions.js | 3 +-- test/payments.js | 3 +-- test/payments.utils.js | 6 +++--- test/script.js | 3 +-- test/script_number.js | 3 +-- test/script_signature.js | 3 +-- test/transaction.js | 3 +-- test/transaction_builder.js | 3 +-- test/types.js | 3 +-- 24 files changed, 26 insertions(+), 47 deletions(-) diff --git a/test/address.js b/test/address.js index 99955e7..a0f4df0 100644 --- a/test/address.js +++ b/test/address.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const baddress = require('../src/address') const bscript = require('../src/script') diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index f0aecf7..560bf20 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const base58 = require('bs58') const bitcoin = require('../') diff --git a/test/block.js b/test/block.js index 1da9de3..d9ff405 100644 --- a/test/block.js +++ b/test/block.js @@ -1,5 +1,4 @@ -/* global describe, it, beforeEach */ - +const { describe, it, beforeEach } = require('mocha') const assert = require('assert') const Block = require('../src/block') diff --git a/test/bufferutils.js b/test/bufferutils.js index aa044af..5f2c39e 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const bufferutils = require('../src/bufferutils') diff --git a/test/classify.js b/test/classify.js index f56647e..3efcc74 100644 --- a/test/classify.js +++ b/test/classify.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const bscript = require('../src/script') const classify = require('../src/classify') diff --git a/test/crypto.js b/test/crypto.js index 18f2a37..3f7802a 100644 --- a/test/crypto.js +++ b/test/crypto.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const bcrypto = require('../src/crypto') diff --git a/test/ecpair.js b/test/ecpair.js index fc215f1..3b5857f 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -1,6 +1,6 @@ -/* global describe, it, beforeEach */ /* eslint-disable no-new */ +const { describe, it, beforeEach } = require('mocha') const assert = require('assert') const proxyquire = require('proxyquire') const hoodwink = require('hoodwink') diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 412e792..4bd71c8 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const bitcoin = require('../../') const dhttp = require('dhttp/200') diff --git a/test/integration/bip32.js b/test/integration/bip32.js index 3d2f12c..27a2da4 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const bip32 = require('bip32') const bip39 = require('bip39') diff --git a/test/integration/blocks.js b/test/integration/blocks.js index a0bdea3..915fd65 100644 --- a/test/integration/blocks.js +++ b/test/integration/blocks.js @@ -1,6 +1,6 @@ -/* global describe, it */ 'use strict' +const { describe, it } = require('mocha') const assert = require('assert') const bitcoin = require('../../') diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 626c7e9..edd4b61 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -1,5 +1,4 @@ -/* global describe, it, before */ - +const { describe, it, before } = require('mocha') const assert = require('assert') const bitcoin = require('../../') const regtestUtils = require('./_regtest') diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 864f4e7..bc4a9bc 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const BN = require('bn.js') const bitcoin = require('../../') diff --git a/test/integration/csv.js b/test/integration/csv.js index e0ad02f..f0db2e4 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -1,5 +1,4 @@ -/* global describe, it, before */ - +const { describe, it, before } = require('mocha') const assert = require('assert') const bitcoin = require('../../') const regtestUtils = require('./_regtest') diff --git a/test/integration/payments.js b/test/integration/payments.js index d319f60..44acea2 100644 --- a/test/integration/payments.js +++ b/test/integration/payments.js @@ -1,7 +1,6 @@ -/* global describe, it */ - const bitcoin = require('../../') +const { describe, it } = require('mocha') const regtestUtils = require('./_regtest') const NETWORK = regtestUtils.network const keyPairs = [ diff --git a/test/integration/stealth.js b/test/integration/stealth.js index 813f48b..dd99d63 100644 --- a/test/integration/stealth.js +++ b/test/integration/stealth.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const bitcoin = require('../../') const ecc = require('tiny-secp256k1') diff --git a/test/integration/transactions.js b/test/integration/transactions.js index ef95d53..4d344a5 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const bitcoin = require('../../') const regtestUtils = require('./_regtest') diff --git a/test/payments.js b/test/payments.js index 3af6699..3c07bf3 100644 --- a/test/payments.js +++ b/test/payments.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const u = require('./payments.utils') diff --git a/test/payments.utils.js b/test/payments.utils.js index 6a7af32..219d086 100644 --- a/test/payments.utils.js +++ b/test/payments.utils.js @@ -1,6 +1,6 @@ -let t = require('assert') -let bscript = require('../src/script') -let bnetworks = require('../src/networks') +const t = require('assert') +const bscript = require('../src/script') +const bnetworks = require('../src/networks') function tryHex (x) { if (Buffer.isBuffer(x)) return x.toString('hex') diff --git a/test/script.js b/test/script.js index 269e18a..c2a60ad 100644 --- a/test/script.js +++ b/test/script.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const bscript = require('../src/script') const minimalData = require('minimaldata') diff --git a/test/script_number.js b/test/script_number.js index d217ff1..bc8f395 100644 --- a/test/script_number.js +++ b/test/script_number.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const scriptNumber = require('../src/script_number') const fixtures = require('./fixtures/script_number.json') diff --git a/test/script_signature.js b/test/script_signature.js index 9908ebc..cee69bd 100644 --- a/test/script_signature.js +++ b/test/script_signature.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const bscriptSig = require('../src/script').signature const Buffer = require('safe-buffer').Buffer diff --git a/test/transaction.js b/test/transaction.js index e249631..f8b7de9 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -1,5 +1,4 @@ -/* global describe, it, beforeEach */ - +const { describe, it, beforeEach } = require('mocha') const assert = require('assert') const bscript = require('../src/script') const fixtures = require('./fixtures/transaction') diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 34ec9da..88ab286 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -1,5 +1,4 @@ -/* global describe, it, beforeEach */ - +const { describe, it, beforeEach } = require('mocha') const assert = require('assert') const baddress = require('../src/address') const bcrypto = require('../src/crypto') diff --git a/test/types.js b/test/types.js index adc6f35..d245d53 100644 --- a/test/types.js +++ b/test/types.js @@ -1,5 +1,4 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') const types = require('../src/types') const typeforce = require('typeforce') From 85b1b92b6d3404d7477e9a485965fb9472c93e91 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 26 Jul 2018 17:35:31 +1000 Subject: [PATCH 100/568] tests/ECPair: test fromPublic/fromPrivate in isolation --- src/ecpair.js | 5 +--- test/ecpair.js | 58 ++++++++++++++++++++------------------- test/fixtures/ecpair.json | 23 ++++++++++++++-- 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index 34c7120..0e2cf26 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -5,9 +5,6 @@ const types = require('./types') const wif = require('wif') const NETWORKS = require('./networks') - -// TODO: why is the function name toJSON weird? -function isPoint (x) { return ecc.isPoint(x) } const isOptions = typeforce.maybe(typeforce.compile({ compressed: types.maybe(types.Boolean), network: types.maybe(types.Network) @@ -57,7 +54,7 @@ function fromPrivateKey (buffer, options) { } function fromPublicKey (buffer, options) { - typeforce(isPoint, buffer) + typeforce(ecc.isPoint, buffer) typeforce(isOptions, options) return new ECPair(null, buffer, options) } diff --git a/test/ecpair.js b/test/ecpair.js index fc215f1..8a246f1 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -22,7 +22,23 @@ const GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03 const GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 'hex') describe('ECPair', function () { - describe('constructor', function () { + describe('getPublicKey', function () { + let keyPair + + beforeEach(function () { + keyPair = ECPair.fromPrivateKey(ONE) + }) + + it('calls pointFromScalar lazily', hoodwink(function () { + assert.strictEqual(keyPair.__Q, null) + + // .publicKey forces the memoization + assert.strictEqual(keyPair.publicKey.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') + assert.strictEqual(keyPair.__Q.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') + })) + }) + + describe('fromPrivateKey', function () { it('defaults to compressed', function () { const keyPair = ECPair.fromPrivateKey(ONE) @@ -49,8 +65,6 @@ describe('ECPair', function () { fixtures.valid.forEach(function (f) { it('derives public key for ' + f.WIF, function () { const d = Buffer.from(f.d, 'hex') - console.log(d) - const keyPair = ECPair.fromPrivateKey(d, { compressed: f.compressed }) @@ -59,37 +73,25 @@ describe('ECPair', function () { }) }) - fixtures.invalid.constructor.forEach(function (f) { + fixtures.invalid.fromPrivateKey.forEach(function (f) { it('throws ' + f.exception, function () { - if (f.d) { - const d = Buffer.from(f.d, 'hex') - assert.throws(function () { - ECPair.fromPrivateKey(d, f.options) - }, new RegExp(f.exception)) - } else { - const Q = Buffer.from(f.Q, 'hex') - assert.throws(function () { - ECPair.fromPublicKey(Q, f.options) - }, new RegExp(f.exception)) - } + const d = Buffer.from(f.d, 'hex') + assert.throws(function () { + ECPair.fromPrivateKey(d, f.options) + }, new RegExp(f.exception)) }) }) }) - describe('getPublicKey', function () { - let keyPair - - beforeEach(function () { - keyPair = ECPair.fromPrivateKey(ONE) + describe('fromPublicKey', function () { + fixtures.invalid.fromPublicKey.forEach(function (f) { + it('throws ' + f.exception, function () { + const Q = Buffer.from(f.Q, 'hex') + assert.throws(function () { + ECPair.fromPublicKey(Q, f.options) + }, new RegExp(f.exception)) + }) }) - - it('calls pointFromScalar lazily', hoodwink(function () { - assert.strictEqual(keyPair.__Q, null) - - // .publicKey forces the memoization - assert.strictEqual(keyPair.publicKey.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') - assert.strictEqual(keyPair.__Q.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') - })) }) describe('fromWIF', function () { diff --git a/test/fixtures/ecpair.json b/test/fixtures/ecpair.json index 77eee08..cd83544 100644 --- a/test/fixtures/ecpair.json +++ b/test/fixtures/ecpair.json @@ -66,7 +66,7 @@ } ], "invalid": { - "constructor": [ + "fromPrivateKey": [ { "exception": "Private key not in range \\[1, n\\)", "d": "0000000000000000000000000000000000000000000000000000000000000000" @@ -93,7 +93,26 @@ "network": {} } } - + ], + "fromPublicKey": [ + { + "exception": "Expected isPoint, got Buffer", + "Q": "", + "options": {} + }, + { + "exception": "Expected property \"network.messagePrefix\" of type Buffer|String, got undefined", + "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", + "options": { + "network": {} + } + }, + { + "description": "Bad X coordinate (== P)", + "exception": "Expected isPoint, got Buffer", + "Q": "040000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "options": {} + } ], "fromWIF": [ { From cf9a35f59bc6346bf4c6bee9466c58683fd55f2b Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 27 Jul 2018 20:14:11 +1000 Subject: [PATCH 101/568] payments: more const --- src/payments/p2pk.js | 18 +++++++++--------- src/payments/p2pkh.js | 11 ++++++----- src/payments/p2sh.js | 3 ++- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index 9cddc81..9e12749 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -1,10 +1,10 @@ -let lazy = require('./lazy') -let typef = require('typeforce') -let OPS = require('bitcoin-ops') -let ecc = require('tiny-secp256k1') +const lazy = require('./lazy') +const typef = require('typeforce') +const OPS = require('bitcoin-ops') +const ecc = require('tiny-secp256k1') -let bscript = require('../script') -let BITCOIN_NETWORK = require('../networks').bitcoin +const bscript = require('../script') +const BITCOIN_NETWORK = require('../networks').bitcoin // input: {signature} // output: {pubKey} OP_CHECKSIG @@ -27,10 +27,10 @@ function p2pk (a, opts) { input: typef.maybe(typef.Buffer) }, a) - let _chunks = lazy.value(function () { return bscript.decompile(a.input) }) + const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) - let network = a.network || BITCOIN_NETWORK - let o = { network } + const network = a.network || BITCOIN_NETWORK + const o = { network } lazy.prop(o, 'output', function () { if (!a.pubkey) return diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index 08a4329..0ab9fa0 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -106,18 +106,19 @@ function p2pkh (a, opts) { a.output[23] !== OPS.OP_EQUALVERIFY || a.output[24] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') - if (hash && !hash.equals(a.output.slice(3, 23))) throw new TypeError('Hash mismatch') - else hash = a.output.slice(3, 23) + const hash2 = a.output.slice(3, 23) + if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + else hash = hash2 } if (a.pubkey) { - let pkh = bcrypto.hash160(a.pubkey) + const pkh = bcrypto.hash160(a.pubkey) if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') else hash = pkh } if (a.input) { - let chunks = _chunks() + const chunks = _chunks() if (chunks.length !== 2) throw new TypeError('Input is invalid') if (!bscript.isCanonicalScriptSignature(chunks[0])) throw new TypeError('Input has invalid signature') if (!ecc.isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey') @@ -125,7 +126,7 @@ function p2pkh (a, opts) { if (a.signature && !a.signature.equals(chunks[0])) throw new TypeError('Signature mismatch') if (a.pubkey && !a.pubkey.equals(chunks[1])) throw new TypeError('Pubkey mismatch') - let pkh = bcrypto.hash160(chunks[1]) + const pkh = bcrypto.hash160(chunks[1]) if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') } } diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index 7b95a45..9c80b09 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -111,7 +111,7 @@ function p2sh (a, opts) { if (a.address) { if (_address().version !== network.scriptHash) throw new TypeError('Invalid version or Network mismatch') if (_address().hash.length !== 20) throw new TypeError('Invalid address') - else hash = _address().hash + hash = _address().hash } if (a.hash) { @@ -125,6 +125,7 @@ function p2sh (a, opts) { a.output[0] !== OPS.OP_HASH160 || a.output[1] !== 0x14 || a.output[22] !== OPS.OP_EQUAL) throw new TypeError('Output is invalid') + const hash2 = a.output.slice(2, 22) if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') else hash = hash2 From 54ec449a751faa55d607d70d02904ad27d6120c0 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 27 Jul 2018 20:20:28 +1000 Subject: [PATCH 102/568] payments/p2sh: try not to rely on o. in validation --- src/payments/p2sh.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index 9c80b09..b642154 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -58,7 +58,7 @@ function p2sh (a, opts) { const _redeem = lazy.value(function () { const chunks = _chunks() return { - network: network, + network, output: chunks[chunks.length - 1], input: bscript.compile(chunks.slice(0, -1)), witness: a.witness || [] @@ -166,9 +166,10 @@ function p2sh (a, opts) { if (a.redeem) { if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') - if (o.redeem) { - if (a.redeem.output && !a.redeem.output.equals(o.redeem.output)) throw new TypeError('Redeem.output mismatch') - if (a.redeem.input && !a.redeem.input.equals(o.redeem.input)) throw new TypeError('Redeem.input mismatch') + if (a.input) { + const redeem = _redeem() + if (a.redeem.output && !a.redeem.output.equals(redeem.output)) throw new TypeError('Redeem.output mismatch') + if (a.redeem.input && !a.redeem.input.equals(redeem.input)) throw new TypeError('Redeem.input mismatch') } checkRedeem(a.redeem) From 3ed77c4820d3e5ddf4bb38fe06da6d63ea67b22e Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 27 Jul 2018 20:22:11 +1000 Subject: [PATCH 103/568] payments: cleanup --- src/payments/p2pk.js | 7 ++----- src/payments/p2wpkh.js | 1 - src/payments/p2wsh.js | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index 9e12749..5f65030 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -58,13 +58,10 @@ function p2pk (a, opts) { // extended validation if (opts.validate) { - if (a.pubkey && a.output) { - if (!a.pubkey.equals(o.pubkey)) throw new TypeError('Pubkey mismatch') - } - if (a.output) { if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') if (!ecc.isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid') + if (a.pubkey && !a.pubkey.equals(o.pubkey)) throw new TypeError('Pubkey mismatch') } if (a.signature) { @@ -73,7 +70,7 @@ function p2pk (a, opts) { if (a.input) { if (_chunks().length !== 1) throw new TypeError('Input is invalid') - if (!bscript.isCanonicalScriptSignature(_chunks()[0])) throw new TypeError('Input has invalid signature') + if (!bscript.isCanonicalScriptSignature(o.signature)) throw new TypeError('Input has invalid signature') } } diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index ba42ba1..c47c354 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -93,7 +93,6 @@ function p2wpkh (a, opts) { if (network && network.bech32 !== _address().prefix) throw new TypeError('Invalid prefix or Network mismatch') if (_address().version !== 0x00) throw new TypeError('Invalid address version') if (_address().data.length !== 20) throw new TypeError('Invalid address data') - // if (hash && !hash.equals(_address().data)) throw new TypeError('Hash mismatch') hash = _address().data } diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index c84d822..8c45022 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -122,7 +122,7 @@ function p2wsh (a, opts) { if (_address().prefix !== network.bech32) throw new TypeError('Invalid prefix or Network mismatch') if (_address().version !== 0x00) throw new TypeError('Invalid address version') if (_address().data.length !== 32) throw new TypeError('Invalid address data') - else hash = _address().data + hash = _address().data } if (a.hash) { From 55207e57427b30ae4380d30dd33e867ac6e1b773 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 27 Jul 2018 20:44:02 +1000 Subject: [PATCH 104/568] tests/payments/p2pk*: add signature mismatch tests --- src/payments/p2pk.js | 2 +- test/fixtures/p2pk.json | 13 +++++++++++++ test/fixtures/p2pkh.json | 7 +++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index 5f65030..4073d52 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -65,7 +65,7 @@ function p2pk (a, opts) { } if (a.signature) { - if (a.input && !a.input.equals(o.input)) throw new TypeError('Input mismatch') + if (a.input && !a.input.equals(o.input)) throw new TypeError('Signature mismatch') } if (a.input) { diff --git a/test/fixtures/p2pk.json b/test/fixtures/p2pk.json index 7ede5f4..a07c5b8 100644 --- a/test/fixtures/p2pk.json +++ b/test/fixtures/p2pk.json @@ -116,6 +116,19 @@ "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", "input": "ffffffffffffffff" } + }, + { + "exception": "Input has invalid signature", + "arguments": { + "input": "30060201ff0201ff01" + } + }, + { + "exception": "Signature mismatch", + "arguments": { + "signature": "300602010002010001", + "input": "300602010302010301" + } } ], "dynamic": { diff --git a/test/fixtures/p2pkh.json b/test/fixtures/p2pkh.json index d16b181..118111d 100644 --- a/test/fixtures/p2pkh.json +++ b/test/fixtures/p2pkh.json @@ -204,6 +204,13 @@ "hash": "ffffffffffffffffffffffffffffffffffffffff", "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001" } + }, + { + "exception": "Signature mismatch", + "arguments": { + "signature": "300602010002010001", + "input": "300602010302010301 030000000000000000000000000000000000000000000000000000000000000001" + } } ], "dynamic": { From 1051946f0014b3ff701ec0f64e3ef0e579c0635f Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 27 Jul 2018 20:44:20 +1000 Subject: [PATCH 105/568] tests/payments/p2pk: fix wrong fixture names --- test/fixtures/p2pk.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/fixtures/p2pk.json b/test/fixtures/p2pk.json index a07c5b8..a9e1063 100644 --- a/test/fixtures/p2pk.json +++ b/test/fixtures/p2pk.json @@ -7,7 +7,7 @@ }, "expected": { "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", - "signatures": null, + "signature": null, "input": null, "witness": null } @@ -19,7 +19,7 @@ }, "expected": { "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", - "signatures": null, + "signature": null, "input": null, "witness": null } From cd9e6d1d5e6bf219a86dc4bd555e1c26733cdcae Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 27 Jul 2018 20:59:13 +1000 Subject: [PATCH 106/568] tests/p2wsh: add secondary stacksEqual test --- test/fixtures/p2wsh.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json index 9579f64..2f7b9cc 100644 --- a/test/fixtures/p2wsh.json +++ b/test/fixtures/p2wsh.json @@ -269,6 +269,20 @@ ] } }, + { + "exception": "Witness and redeem.witness mismatch", + "arguments": { + "redeem": { + "output": "OP_TRUE", + "witness": [ + "04000000ff" + ] + }, + "witness": [ + "04000000ee" + ] + } + }, { "exception": "Ambiguous witness source", "arguments": { From e639914d229f80eb6dde39244b061f82c39dca4b Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 3 Aug 2018 10:34:49 +1000 Subject: [PATCH 107/568] CHANGELOG: note alternative to templates --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10d3eb3..e6cd2a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,7 @@ __removed__ - Removed `bufferutils` (#1035) - Removed `networks.litecoin`, BYO non-Bitcoin networks instead (#1095) - Removed `script.isCanonicalSignature`, use `script.isCanonicalScriptSignature` instead (#1094) -- Removed `script.*.input/output/check` functions (`templates`) (previously added in #681, #682) (#1119) +- Removed `script.*.input/output/check` functions (`templates`), use `payments.*` instead (`templates` previously added in #681, #682) (#1119) - Removed dependency `bigi`, uses `bn.js` internally now (via `tiny-secp256k1`) (#1070, #1112) - Removed public access to `ECPair` constructor, use exported functions `ECPair.fromPrivateKey`, `ECPair.fromWIF`, `ECPair.makeRandom`, or `ECPair.fromPublicKey` (#1070) From 64b9646870b712be9e1ee0e25278c3dc154ed9b4 Mon Sep 17 00:00:00 2001 From: Yosef Benny Widyokarsono <fybwid@gmail.com> Date: Wed, 8 Aug 2018 10:53:20 +0800 Subject: [PATCH 108/568] Update README.md Fix example anchor at Documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 413bf5d..2a7b5b8 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Mistakes and bugs happen, but with your help in resolving and reporting [issues] - Friendly, with a strong and helpful community, ready to answer questions. ## Documentation -Presently, we do not have any formal documentation other than our [examples](#Examples), please [ask for help](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new) if our examples aren't enough to guide you. +Presently, we do not have any formal documentation other than our [examples](#examples), please [ask for help](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new) if our examples aren't enough to guide you. ## Installation From 77bb5f9bf8aa55f8b8f864aa9bb1612adf36bd6a Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 21 Aug 2018 18:09:20 +1000 Subject: [PATCH 109/568] test/integration/addresses: add missing extra timeout --- test/integration/addresses.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 412e792..9575201 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -102,6 +102,8 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can support the retrieval of transactions for an address (via 3PBP)', function (done) { + this.timeout(30000) + const keyPair = bitcoin.ECPair.makeRandom() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) From ad6fa57a6dc919b19407d89392ed1895ccae49d0 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 21 Aug 2018 18:11:35 +1000 Subject: [PATCH 110/568] 1 timeout to rule them all --- package.json | 2 +- test/integration/addresses.js | 2 -- test/integration/cltv.js | 9 --------- test/integration/crypto.js | 2 -- test/integration/csv.js | 5 ----- test/integration/payments.js | 2 -- test/integration/transactions.js | 14 -------------- 7 files changed, 1 insertion(+), 35 deletions(-) diff --git a/package.json b/package.json index b9be434..a696033 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", "coverage": "nyc --check-coverage --branches 90 --functions 90 mocha", - "integration": "mocha test/integration/", + "integration": "mocha --timeout 50000 test/integration/", "standard": "standard", "test": "npm run standard && npm run coverage", "unit": "mocha" diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 9575201..412e792 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -102,8 +102,6 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can support the retrieval of transactions for an address (via 3PBP)', function (done) { - this.timeout(30000) - const keyPair = bitcoin.ECPair.makeRandom() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 626c7e9..0161588 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -12,7 +12,6 @@ const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZs describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // force update MTP before(function (done) { - this.timeout(30000) regtestUtils.mine(11, done) }) @@ -41,8 +40,6 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // expiry past, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', function (done) { - this.timeout(30000) - // 3 hours ago const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) @@ -86,8 +83,6 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // expiry will pass, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', function (done) { - this.timeout(30000) - regtestUtils.height(function (err, height) { if (err) return done(err) @@ -142,8 +137,6 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', function (done) { - this.timeout(30000) - // two hours ago const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) @@ -188,8 +181,6 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // expiry in the future, {Alice's signature} OP_TRUE it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', function (done) { - this.timeout(30000) - // two hours from now const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 864f4e7..c0eb2e2 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -9,8 +9,6 @@ const tinysecp = require('tiny-secp256k1') describe('bitcoinjs-lib (crypto)', function () { it('can recover a private key from duplicate R values', function () { - this.timeout(30000) - // https://blockchain.info/tx/f4c16475f2a6e9c602e4a287f9db3040e319eb9ece74761a4b84bc820fbeef50 const tx = bitcoin.Transaction.fromHex('01000000020b668015b32a6178d8524cfef6dc6fc0a4751915c2e9b2ed2d2eab02424341c8000000006a47304402205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022024bf5f506968f5f23f1835574d5afe0e9021b4a5b65cf9742332d5e4acb68f41012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffffa95fa69f11dc1cbb77ef64f25a95d4b12ebda57d19d843333819d95c9172ff89000000006b48304502205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022100832176b59e8f50c56631acbc824bcba936c9476c559c42a4468be98975d07562012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffff02b000eb04000000001976a91472956eed9a8ecb19ae7e3ebd7b06cae4668696a788ac303db000000000001976a9146c0bd55dd2592287cd9992ce3ba3fc1208fb76da88ac00000000') diff --git a/test/integration/csv.js b/test/integration/csv.js index e0ad02f..ac250f5 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -12,7 +12,6 @@ const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZs describe('bitcoinjs-lib (transactions w/ CSV)', function () { // force update MTP before(function (done) { - this.timeout(30000) regtestUtils.mine(11, done) }) @@ -38,8 +37,6 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // expiry will pass, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', function (done) { - this.timeout(30000) - regtestUtils.height(function (err, height) { if (err) return done(err) @@ -99,8 +96,6 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // expiry in the future, {Alice's signature} OP_TRUE it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', function (done) { - this.timeout(30000) - // two hours after confirmation const sequence = bip68.encode({ seconds: 7168 }) const p2sh = bitcoin.payments.p2sh({ diff --git a/test/integration/payments.js b/test/integration/payments.js index d319f60..263111e 100644 --- a/test/integration/payments.js +++ b/test/integration/payments.js @@ -43,8 +43,6 @@ function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) { if (!output) throw new TypeError('Missing output') describe('bitcoinjs-lib (payments - ' + k + ')', function () { - this.timeout(30000) - it('can broadcast as an output, and be spent as an input', (done) => { buildAndSign(depends, output, null, null, done) }) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index ef95d53..35f8ff2 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -45,8 +45,6 @@ describe('bitcoinjs-lib (transactions)', function () { }) it('can create (and broadcast via 3PBP) a typical Transaction', function (done) { - this.timeout(30000) - const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }) const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }) const aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng }) @@ -81,8 +79,6 @@ describe('bitcoinjs-lib (transactions)', function () { }) it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', function (done) { - this.timeout(30000) - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: regtest }) @@ -103,8 +99,6 @@ describe('bitcoinjs-lib (transactions)', function () { }) it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', function (done) { - this.timeout(30000) - const keyPairs = [ bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), @@ -141,8 +135,6 @@ describe('bitcoinjs-lib (transactions)', function () { }) it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', function (done) { - this.timeout(30000) - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }) @@ -172,8 +164,6 @@ describe('bitcoinjs-lib (transactions)', function () { }) it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', function (done) { - this.timeout(30000) - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) @@ -202,8 +192,6 @@ describe('bitcoinjs-lib (transactions)', function () { }) it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', function (done) { - this.timeout(30000) - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest }) const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }) @@ -233,8 +221,6 @@ describe('bitcoinjs-lib (transactions)', function () { }) it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', function (done) { - this.timeout(50000) - const keyPairs = [ bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), From ce60e72cd41ff26b8b59ca1a458e9a9245ce1dd1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 22 Aug 2018 12:23:56 +0900 Subject: [PATCH 111/568] Add complex CSV case for integration tests --- test/integration/csv.js | 210 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) diff --git a/test/integration/csv.js b/test/integration/csv.js index ac250f5..81b3f6f 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -8,6 +8,8 @@ const bip68 = require('bip68') const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) +const charles = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf', regtest) +const dave = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', regtest) describe('bitcoinjs-lib (transactions w/ CSV)', function () { // force update MTP @@ -35,6 +37,37 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { ]) } + // 2 of 3 multisig of bQ, cQ, dQ, but after sequence1 time, + // aQ can allow the multisig to become 1 of 3. + // But after sequence2 time, aQ can sign for the output all by themself. + function complexCsvOutput (aQ, bQ, cQ, dQ, sequence1, sequence2) { + return bitcoin.script.compile([ + bitcoin.opcodes.OP_IF, + bitcoin.opcodes.OP_IF, + bitcoin.opcodes.OP_2, + bitcoin.opcodes.OP_ELSE, + bitcoin.script.number.encode(sequence1), + bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, + bitcoin.opcodes.OP_DROP, + aQ.publicKey, + bitcoin.opcodes.OP_CHECKSIGVERIFY, + bitcoin.opcodes.OP_1, + bitcoin.opcodes.OP_ENDIF, + bQ.publicKey, + cQ.publicKey, + dQ.publicKey, + bitcoin.opcodes.OP_3, + bitcoin.opcodes.OP_CHECKMULTISIG, + bitcoin.opcodes.OP_ELSE, + bitcoin.script.number.encode(sequence2), + bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, + bitcoin.opcodes.OP_DROP, + aQ.publicKey, + bitcoin.opcodes.OP_CHECKSIG, + bitcoin.opcodes.OP_ENDIF, + ]) + } + // expiry will pass, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', function (done) { regtestUtils.height(function (err, height) { @@ -139,4 +172,181 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) }) }) + + // Check first combination of complex CSV, 2 of 3 + it('can create (and broadcast via 3PBP) a Transaction where Bob and Charles can send ', function (done) { + regtestUtils.height(function (err, height) { + if (err) return done(err) + + // 5 blocks from now + const sequence1 = bip68.encode({ blocks: 5 }) + // 10 blocks from now + const sequence2 = bip68.encode({ blocks: 20 }) + const p2sh = bitcoin.payments.p2sh({ + redeem: { + output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) + }, + network: regtest + }) + + // fund the P2SH(CCSV) address + regtestUtils.faucet(p2sh.address, 1e5, function (err, unspent) { + if (err) return done(err) + + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + + // OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem.output, + input: bitcoin.script.compile([ + bitcoin.opcodes.OP_0, + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.script.signature.encode(charles.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE, + bitcoin.opcodes.OP_TRUE + ]) + } + }).input + tx.setInputScript(0, redeemScriptSig) + + regtestUtils.broadcast(tx.toHex(), function (err) { + if (err) return done(err) + + regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4 + }, done) + }) + }) + }) + }) + + // Check first combination of complex CSV, lawyer + 1 of 3 after 5 blocks + it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) and Bob can send after 5 blocks ', function (done) { + regtestUtils.height(function (err, height) { + if (err) return done(err) + + // 5 blocks from now + const sequence1 = bip68.encode({ blocks: 5 }) + // 10 blocks from now + const sequence2 = bip68.encode({ blocks: 20 }) + const p2sh = bitcoin.payments.p2sh({ + redeem: { + output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) + }, + network: regtest + }) + + // fund the P2SH(CCSV) address + regtestUtils.faucet(p2sh.address, 1e5, function (err, unspent) { + if (err) return done(err) + + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, sequence1) // Set sequence1 for input + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + + // OP_0 {Bob sig} {Alice lawyer sig} OP_FALSE OP_TRUE + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem.output, + input: bitcoin.script.compile([ + bitcoin.opcodes.OP_0, + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.opcodes.OP_0, + bitcoin.opcodes.OP_TRUE + ]) + } + }).input + tx.setInputScript(0, redeemScriptSig) + + // 10 > 5 but < 20 + regtestUtils.mine(10, function (err) { + if (err) return done(err) + + regtestUtils.broadcast(tx.toHex(), function (err) { + if (err) return done(err) + + regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4 + }, done) + }) + }) + }) + }) + }) + + // Check first combination of complex CSV, lawyer after 20 blocks + it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) can send after 20 blocks ', function (done) { + regtestUtils.height(function (err, height) { + if (err) return done(err) + + // 5 blocks from now + const sequence1 = bip68.encode({ blocks: 5 }) + // 10 blocks from now + const sequence2 = bip68.encode({ blocks: 20 }) + const p2sh = bitcoin.payments.p2sh({ + redeem: { + output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) + }, + network: regtest + }) + + // fund the P2SH(CCSV) address + regtestUtils.faucet(p2sh.address, 1e5, function (err, unspent) { + if (err) return done(err) + + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, sequence2) // Set sequence2 for input + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + + // {Alice lawyer sig} OP_FALSE + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem.output, + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.opcodes.OP_0 + ]) + } + }).input + tx.setInputScript(0, redeemScriptSig) + + regtestUtils.mine(30, function (err) { // 30 > 20 + if (err) return done(err) + + regtestUtils.broadcast(tx.toHex(), function (err) { + if (err) return done(err) + + regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4 + }, done) + }) + }) + }) + }) + }) }) From 86842c821e514c1f79fd14b44f539359f8e8d134 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 22 Aug 2018 14:18:21 +0900 Subject: [PATCH 112/568] Make test faster (manually tested failure for 1 and 4 vs 2 and 5) --- test/integration/csv.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/test/integration/csv.js b/test/integration/csv.js index 81b3f6f..dbfea1d 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -178,10 +178,10 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { regtestUtils.height(function (err, height) { if (err) return done(err) + // 2 blocks from now + const sequence1 = bip68.encode({ blocks: 2 }) // 5 blocks from now - const sequence1 = bip68.encode({ blocks: 5 }) - // 10 blocks from now - const sequence2 = bip68.encode({ blocks: 20 }) + const sequence2 = bip68.encode({ blocks: 5 }) const p2sh = bitcoin.payments.p2sh({ redeem: { output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) @@ -230,15 +230,15 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) }) - // Check first combination of complex CSV, lawyer + 1 of 3 after 5 blocks - it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) and Bob can send after 5 blocks ', function (done) { + // Check first combination of complex CSV, lawyer + 1 of 3 after 2 blocks + it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) and Bob can send after 2 blocks ', function (done) { regtestUtils.height(function (err, height) { if (err) return done(err) + // 2 blocks from now + const sequence1 = bip68.encode({ blocks: 2 }) // 5 blocks from now - const sequence1 = bip68.encode({ blocks: 5 }) - // 10 blocks from now - const sequence2 = bip68.encode({ blocks: 20 }) + const sequence2 = bip68.encode({ blocks: 5 }) const p2sh = bitcoin.payments.p2sh({ redeem: { output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) @@ -273,8 +273,8 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }).input tx.setInputScript(0, redeemScriptSig) - // 10 > 5 but < 20 - regtestUtils.mine(10, function (err) { + // Wait 2 blocks + regtestUtils.mine(2, function (err) { if (err) return done(err) regtestUtils.broadcast(tx.toHex(), function (err) { @@ -292,15 +292,15 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) }) - // Check first combination of complex CSV, lawyer after 20 blocks - it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) can send after 20 blocks ', function (done) { + // Check first combination of complex CSV, lawyer after 5 blocks + it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) can send after 5 blocks ', function (done) { regtestUtils.height(function (err, height) { if (err) return done(err) + // 2 blocks from now + const sequence1 = bip68.encode({ blocks: 2 }) // 5 blocks from now - const sequence1 = bip68.encode({ blocks: 5 }) - // 10 blocks from now - const sequence2 = bip68.encode({ blocks: 20 }) + const sequence2 = bip68.encode({ blocks: 5 }) const p2sh = bitcoin.payments.p2sh({ redeem: { output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) @@ -332,7 +332,8 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }).input tx.setInputScript(0, redeemScriptSig) - regtestUtils.mine(30, function (err) { // 30 > 20 + // Wait 5 blocks + regtestUtils.mine(5, function (err) { if (err) return done(err) regtestUtils.broadcast(tx.toHex(), function (err) { From 9a9ab3b8837213c2e74526bfc5f3d3d0f10a222a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 22 Aug 2018 14:33:58 +0900 Subject: [PATCH 113/568] Add examples to readme --- README.md | 5 +++++ test/integration/csv.js | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2a7b5b8..edcb265 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,11 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). - [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L88) - [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L144) - [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L190) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L72) +- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L131) +- [Create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L177) +- [Create (and broadcast via 3PBP) a Transaction where Alice (lawyer) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L234) +- [Create (and broadcast via 3PBP) a Transaction where Alice (lawyer) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L296) - [Recover a private key from duplicate R values](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L14) - [Recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L68) - [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L72) diff --git a/test/integration/csv.js b/test/integration/csv.js index dbfea1d..91e4ceb 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -69,7 +69,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { } // expiry will pass, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', function (done) { + it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)', function (done) { regtestUtils.height(function (err, height) { if (err) return done(err) @@ -128,7 +128,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) // expiry in the future, {Alice's signature} OP_TRUE - it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', function (done) { + it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)', function (done) { // two hours after confirmation const sequence = bip68.encode({ seconds: 7168 }) const p2sh = bitcoin.payments.p2sh({ @@ -174,7 +174,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) // Check first combination of complex CSV, 2 of 3 - it('can create (and broadcast via 3PBP) a Transaction where Bob and Charles can send ', function (done) { + it('can create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)', function (done) { regtestUtils.height(function (err, height) { if (err) return done(err) @@ -231,7 +231,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) // Check first combination of complex CSV, lawyer + 1 of 3 after 2 blocks - it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) and Bob can send after 2 blocks ', function (done) { + it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)', function (done) { regtestUtils.height(function (err, height) { if (err) return done(err) @@ -293,7 +293,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) // Check first combination of complex CSV, lawyer after 5 blocks - it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) can send after 5 blocks ', function (done) { + it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)', function (done) { regtestUtils.height(function (err, height) { if (err) return done(err) From d7cbbb553b268c9fefda6df8fbe12af31b9df38d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 22 Aug 2018 14:41:41 +0900 Subject: [PATCH 114/568] Fix indent --- test/integration/csv.js | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/test/integration/csv.js b/test/integration/csv.js index 91e4ceb..704ac60 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -42,29 +42,29 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // But after sequence2 time, aQ can sign for the output all by themself. function complexCsvOutput (aQ, bQ, cQ, dQ, sequence1, sequence2) { return bitcoin.script.compile([ - bitcoin.opcodes.OP_IF, - bitcoin.opcodes.OP_IF, - bitcoin.opcodes.OP_2, - bitcoin.opcodes.OP_ELSE, - bitcoin.script.number.encode(sequence1), - bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, - bitcoin.opcodes.OP_DROP, - aQ.publicKey, - bitcoin.opcodes.OP_CHECKSIGVERIFY, - bitcoin.opcodes.OP_1, - bitcoin.opcodes.OP_ENDIF, - bQ.publicKey, - cQ.publicKey, - dQ.publicKey, - bitcoin.opcodes.OP_3, - bitcoin.opcodes.OP_CHECKMULTISIG, - bitcoin.opcodes.OP_ELSE, - bitcoin.script.number.encode(sequence2), - bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, - bitcoin.opcodes.OP_DROP, - aQ.publicKey, - bitcoin.opcodes.OP_CHECKSIG, - bitcoin.opcodes.OP_ENDIF, + bitcoin.opcodes.OP_IF, + bitcoin.opcodes.OP_IF, + bitcoin.opcodes.OP_2, + bitcoin.opcodes.OP_ELSE, + bitcoin.script.number.encode(sequence1), + bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, + bitcoin.opcodes.OP_DROP, + aQ.publicKey, + bitcoin.opcodes.OP_CHECKSIGVERIFY, + bitcoin.opcodes.OP_1, + bitcoin.opcodes.OP_ENDIF, + bQ.publicKey, + cQ.publicKey, + dQ.publicKey, + bitcoin.opcodes.OP_3, + bitcoin.opcodes.OP_CHECKMULTISIG, + bitcoin.opcodes.OP_ELSE, + bitcoin.script.number.encode(sequence2), + bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, + bitcoin.opcodes.OP_DROP, + aQ.publicKey, + bitcoin.opcodes.OP_CHECKSIG, + bitcoin.opcodes.OP_ENDIF ]) } From 89eb6fac03bc022b53b81ed4e103dfa509af92b1 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Tue, 21 Aug 2018 17:52:17 +1000 Subject: [PATCH 115/568] break test/transaction_builder contruct stages, add sequential example --- test/fixtures/transaction_builder.json | 22 ++++++ test/transaction_builder.js | 94 ++++++++++++++++---------- 2 files changed, 82 insertions(+), 34 deletions(-) diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 6896f30..7a3a8d2 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -1547,6 +1547,28 @@ ] } ], + "fromTransactionSequential": [ + { + "description": "Transaction w/ P2SH(P2MS 2/3) -> ?", + "network": "testnet", + "txHex": "0100000001b033b2214568b49fda417371aba0634b0303a2b6a19884c25d03d0b91bdbe231000000006f000000004c6952210258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a9121038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f2103b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d953aeffffffff0200e1f5050000000017a914a9974100aeee974a20cda9a2f545704a0ab54fdc87c72831010000000017a9149f57a6712ef023f85ffac631ed4263b977b2d0678700000000", + "txHexAfter": "0100000001b033b2214568b49fda417371aba0634b0303a2b6a19884c25d03d0b91bdbe23100000000b60000004730440220793d87f2a8afeb856816efa38984418c692c15170e99ca371f547454079c0dd3022074ae95e438fee1f37619fabe0ce1083c3be0d65c3defb5337833d50fdc694b13014c6952210258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a9121038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f2103b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d953aeffffffff0200e1f5050000000017a914a9974100aeee974a20cda9a2f545704a0ab54fdc87c72831010000000017a9149f57a6712ef023f85ffac631ed4263b977b2d0678700000000", + "incomplete": true, + "inputs": [ + { + "vout": 0, + "scriptSig": "OP_0 OP_0 OP_0 OP_0 52210258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a9121038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f2103b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d953ae", + "scriptSigAfter": "OP_0 OP_0 OP_0 30440220793d87f2a8afeb856816efa38984418c692c15170e99ca371f547454079c0dd3022074ae95e438fee1f37619fabe0ce1083c3be0d65c3defb5337833d50fdc694b1301 52210258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a9121038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f2103b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d953ae", + "signs": [ + { + "keyPair": "cTkcnMZoFYH1UgumzCFHv2veLMNN1PaJyHHUxFT127zhNGBqqEZ2", + "redeemScript": "OP_2 0258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a91 038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f 03b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d9 OP_3 OP_CHECKMULTISIG" + } + ] + } + ] + } + ], "classification": { "hex": "01000000059c06fb641a8cd69be81ca91e68d8a115cb698396876ecd77120ec1e4ab9002279f000000b500483045022100d58f828ab39cfac592f89fe372fb520992975218698c683a893f29e39cf0080302207cc0485dab5ce621089bdd15e1f15db0ecbde8dd4bb661bcf0e3af6ecab075e6014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffff0821dc00213d2b7993f8f2a1553800c6f2f31106da176505d0ade467b68401d795000000b400473044022028e937a7bba888fe3428f442f6e22d92ce2ddba01548c38780d40890fa6cc305022043204d0bcfb1150b045d54cf9b13462e44e2ef47fee03d3cea08e84a8060fc30014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffffaa997ac385dc666af1f5947ef615431024eb314cac2308d5e1b903e28ca466f499000000b50048304502210093efc26facedc5f51e304aa270a7b4f1a911b2d912c3674e5c6e2ad4ac7a410402201cf0b62c240461902f9f16d8a0bc3a210b7bfcd2c06523dd4b4b63be22e85252014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffffd9f61bf98a021ee144f33ba5a6b04274de8fcb5c05f1ff7c12367fb7a608b2dd9e000000b4004730440220456e1201c1fa727288cba7fa0054dc02d8dd6c7418cae1e97006ef0652891c9202201192d0fbf3a9c00afb99a415f2bf515509e1150805acd8de95c496c27cb6570f014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffff1f8119e3bc7c2f451feaa79f42ec5a63502afb425c253c935e43d217d5c29bdea1000000b500483045022100f669004f770490093eba4ac4903cb7581f7d18ea9245c538585ef5367e520e4702205485fafe0be178563a599d41e0cc172bb01314ed65d0e48df19a5258f17bdbc4014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffff0380f0fa02000000001976a91439692085cf9d27e8c1cf63e76bd32d9bd15cab8b88ac50c300000000000017a9147204bb26950ce1595255897f63d205779f033f3e875b5409000000000017a9142538893d984a4b5695e4bfde1a90a9f02fabf8e38700000000" }, diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 34ec9da..93f407e 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -19,9 +19,46 @@ function getAddress (node) { return baddress.toBase58Check(bcrypto.hash160(node.publicKey), NETWORKS.bitcoin.pubKeyHash) } +function constructSign (f, txb) { + const network = NETWORKS[f.network] + const stages = f.stages && f.stages.concat() + + f.inputs.forEach(function (input, index) { + if (!input.signs) return + input.signs.forEach(function (sign) { + const keyPair = ECPair.fromWIF(sign.keyPair, network) + let redeemScript + let witnessScript + let value + + if (sign.redeemScript) { + redeemScript = bscript.fromASM(sign.redeemScript) + } + + if (sign.value) { + value = sign.value + } + + if (sign.witnessScript) { + witnessScript = bscript.fromASM(sign.witnessScript) + } + + txb.sign(index, keyPair, redeemScript, sign.hashType, value, witnessScript) + + if (sign.stage) { + const tx = txb.buildIncomplete() + assert.strictEqual(tx.toHex(), stages.shift()) + txb = TransactionBuilder.fromTransaction(tx, network) + } + }) + }) + + return txb +} + function construct (f, dontSign) { const network = NETWORKS[f.network] - let txb = new TransactionBuilder(network) + const txb = new TransactionBuilder(network) if (Number.isFinite(f.version)) txb.setVersion(f.version) if (f.locktime !== undefined) txb.setLockTime(f.locktime) @@ -55,39 +92,7 @@ function construct (f, dontSign) { }) if (dontSign) return txb - - const stages = f.stages && f.stages.concat() - f.inputs.forEach(function (input, index) { - if (!input.signs) return - input.signs.forEach(function (sign) { - const keyPair = ECPair.fromWIF(sign.keyPair, network) - let redeemScript - let witnessScript - let value - - if (sign.redeemScript) { - redeemScript = bscript.fromASM(sign.redeemScript) - } - - if (sign.value) { - value = sign.value - } - - if (sign.witnessScript) { - witnessScript = bscript.fromASM(sign.witnessScript) - } - - txb.sign(index, keyPair, redeemScript, sign.hashType, value, witnessScript) - - if (sign.stage) { - const tx = txb.buildIncomplete() - assert.strictEqual(tx.toHex(), stages.shift()) - txb = TransactionBuilder.fromTransaction(tx, network) - } - }) - }) - - return txb + return constructSign(f, txb) } describe('TransactionBuilder', function () { @@ -142,6 +147,27 @@ describe('TransactionBuilder', function () { }) }) + fixtures.valid.fromTransactionSequential.forEach(function (f) { + it('with ' + f.description, function () { + const network = NETWORKS[f.network] + const tx = Transaction.fromHex(f.txHex) + const txb = TransactionBuilder.fromTransaction(tx, network) + + tx.ins.forEach(function (input, i) { + assert.equal(bscript.toASM(input.script), f.inputs[i].scriptSig) + }) + + constructSign(f, txb) + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + + txAfter.ins.forEach(function (input, i) { + assert.equal(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) + }) + + assert.equal(txAfter.toHex(), f.txHexAfter) + }) + }) + it('classifies transaction inputs', function () { const tx = Transaction.fromHex(fixtures.valid.classification.hex) const txb = TransactionBuilder.fromTransaction(tx) From 24d541d0ed13afaeaf866f262fc5a7b592b64572 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 28 Aug 2018 14:21:18 +0900 Subject: [PATCH 116/568] Fix default assignment of validate key for payments Fixes problems with p2ms experienced in issue below. Related: #1194 --- src/payments/embed.js | 2 +- src/payments/p2ms.js | 2 +- src/payments/p2pk.js | 2 +- src/payments/p2pkh.js | 2 +- src/payments/p2sh.js | 2 +- src/payments/p2wpkh.js | 2 +- src/payments/p2wsh.js | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/payments/embed.js b/src/payments/embed.js index c636c80..ea2c8e9 100644 --- a/src/payments/embed.js +++ b/src/payments/embed.js @@ -19,7 +19,7 @@ function p2data (a, opts) { !a.data && !a.output ) throw new TypeError('Not enough data') - opts = opts || { validate: true } + opts = Object.assign({ validate: true }, opts || {}) typef({ network: typef.maybe(typef.Object), diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index 15de44e..5c90a4d 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -24,7 +24,7 @@ function p2ms (a, opts) { !(a.pubkeys && a.m !== undefined) && !a.signatures ) throw new TypeError('Not enough data') - opts = opts || { validate: true } + opts = Object.assign({ validate: true }, opts || {}) function isAcceptableSignature (x) { return bscript.isCanonicalScriptSignature(x) || (opts.allowIncomplete && (x === OPS.OP_0)) diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index 4073d52..b930612 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -16,7 +16,7 @@ function p2pk (a, opts) { !a.input && !a.signature ) throw new TypeError('Not enough data') - opts = opts || { validate: true } + opts = Object.assign({ validate: true }, opts || {}) typef({ network: typef.maybe(typef.Object), diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index 0ab9fa0..e9248a2 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -18,7 +18,7 @@ function p2pkh (a, opts) { !a.pubkey && !a.input ) throw new TypeError('Not enough data') - opts = opts || { validate: true } + opts = Object.assign({ validate: true }, opts || {}) typef({ network: typef.maybe(typef.Object), diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index b642154..4741936 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -26,7 +26,7 @@ function p2sh (a, opts) { !a.redeem && !a.input ) throw new TypeError('Not enough data') - opts = opts || { validate: true } + opts = Object.assign({ validate: true }, opts || {}) typef({ network: typef.maybe(typef.Object), diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index c47c354..1483bd8 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -21,7 +21,7 @@ function p2wpkh (a, opts) { !a.pubkey && !a.witness ) throw new TypeError('Not enough data') - opts = opts || { validate: true } + opts = Object.assign({ validate: true }, opts || {}) typef({ address: typef.maybe(typef.String), diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index 8c45022..a26e706 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -28,7 +28,7 @@ function p2wsh (a, opts) { !a.redeem && !a.witness ) throw new TypeError('Not enough data') - opts = opts || { validate: true } + opts = Object.assign({ validate: true }, opts || {}) typef({ network: typef.maybe(typef.Object), From f84a5d18a3246d5f2a05f6b170c356dbc79d2e02 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 3 Sep 2018 12:05:06 +0900 Subject: [PATCH 117/568] Fix test cases to make sure that validate: true is being set to default --- test/fixtures/embed.json | 9 +++++++++ test/fixtures/p2ms.json | 4 ++++ test/fixtures/p2pk.json | 2 ++ test/fixtures/p2pkh.json | 2 ++ test/fixtures/p2sh.json | 2 ++ test/fixtures/p2wpkh.json | 2 ++ test/fixtures/p2wsh.json | 2 ++ test/transaction_builder.js | 2 +- 8 files changed, 24 insertions(+), 1 deletion(-) diff --git a/test/fixtures/embed.json b/test/fixtures/embed.json index ccc0e70..40e43cd 100644 --- a/test/fixtures/embed.json +++ b/test/fixtures/embed.json @@ -5,6 +5,7 @@ "arguments": { "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" }, + "options": {}, "expected": { "data": [ "a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4" @@ -35,6 +36,14 @@ { "exception": "Not enough data", "arguments": {} + }, + { + "description": "First OP is not OP_RETURN", + "exception": "Output is invalid", + "options": {}, + "arguments": { + "output": "OP_1 OP_2 OP_ADD" + } } ], "dynamic": { diff --git a/test/fixtures/p2ms.json b/test/fixtures/p2ms.json index d5bb0c8..8ea033e 100644 --- a/test/fixtures/p2ms.json +++ b/test/fixtures/p2ms.json @@ -5,6 +5,7 @@ "arguments": { "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG" }, + "options": {}, "expected": { "m": 2, "n": 2, @@ -239,6 +240,7 @@ { "description": "n !== output pubkeys", "exception": "Output is invalid", + "options": {}, "arguments": { "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_2 OP_CHECKMULTISIG" } @@ -266,6 +268,7 @@ }, { "exception": "Pubkeys mismatch", + "options": {}, "arguments": { "pubkeys": [ "030000000000000000000000000000000000000000000000000000000000000001" @@ -325,6 +328,7 @@ { "description": "Missing OP_0", "exception": "Input is invalid", + "options": {}, "arguments": { "m": 2, "pubkeys": [ diff --git a/test/fixtures/p2pk.json b/test/fixtures/p2pk.json index a9e1063..58f51cb 100644 --- a/test/fixtures/p2pk.json +++ b/test/fixtures/p2pk.json @@ -5,6 +5,7 @@ "arguments": { "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG" }, + "options": {}, "expected": { "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", "signature": null, @@ -97,6 +98,7 @@ }, { "exception": "Pubkey mismatch", + "options": {}, "arguments": { "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", "output": "030000000000000000000000000000000000000000000000000000000000000002 OP_CHECKSIG" diff --git a/test/fixtures/p2pkh.json b/test/fixtures/p2pkh.json index 118111d..44c20fe 100644 --- a/test/fixtures/p2pkh.json +++ b/test/fixtures/p2pkh.json @@ -5,6 +5,7 @@ "arguments": { "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh" }, + "options": {}, "expected": { "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", @@ -103,6 +104,7 @@ { "description": "Unexpected OP_DUP", "exception": "Output is invalid", + "options": {}, "arguments": { "output": "OP_DUP OP_DUP 168b992bcfc44050310b3a94bd0771136d0b28d137 OP_EQUALVERIFY" } diff --git a/test/fixtures/p2sh.json b/test/fixtures/p2sh.json index 44611a5..e6fe2ae 100644 --- a/test/fixtures/p2sh.json +++ b/test/fixtures/p2sh.json @@ -5,6 +5,7 @@ "arguments": { "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT" }, + "options": {}, "expected": { "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", @@ -182,6 +183,7 @@ { "description": "Expected OP_HASH160", "exception": "Output is invalid", + "options": {}, "arguments": { "output": "OP_HASH256 ffffffffffffffffffffffffffffffffffffffff OP_EQUAL" } diff --git a/test/fixtures/p2wpkh.json b/test/fixtures/p2wpkh.json index 7341907..f667bdc 100644 --- a/test/fixtures/p2wpkh.json +++ b/test/fixtures/p2wpkh.json @@ -5,6 +5,7 @@ "arguments": { "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d" }, + "options": {}, "expected": { "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727", "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", @@ -108,6 +109,7 @@ }, { "exception": "Pubkey mismatch", + "options": {}, "arguments": { "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", "witness": [ diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json index 2f7b9cc..0870278 100644 --- a/test/fixtures/p2wsh.json +++ b/test/fixtures/p2wsh.json @@ -5,6 +5,7 @@ "arguments": { "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q" }, + "options": {}, "expected": { "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", @@ -221,6 +222,7 @@ }, { "exception": "Output is invalid", + "options": {}, "arguments": { "output": "OP_HASH256 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff OP_EQUAL" } diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 34ec9da..05dbbe3 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -477,7 +477,7 @@ describe('TransactionBuilder', function () { redeem: payments.p2ms({ output: redeemScript, signatures - }, { allowIncomplete: true }) + }, { allowIncomplete: true, validate: false }) }).input assert.strictEqual(bscript.toASM(replacement), sign.scriptSigFiltered) From d06c149ec3d3d2594fe816049539b8abce550aea Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 3 Sep 2018 13:46:16 +1000 Subject: [PATCH 118/568] avoid special code path, add explicit fixture overwrite --- test/fixtures/transaction_builder.json | 7 +++---- test/transaction_builder.js | 26 ++++++-------------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 6896f30..6cc6851 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -1491,7 +1491,7 @@ ], "fromTransaction": [ { - "description": "Transaction w/ P2SH(P2MS 2/2) -> OP_RETURN | 1 OP_0, no signatures", + "description": "Transaction w/ P2SH(P2MS 2/2) -> OP_RETURN | 1 OP_0 fixes to 2 OP_0, no signatures", "network": "testnet", "incomplete": true, "inputs": [ @@ -1785,11 +1785,10 @@ "scriptSig": "OP_0 OP_0 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { - "filterOP_0": true, "pubKeyIndex": 0, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae", - "scriptSigFiltered": "OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" + "scriptSigBefore": "OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae", + "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" } ] } diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 05dbbe3..0aaed24 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -463,27 +463,12 @@ describe('TransactionBuilder', function () { input.signs.forEach(function (sign) { // rebuild the transaction each-time after the first if (tx) { - // do we filter OP_0's beforehand? - if (sign.filterOP_0) { - const scriptSig = tx.ins[i].script - - // ignore OP_0 on the front, ignore redeemScript - const signatures = bscript.decompile(scriptSig) - .slice(1, -1) - .filter(x => x !== ops.OP_0) - - // rebuild/replace the scriptSig without them - const replacement = payments.p2sh({ - redeem: payments.p2ms({ - output: redeemScript, - signatures - }, { allowIncomplete: true, validate: false }) - }).input - assert.strictEqual(bscript.toASM(replacement), sign.scriptSigFiltered) - - tx.ins[i].script = replacement + // manually override the scriptSig? + if (sign.scriptSigBefore) { + tx.ins[i].script = bscript.fromASM(sign.scriptSigBefore) } - // now import it + + // rebuild txb = TransactionBuilder.fromTransaction(tx, network) } @@ -492,6 +477,7 @@ describe('TransactionBuilder', function () { // update the tx tx = txb.buildIncomplete() + // now verify the serialized scriptSig is as expected assert.strictEqual(bscript.toASM(tx.ins[i].script), sign.scriptSig) }) From a103e484444919f8b7e7f95eb2801bb8ec764b5a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 3 Sep 2018 15:53:09 +0900 Subject: [PATCH 119/568] Fix indents + references + lawyer changed to mediator --- README.md | 4 +-- test/integration/csv.js | 77 ++++++++++++++++++++++------------------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index edcb265..c85db43 100644 --- a/README.md +++ b/README.md @@ -117,8 +117,8 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). - [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L72) - [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L131) - [Create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L177) -- [Create (and broadcast via 3PBP) a Transaction where Alice (lawyer) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L234) -- [Create (and broadcast via 3PBP) a Transaction where Alice (lawyer) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L296) +- [Create (and broadcast via 3PBP) a Transaction where Alice (mediator) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L234) +- [Create (and broadcast via 3PBP) a Transaction where Alice (mediator) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js#L296) - [Recover a private key from duplicate R values](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L14) - [Recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L68) - [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L72) diff --git a/test/integration/csv.js b/test/integration/csv.js index 704ac60..ec29cbe 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -22,49 +22,54 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // IF MTP (from when confirmed) > seconds, aQ can redeem function csvCheckSigOutput (aQ, bQ, sequence) { return bitcoin.script.compile([ + /* eslint-disable indent */ bitcoin.opcodes.OP_IF, - bitcoin.script.number.encode(sequence), - bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, - bitcoin.opcodes.OP_DROP, - + bitcoin.script.number.encode(sequence), + bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, + bitcoin.opcodes.OP_DROP, bitcoin.opcodes.OP_ELSE, - bQ.publicKey, - bitcoin.opcodes.OP_CHECKSIGVERIFY, + bQ.publicKey, + bitcoin.opcodes.OP_CHECKSIGVERIFY, bitcoin.opcodes.OP_ENDIF, - aQ.publicKey, bitcoin.opcodes.OP_CHECKSIG + /* eslint-enable indent */ ]) } - // 2 of 3 multisig of bQ, cQ, dQ, but after sequence1 time, - // aQ can allow the multisig to become 1 of 3. - // But after sequence2 time, aQ can sign for the output all by themself. + // 2 of 3 multisig of bQ, cQ, dQ, + // but after sequence1 time, aQ can allow the multisig to become 1 of 3. + // but after sequence2 time, aQ can sign for the output all by themself. + // Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example + // Note: bitcoinjs-lib will not offer specific support for problems with + // advanced script usages such as below. Use at your own risk. function complexCsvOutput (aQ, bQ, cQ, dQ, sequence1, sequence2) { return bitcoin.script.compile([ + /* eslint-disable indent */ bitcoin.opcodes.OP_IF, - bitcoin.opcodes.OP_IF, - bitcoin.opcodes.OP_2, + bitcoin.opcodes.OP_IF, + bitcoin.opcodes.OP_2, + bitcoin.opcodes.OP_ELSE, + bitcoin.script.number.encode(sequence1), + bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, + bitcoin.opcodes.OP_DROP, + aQ.publicKey, + bitcoin.opcodes.OP_CHECKSIGVERIFY, + bitcoin.opcodes.OP_1, + bitcoin.opcodes.OP_ENDIF, + bQ.publicKey, + cQ.publicKey, + dQ.publicKey, + bitcoin.opcodes.OP_3, + bitcoin.opcodes.OP_CHECKMULTISIG, bitcoin.opcodes.OP_ELSE, - bitcoin.script.number.encode(sequence1), - bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, - bitcoin.opcodes.OP_DROP, - aQ.publicKey, - bitcoin.opcodes.OP_CHECKSIGVERIFY, - bitcoin.opcodes.OP_1, - bitcoin.opcodes.OP_ENDIF, - bQ.publicKey, - cQ.publicKey, - dQ.publicKey, - bitcoin.opcodes.OP_3, - bitcoin.opcodes.OP_CHECKMULTISIG, - bitcoin.opcodes.OP_ELSE, - bitcoin.script.number.encode(sequence2), - bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, - bitcoin.opcodes.OP_DROP, - aQ.publicKey, - bitcoin.opcodes.OP_CHECKSIG, + bitcoin.script.number.encode(sequence2), + bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, + bitcoin.opcodes.OP_DROP, + aQ.publicKey, + bitcoin.opcodes.OP_CHECKSIG, bitcoin.opcodes.OP_ENDIF + /* eslint-enable indent */ ]) } @@ -230,8 +235,8 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) }) - // Check first combination of complex CSV, lawyer + 1 of 3 after 2 blocks - it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)', function (done) { + // Check first combination of complex CSV, mediator + 1 of 3 after 2 blocks + it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)', function (done) { regtestUtils.height(function (err, height) { if (err) return done(err) @@ -254,7 +259,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { txb.addInput(unspent.txId, unspent.vout, sequence1) // Set sequence1 for input txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) - // OP_0 {Bob sig} {Alice lawyer sig} OP_FALSE OP_TRUE + // OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE const tx = txb.buildIncomplete() const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) const redeemScriptSig = bitcoin.payments.p2sh({ @@ -292,8 +297,8 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) }) - // Check first combination of complex CSV, lawyer after 5 blocks - it('can create (and broadcast via 3PBP) a Transaction where Alice (lawyer) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)', function (done) { + // Check first combination of complex CSV, mediator after 5 blocks + it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)', function (done) { regtestUtils.height(function (err, height) { if (err) return done(err) @@ -316,7 +321,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { txb.addInput(unspent.txId, unspent.vout, sequence2) // Set sequence2 for input txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) - // {Alice lawyer sig} OP_FALSE + // {Alice mediator sig} OP_FALSE const tx = txb.buildIncomplete() const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) const redeemScriptSig = bitcoin.payments.p2sh({ From e6ac7c16ce350b6c43689cfaad7c537392e28bbc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 3 Sep 2018 16:54:56 +0900 Subject: [PATCH 120/568] Use fromASM instead. Much cleaner. --- test/integration/csv.js | 78 +++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/test/integration/csv.js b/test/integration/csv.js index ec29cbe..b654756 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -21,20 +21,18 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // IF MTP (from when confirmed) > seconds, aQ can redeem function csvCheckSigOutput (aQ, bQ, sequence) { - return bitcoin.script.compile([ - /* eslint-disable indent */ - bitcoin.opcodes.OP_IF, - bitcoin.script.number.encode(sequence), - bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, - bitcoin.opcodes.OP_DROP, - bitcoin.opcodes.OP_ELSE, - bQ.publicKey, - bitcoin.opcodes.OP_CHECKSIGVERIFY, - bitcoin.opcodes.OP_ENDIF, - aQ.publicKey, - bitcoin.opcodes.OP_CHECKSIG - /* eslint-enable indent */ - ]) + return bitcoin.script.fromASM(` + OP_IF + ${bitcoin.script.number.encode(sequence).toString('hex')} + OP_CHECKSEQUENCEVERIFY + OP_DROP + OP_ELSE + ${bQ.publicKey.toString('hex')} + OP_CHECKSIGVERIFY + OP_ENDIF + ${aQ.publicKey.toString('hex')} + OP_CHECKSIG + `.trim().replace(/\s+/g, ' ')) } // 2 of 3 multisig of bQ, cQ, dQ, @@ -44,33 +42,31 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { // Note: bitcoinjs-lib will not offer specific support for problems with // advanced script usages such as below. Use at your own risk. function complexCsvOutput (aQ, bQ, cQ, dQ, sequence1, sequence2) { - return bitcoin.script.compile([ - /* eslint-disable indent */ - bitcoin.opcodes.OP_IF, - bitcoin.opcodes.OP_IF, - bitcoin.opcodes.OP_2, - bitcoin.opcodes.OP_ELSE, - bitcoin.script.number.encode(sequence1), - bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, - bitcoin.opcodes.OP_DROP, - aQ.publicKey, - bitcoin.opcodes.OP_CHECKSIGVERIFY, - bitcoin.opcodes.OP_1, - bitcoin.opcodes.OP_ENDIF, - bQ.publicKey, - cQ.publicKey, - dQ.publicKey, - bitcoin.opcodes.OP_3, - bitcoin.opcodes.OP_CHECKMULTISIG, - bitcoin.opcodes.OP_ELSE, - bitcoin.script.number.encode(sequence2), - bitcoin.opcodes.OP_CHECKSEQUENCEVERIFY, - bitcoin.opcodes.OP_DROP, - aQ.publicKey, - bitcoin.opcodes.OP_CHECKSIG, - bitcoin.opcodes.OP_ENDIF - /* eslint-enable indent */ - ]) + return bitcoin.script.fromASM(` + OP_IF + OP_IF + OP_2 + OP_ELSE + ${bitcoin.script.number.encode(sequence1).toString('hex')} + OP_CHECKSEQUENCEVERIFY + OP_DROP + ${aQ.publicKey.toString('hex')} + OP_CHECKSIGVERIFY + OP_1 + OP_ENDIF + ${bQ.publicKey.toString('hex')} + ${cQ.publicKey.toString('hex')} + ${dQ.publicKey.toString('hex')} + OP_3 + OP_CHECKMULTISIG + OP_ELSE + ${bitcoin.script.number.encode(sequence2).toString('hex')} + OP_CHECKSEQUENCEVERIFY + OP_DROP + ${aQ.publicKey.toString('hex')} + OP_CHECKSIG + OP_ENDIF + `.trim().replace(/\s+/g, ' ')) } // expiry will pass, {Alice's signature} OP_TRUE From b906fd6be6bb6b731d6e6a41ddb0359f31b332cf Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 13 Sep 2018 08:50:14 +1000 Subject: [PATCH 121/568] CHANGELOG: amend script.decompile return type --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6cd2a3..b3985c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ __changed__ - `ECPair` (and all ECDSA code) now uses [`tiny-secp256k1`](http://github.com/bitcoinjs/tiny-secp256k1), which uses the [`libsecp256k1` library](https://github.com/bitcoin-core/secp256k1) (#1070) - `TransactionBuilder` internal variables are now `__` prefixed to discourage public usage (#1038) - `TransactionBuilder` now defaults to version 2 transaction versions (#1036) -- `script.decompile` now returns `Buffer` or `null`, if decompilation failed (#1039) +- `script.decompile` now returns `[Buffer]` or `null`, if decompilation failed (#1039) __fixed__ - Fixed `TransactionBuilder` rejecting uncompressed public keys to comply with BIP143 (#987) From 07fcbe5890d4ef1b63ba4f6e131162d62a18fb52 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 13 Sep 2018 17:34:42 +1000 Subject: [PATCH 122/568] rm unused imports --- test/transaction_builder.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 88294fb..5a6b86f 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -4,8 +4,6 @@ const assert = require('assert') const baddress = require('../src/address') const bcrypto = require('../src/crypto') const bscript = require('../src/script') -const ops = require('bitcoin-ops') -const payments = require('../src/payments') const ECPair = require('../src/ecpair') const Transaction = require('../src/transaction') From 1c64094c2949747cb7ae4434d4a163611b7ef35e Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 13 Sep 2018 17:34:50 +1000 Subject: [PATCH 123/568] use hoodwink@2.0.0 --- package.json | 2 +- test/ecpair.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a696033..6d6b524 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "bn.js": "^4.11.8", "bs58": "^4.0.0", "dhttp": "^2.5.0", - "hoodwink": "^1.0.0", + "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^5.2.0", "nyc": "^11.8.0", diff --git a/test/ecpair.js b/test/ecpair.js index 8a246f1..78223ca 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -187,8 +187,8 @@ describe('ECPair', function () { }) it('loops until d is within interval [1, n) : 1', hoodwink(function () { - const rng = this.stub(function f () { - if (f.calls === 0) return ZERO // 0 + const rng = this.stub(function () { + if (rng.calls === 0) return ZERO // 0 return ONE // >0 }, 2) @@ -196,9 +196,9 @@ describe('ECPair', function () { })) it('loops until d is within interval [1, n) : n - 1', hoodwink(function () { - const rng = this.stub(function f () { - if (f.calls === 0) return ZERO // <1 - if (f.calls === 1) return GROUP_ORDER // >n-1 + const rng = this.stub(function () { + if (rng.calls === 0) return ZERO // <1 + if (rng.calls === 1) return GROUP_ORDER // >n-1 return GROUP_ORDER_LESS_1 // n-1 }, 3) From 09b2475117b2553547eb0fc360946722a457119f Mon Sep 17 00:00:00 2001 From: JP Richardson <jprichardson@gmail.com> Date: Sun, 16 Sep 2018 01:17:04 -0500 Subject: [PATCH 124/568] remove superfluous package.json file in payments/ --- src/payments/package.json | 40 --------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 src/payments/package.json diff --git a/src/payments/package.json b/src/payments/package.json deleted file mode 100644 index 9383db2..0000000 --- a/src/payments/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "bitcoinjs-playground", - "version": "1.0.0", - "description": "Go nuts!", - "main": "_testnet.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/bitcoinjs/bitcoinjs-playground.git" - }, - "author": "", - "license": "ISC", - "bugs": { - "url": "https://github.com/bitcoinjs/bitcoinjs-playground/issues" - }, - "homepage": "https://github.com/bitcoinjs/bitcoinjs-playground#readme", - "dependencies": { - "async": "^2.5.0", - "bech32": "^1.1.3", - "bip21": "^2.0.1", - "bip32-utils": "^0.11.1", - "bip38": "^2.0.2", - "bip39": "^2.5.0", - "bip69": "^2.1.1", - "bitcoin-ops": "^1.4.1", - "bitcoinjs-lib": "^3.3.2", - "bs58": "^4.0.1", - "bs58check": "^2.1.1", - "cb-http-client": "^0.2.3", - "coinselect": "^3.1.11", - "dhttp": "^2.4.2", - "merkle-lib": "^2.0.10", - "mocha": "^5.0.5", - "tape": "^4.9.0", - "typeforce": "^1.11.4", - "utxo": "^2.0.4" - } -} From 35dbcfaa0c8898a7584267e6d9bcee9d55af3331 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sun, 16 Sep 2018 16:52:36 +1000 Subject: [PATCH 125/568] CHANGELOG: note TxBuilder fixes --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3985c8..210f6da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 4.0.2 +__fixed__ +- Fixed `TransactionBuilder` not throwing when payment type validation should fail (#1195) + # 4.0.1 __fixed__ - Fixed `tiny-secp256k1` dependency version (used `ecurve`) (#1139) From c8b7906058b310d2887ce891054a0db7d11d081a Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sun, 16 Sep 2018 16:52:46 +1000 Subject: [PATCH 126/568] 4.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d6b524..dadea12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "4.0.1", + "version": "4.0.2", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "engines": { From a309ee53e2f1432e93003afadd132ed29f05c206 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Sun, 16 Sep 2018 16:54:51 +1000 Subject: [PATCH 127/568] CHANGELOG: add rogue packagejson removal --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 210f6da..907c6d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ __fixed__ - Fixed `TransactionBuilder` not throwing when payment type validation should fail (#1195) +__removed__ +- Removed rogue `package.json` from `src/payments` (#1216) + # 4.0.1 __fixed__ - Fixed `tiny-secp256k1` dependency version (used `ecurve`) (#1139) From f89ef93d1e7dc7d31b0dedfd5076364bb781d177 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 17 Sep 2018 10:38:18 +1000 Subject: [PATCH 128/568] rm empty file --- src/payments/p2data.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/payments/p2data.js diff --git a/src/payments/p2data.js b/src/payments/p2data.js deleted file mode 100644 index e69de29..0000000 From da88e1c5af215292b6507f25ac29e5a0316e4204 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 24 Sep 2018 15:24:23 +1000 Subject: [PATCH 129/568] bump dhttp --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dadea12..755be28 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "bip68": "^1.0.3", "bn.js": "^4.11.8", "bs58": "^4.0.0", - "dhttp": "^2.5.0", + "dhttp": "^3.0.0", "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^5.2.0", From 4616d24016a743ad8e687adcb556a1569efbe2bf Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 27 Jul 2018 15:47:28 +1000 Subject: [PATCH 130/568] tests/txb: remove getAddress --- test/transaction_builder.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 4911cbb..554257e 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -1,8 +1,8 @@ const { describe, it, beforeEach } = require('mocha') const assert = require('assert') const baddress = require('../src/address') -const bcrypto = require('../src/crypto') const bscript = require('../src/script') +const payments = require('../src/payments') const ECPair = require('../src/ecpair') const Transaction = require('../src/transaction') @@ -11,11 +11,6 @@ const NETWORKS = require('../src/networks') const fixtures = require('./fixtures/transaction_builder') -// TODO: remove -function getAddress (node) { - return baddress.toBase58Check(bcrypto.hash160(node.publicKey), NETWORKS.bitcoin.pubKeyHash) -} - function constructSign (f, txb) { const network = NETWORKS[f.network] const stages = f.stages && f.stages.concat() @@ -251,7 +246,7 @@ describe('TransactionBuilder', function () { }) it('accepts an address string and value', function () { - const address = getAddress(keyPair) + const { address } = payments.p2pkh({ pubkey: keyPair.publicKey }) const vout = txb.addOutput(address, 1000) assert.strictEqual(vout, 0) From 43f52c452b764fd41b307bb57c433d29cb2acaf9 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 26 Sep 2018 14:35:47 +1000 Subject: [PATCH 131/568] tests: enforce something threw for invalid cases --- test/transaction_builder.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 554257e..7112be0 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -340,6 +340,7 @@ describe('TransactionBuilder', function () { it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), function () { const txb = construct(f, true) + let threw = false f.inputs.forEach(function (input, index) { input.signs.forEach(function (sign) { const keyPairNetwork = NETWORKS[sign.network || f.network] @@ -355,15 +356,18 @@ describe('TransactionBuilder', function () { witnessScript = bscript.fromASM(sign.witnessScript) } - if (!sign.throws) { - txb.sign(index, keyPair2, redeemScript, sign.hashType, sign.value, witnessScript) - } else { + if (sign.throws) { assert.throws(function () { txb.sign(index, keyPair2, redeemScript, sign.hashType, sign.value, witnessScript) }, new RegExp(f.exception)) + threw = true + } else { + txb.sign(index, keyPair2, redeemScript, sign.hashType, sign.value, witnessScript) } }) }) + + assert.equal(threw, true) }) }) }) From acdfb34545bf38f80189fbb6a2e3365563d1304d Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Thu, 23 Aug 2018 12:38:01 +1000 Subject: [PATCH 132/568] rename Sighash: to SIGHASH --- test/fixtures/transaction_builder.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 4ddc5cb..83ae4eb 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -443,7 +443,7 @@ ] }, { - "description": "Sighash: SINGLE (random)", + "description": "SIGHASH SINGLE (random)", "txHex": "01000000012ffb29d53528ad30c37c267fbbeda3c6fce08f5f6f5d3b1eab22193599a3612a010000006b483045022100f963f1d9564075a934d7c3cfa333bd1378859b84cba947e149926fc9ec89b5ae02202b5b912e507bae65002aff972f9752e2aeb2e22c5fdbaaad672090378184df37032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff0260a62f01000000001976a9140de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b888ac80969800000000001976a91454d0e925d5ee0ee26768a237067dee793d01a70688ac00000000", "version": 1, "inputs": [ @@ -473,7 +473,7 @@ ] }, { - "description": "Sighash: ALL", + "description": "SIGHASH ALL", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006a47304402206abb0622b8b6ca83f1f4de84830cf38bf4615dc9e47a7dcdcc489905f26aa9cb02201d2d8a7815242b88e4cd66390ca46da802238f9b1395e0d118213d30dad38184012102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006b483045022100de13b42804f87a09bb46def12ab4608108d8c2db41db4bc09064f9c46fcf493102205e5c759ab7b2895c9b0447e56029f6895ff7bb20e0847c564a88a3cfcf080c4f012102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b4830450221009100a3f5b30182d1cb0172792af6947b6d8d42badb0539f2c209aece5a0628f002200ae91702ca63347e344c85fcb536f30ee97b75cdf4900de534ed5e040e71a548012102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", "version": 1, "inputs": [ @@ -533,7 +533,7 @@ ] }, { - "description": "Sighash: ALL | ANYONECANPAY", + "description": "SIGHASH ALL | ANYONECANPAY", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100bd2829550e9b3a081747281029b5f5a96bbd83bb6a92fa2f8310f1bd0d53abc90220071b469417c55cdb3b04171fd7900d2768981b7ab011553d84d24ea85d277079812102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a47304402206295e17c45c6356ffb20365b696bcbb869db7e8697f4b8a684098ee2bff85feb02202905c441abe39ec9c480749236b84fdd3ebd91ecd25b559136370aacfcf2815c812102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b483045022100f58e7c98ac8412944d575bcdece0e5966d4018f05988b5b60b6f46b8cb7a543102201c5854d3361e29b58123f34218cec2c722f5ec7a08235ebd007ec637b07c193a812102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", "version": 1, "inputs": [ @@ -593,7 +593,7 @@ ] }, { - "description": "Sighash: SINGLE", + "description": "SIGHASH SINGLE", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100e822f152bb15a1d623b91913cd0fb915e9f85a8dc6c26d51948208bbc0218e800220255f78549d9614c88eac9551429bc00224f22cdcb41a3af70d52138f7e98d333032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a47304402206f37f79adeb86e0e2da679f79ff5c3ba206c6d35cd9a21433f0de34ee83ddbc00220118cabbac5d83b3aa4c2dc01b061e4b2fe83750d85a72ae6a1752300ee5d9aff032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006a473044022042ac843d220a56b3de05f24c85a63e71efa7e5fc7c2ec766a2ffae82a88572b0022051a816b317313ea8d90010a77c3e02d41da4a500e67e6a5347674f836f528d82032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", "version": 1, "inputs": [ @@ -653,7 +653,7 @@ ] }, { - "description": "Sighash: SINGLE|ANYONECANPAY", + "description": "SIGHASH SINGLE|ANYONECANPAY", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100d05a3b6cf2f0301000b0e45c09054f2c61570ce8798ebf571eef72da3b1c94a1022016d7ef3c133fa703bae2c75158ea08d335ac698506f99b3c369c37a9e8fc4beb832102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006b483045022100ee6bf07b051001dcbfa062692a40adddd070303286b714825b3fb4693dd8fcdb022056610885e5053e5d47f2be3433051305abe7978ead8f7cf2d0368947aff6b307832102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b483045022100cfc930d5b5272d0220d9da98fabec97b9e66306f735efa837f43f6adc675cad902202f9dff76b8b9ec8f613d46094f17f64d875804292d8804aa59fd295b6fc1416b832102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", "version": 1, "inputs": [ @@ -713,7 +713,7 @@ ] }, { - "description": "Sighash: NONE", + "description": "SIGHASH NONE", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100e7f0a1ddd2c0b81e093e029b8a503afa27fe43549b0668d2141abf35eb3a63be022037f12d12cd50fc94a135f933406a8937557de9b9566a8841ff1548c1b6984531022102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a473044022008451123ec2535dab545ade9d697519e63b28df5e311ea05e0ce28d39877a7c8022061ce5dbfb7ab478dd9e05b0acfd959ac3eb2641f61958f5d352f37621073d7c0022102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006a47304402205c001bcdfb35c70d8aa3bdbc75399afb72eb7cf1926ca7c1dfcddcb4d4d3e0f8022028992fffdcd4e9f34ab726f97c24157917641c2ef99361f588e3d4147d46eea5022102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", "version": 1, "inputs": [ @@ -773,7 +773,7 @@ ] }, { - "description": "Sighash: NONE | ANYONECANPAY", + "description": "SIGHASH NONE | ANYONECANPAY", "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006a47304402204ed272952177aaa5a1b171c2ca5a7a3d300ffcd7e04b040c0baaa4e3561862a502207e65a5b8f99c8a632b186c8a60496a12bf3116f51909b7497413aefdc3be7bf6822102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a47304402203ec365300cc67602f4cc5be027959d3667b48db34c6c87d267c94a7e210d5c1f02204843350311c0a9711cad1960b17ce9e323a1ce6f37deefc3ffe63082d480be92822102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b48304502210084f86f905c36372eff9c54ccd509a519a3325bcace8abfeed7ed3f0d579979e902201ff330dd2402e5ca9989a8a294fa36d6cf3a093edb18d29c9d9644186a3efeb4822102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", "version": 1, "inputs": [ From ed1c1a5661efa2f274b4a57f6a0b5a8dd68c4e2d Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Fri, 27 Jul 2018 15:55:17 +1000 Subject: [PATCH 133/568] txb: enforce outputs exist when signing --- src/transaction_builder.js | 10 ++++++++-- test/fixtures/transaction_builder.json | 17 +++++++++++++++++ test/transaction_builder.js | 1 + 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 8706841..597c1d0 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -633,6 +633,8 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy // TODO: remove keyPair.network matching in 4.0.0 if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network') if (!this.__inputs[vin]) throw new Error('No input at index: ' + vin) + if (this.__needsOutputs()) throw new Error('Transaction needs outputs') + hashType = hashType || Transaction.SIGHASH_ALL const input = this.__inputs[vin] @@ -694,8 +696,7 @@ function signatureHashType (buffer) { TransactionBuilder.prototype.__canModifyInputs = function () { return this.__inputs.every(function (input) { - // any signatures? - if (input.signatures === undefined) return true + if (!input.signatures) return true return input.signatures.every(function (signature) { if (!signature) return true @@ -708,6 +709,11 @@ TransactionBuilder.prototype.__canModifyInputs = function () { }) } +// TODO: handle incomplete SIGHASH_NONE flow +TransactionBuilder.prototype.__needsOutputs = function () { + return this.__tx.outs.length === 0 +} + TransactionBuilder.prototype.__canModifyOutputs = function () { const nInputs = this.__tx.ins.length const nOutputs = this.__tx.outs.length diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 83ae4eb..62c410a 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -2357,6 +2357,23 @@ "value": 1000 } ] + }, + { + "description": "Transaction w/ no outputs", + "exception": "Transaction needs outputs", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "signs": [ + { + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "throws": true + } + ] + } + ], + "outputs": [] } ], "fromTransaction": [ diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 7112be0..bd0a3ab 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -313,6 +313,7 @@ describe('TransactionBuilder', function () { it('throws if if there exist any scriptSigs', function () { const txb = new TransactionBuilder() txb.addInput(txHash, 0) + txb.addOutput(scripts[0], 100) txb.sign(0, keyPair) assert.throws(function () { From 2223e6170ee964f2637d3840e2beea25025fe707 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 26 Sep 2018 14:39:55 +1000 Subject: [PATCH 134/568] txb/tests: test solo SIGHASH_ALL, support existing SIGHASH_NONE --- src/transaction_builder.js | 14 ++++++++++++-- test/fixtures/transaction_builder.json | 11 ++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 597c1d0..232c9a2 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -709,9 +709,19 @@ TransactionBuilder.prototype.__canModifyInputs = function () { }) } -// TODO: handle incomplete SIGHASH_NONE flow TransactionBuilder.prototype.__needsOutputs = function () { - return this.__tx.outs.length === 0 + // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs + // .build() will fail, but .buildIncomplete() is OK + return (this.__tx.outs.length === 0) && this.__inputs.some((input) => { + if (!input.signatures) return false + + return input.signatures.some((signature) => { + if (!signature) return false // no signature, no issue + const hashType = signatureHashType(signature) + if (hashType & Transaction.SIGHASH_NONE) return false // SIGHASH_NONE doesn't care about outputs + return true // SIGHASH_* does care + }) + }) } TransactionBuilder.prototype.__canModifyOutputs = function () { diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 62c410a..607e16a 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -2359,12 +2359,21 @@ ] }, { - "description": "Transaction w/ no outputs", + "description": "Transaction w/ no outputs (but 1 SIGHASH_ALL)", "exception": "Transaction needs outputs", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "vout": 0, + "signs": [ + { + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + } + ] + }, + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 1, "signs": [ { "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", From a58c5b4f5b38a91458e8b06e73b8bcc9b38eb5ee Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 26 Sep 2018 14:47:14 +1000 Subject: [PATCH 135/568] txb/tests: add solo SIGHASH_ALL fixes --- src/transaction_builder.js | 8 ++++++-- test/fixtures/transaction_builder.json | 22 ++++++++++++++++++++-- test/transaction_builder.js | 1 + 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 232c9a2..af238da 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -633,9 +633,9 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy // TODO: remove keyPair.network matching in 4.0.0 if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network') if (!this.__inputs[vin]) throw new Error('No input at index: ' + vin) - if (this.__needsOutputs()) throw new Error('Transaction needs outputs') hashType = hashType || Transaction.SIGHASH_ALL + if (this.__needsOutputs(hashType)) throw new Error('Transaction needs outputs') const input = this.__inputs[vin] @@ -709,7 +709,11 @@ TransactionBuilder.prototype.__canModifyInputs = function () { }) } -TransactionBuilder.prototype.__needsOutputs = function () { +TransactionBuilder.prototype.__needsOutputs = function (signingHashType) { + if (signingHashType === Transaction.SIGHASH_ALL) { + return this.__tx.outs.length === 0 + } + // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs // .build() will fail, but .buildIncomplete() is OK return (this.__tx.outs.length === 0) && this.__inputs.some((input) => { diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 607e16a..133fc2d 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -2359,7 +2359,7 @@ ] }, { - "description": "Transaction w/ no outputs (but 1 SIGHASH_ALL)", + "description": "Transaction w/ no outputs (but 1 SIGHASH_NONE)", "exception": "Transaction needs outputs", "inputs": [ { @@ -2367,7 +2367,8 @@ "vout": 0, "signs": [ { - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "hashType": 2 } ] }, @@ -2383,6 +2384,23 @@ } ], "outputs": [] + }, + { + "description": "Transaction w/ no outputs", + "exception": "Transaction needs outputs", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "signs": [ + { + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "throws": true + } + ] + } + ], + "outputs": [] } ], "fromTransaction": [ diff --git a/test/transaction_builder.js b/test/transaction_builder.js index bd0a3ab..85adcff 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -231,6 +231,7 @@ describe('TransactionBuilder', function () { it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', function () { txb.addInput(txHash, 0) + txb.addOutput(scripts[0], 1000) txb.sign(0, keyPair) assert.throws(function () { From d232545ac8728f5417de25ff4b06570d8f0775e3 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 3 Sep 2018 14:06:30 +1000 Subject: [PATCH 136/568] rename MULTISIG to P2MS --- src/classify.js | 6 +++--- src/transaction_builder.js | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/classify.js b/src/classify.js index 0b98fa6..8350e07 100644 --- a/src/classify.js +++ b/src/classify.js @@ -9,7 +9,7 @@ const witnessScriptHash = require('./templates/witnessscripthash') const witnessCommitment = require('./templates/witnesscommitment') const types = { - MULTISIG: 'multisig', + P2MS: 'multisig', NONSTANDARD: 'nonstandard', NULLDATA: 'nulldata', P2PK: 'pubkey', @@ -30,7 +30,7 @@ function classifyOutput (script) { const chunks = decompile(script) if (!chunks) throw new TypeError('Invalid script') - if (multisig.output.check(chunks)) return types.MULTISIG + if (multisig.output.check(chunks)) return types.P2MS if (pubKey.output.check(chunks)) return types.P2PK if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT if (nullData.output.check(chunks)) return types.NULLDATA @@ -45,7 +45,7 @@ function classifyInput (script, allowIncomplete) { if (pubKeyHash.input.check(chunks)) return types.P2PKH if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH - if (multisig.input.check(chunks, allowIncomplete)) return types.MULTISIG + if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS if (pubKey.input.check(chunks)) return types.P2PK return types.NONSTANDARD diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 8706841..a97df14 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -56,14 +56,14 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) { } } - case SCRIPT_TYPES.MULTISIG: { + case SCRIPT_TYPES.P2MS: { const { pubkeys, signatures } = payments.p2ms({ input: scriptSig, output: scriptPubKey }, { allowIncomplete: true }) return { - prevOutType: SCRIPT_TYPES.MULTISIG, + prevOutType: SCRIPT_TYPES.P2MS, pubkeys: pubkeys, signatures: signatures } @@ -126,7 +126,7 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) { // could be done in expandInput, but requires the original Transaction for hashForSignature function fixMultisigOrder (input, transaction, vin) { - if (input.redeemScriptType !== SCRIPT_TYPES.MULTISIG || !input.redeemScript) return + if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) return if (input.pubkeys.length === input.signatures.length) return const unmatched = input.signatures.concat() @@ -202,7 +202,7 @@ function expandOutput (script, ourPubKey) { } } - case SCRIPT_TYPES.MULTISIG: { + case SCRIPT_TYPES.P2MS: { const p2ms = payments.p2ms({ output: script }) return { type, @@ -392,7 +392,7 @@ function build (type, input, allowIncomplete) { return payments.p2pk({ signature: signatures[0] }) } - case SCRIPT_TYPES.MULTISIG: { + case SCRIPT_TYPES.P2MS: { if (allowIncomplete) { signatures = signatures.map(x => x || ops.OP_0) } else { From 1119a449a5ed41b10751c27621b482b812558262 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Mon, 3 Sep 2018 14:00:13 +1000 Subject: [PATCH 137/568] txbuilder: add missing signature P2MS fixture --- src/transaction_builder.js | 28 +++++++++++++++++--------- test/fixtures/transaction_builder.json | 23 +++++++++++++++++++++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index a97df14..861483f 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -57,7 +57,7 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) { } case SCRIPT_TYPES.P2MS: { - const { pubkeys, signatures } = payments.p2ms({ + const { m, pubkeys, signatures } = payments.p2ms({ input: scriptSig, output: scriptPubKey }, { allowIncomplete: true }) @@ -65,7 +65,8 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) { return { prevOutType: SCRIPT_TYPES.P2MS, pubkeys: pubkeys, - signatures: signatures + signatures: signatures, + maxSignatures: m } } } @@ -207,7 +208,8 @@ function expandOutput (script, ourPubKey) { return { type, pubkeys: p2ms.pubkeys, - signatures: p2ms.pubkeys.map(() => undefined) + signatures: p2ms.pubkeys.map(() => undefined), + maxSignatures: p2ms.m } } } @@ -250,7 +252,8 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri signType: expanded.type, pubkeys: expanded.pubkeys, - signatures: expanded.signatures + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures } } @@ -288,7 +291,8 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri signType: expanded.type, pubkeys: expanded.pubkeys, - signatures: expanded.signatures + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures } } @@ -321,7 +325,8 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri signType: expanded.type, pubkeys: expanded.pubkeys, - signatures: expanded.signatures + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures } } @@ -351,7 +356,8 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri signType: expanded.type, pubkeys: expanded.pubkeys, - signatures: expanded.signatures + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures } } @@ -365,7 +371,8 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri signType: SCRIPT_TYPES.P2PKH, pubkeys: [ourPubKey], - signatures: [undefined] + signatures: [undefined], + maxSignatures: 1 } } @@ -393,13 +400,14 @@ function build (type, input, allowIncomplete) { return payments.p2pk({ signature: signatures[0] }) } case SCRIPT_TYPES.P2MS: { + const m = input.maxSignatures if (allowIncomplete) { signatures = signatures.map(x => x || ops.OP_0) } else { signatures = signatures.filter(x => x) } - - return payments.p2ms({ signatures }, { allowIncomplete }) + const validate = !allowIncomplete || (m === signatures.length) + return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate }) } case SCRIPT_TYPES.P2SH: { const redeem = build(input.redeemScriptType, input, allowIncomplete) diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 4ddc5cb..40e3d74 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -1917,6 +1917,29 @@ } ] }, + { + "description": "Incomplete Transaction P2SH(P2MS 2/3), missing signature", + "exception": "Not enough signatures provided", + "network": "testnet", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "signs": [ + { + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG" + } + ] + } + ], + "outputs": [ + { + "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", + "value": 1000 + } + ] + }, { "description": "Duplicate transaction outs", "exception": "Duplicate TxOut: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:0", From 7c3d1f292d6914c355e5b3e8cbd5ea732bb735cd Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 26 Sep 2018 14:57:03 +1000 Subject: [PATCH 138/568] txb: add inline explanatory comment --- src/transaction_builder.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 861483f..3d1b2c5 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -406,6 +406,9 @@ function build (type, input, allowIncomplete) { } else { signatures = signatures.filter(x => x) } + + // if the transaction is not not complete (complete), or if signatures.length === m, validate + // otherwise, the number of OP_0's may be >= m, so don't validate (boo) const validate = !allowIncomplete || (m === signatures.length) return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate }) } From c8ae86b9ce728fbd6e982d111cf8dcdb5de1c43b Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 26 Sep 2018 15:02:18 +1000 Subject: [PATCH 139/568] refactor(txb): maxSignatures is optional --- src/transaction_builder.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 3d1b2c5..7411a29 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -371,8 +371,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri signType: SCRIPT_TYPES.P2PKH, pubkeys: [ourPubKey], - signatures: [undefined], - maxSignatures: 1 + signatures: [undefined] } } From 17c89fdc5eb9e508c372f91996bda8f1b183f3f3 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 26 Sep 2018 14:54:53 +0900 Subject: [PATCH 140/568] Move Transaction to ES6 --- src/transaction.js | 801 ++++++++++++++++++++++++--------------------- 1 file changed, 432 insertions(+), 369 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 751446f..c6a9909 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -7,35 +7,20 @@ const typeforce = require('typeforce') const types = require('./types') const varuint = require('varuint-bitcoin') -function varSliceSize (someScript) { +const varSliceSize = (someScript) => { const length = someScript.length return varuint.encodingLength(length) + length } -function vectorSize (someVector) { +const vectorSize = (someVector) => { const length = someVector.length - return varuint.encodingLength(length) + someVector.reduce(function (sum, witness) { + return varuint.encodingLength(length) + someVector.reduce((sum, witness) => { return sum + varSliceSize(witness) }, 0) } -function Transaction () { - this.version = 1 - this.locktime = 0 - this.ins = [] - this.outs = [] -} - -Transaction.DEFAULT_SEQUENCE = 0xffffffff -Transaction.SIGHASH_ALL = 0x01 -Transaction.SIGHASH_NONE = 0x02 -Transaction.SIGHASH_SINGLE = 0x03 -Transaction.SIGHASH_ANYONECANPAY = 0x80 -Transaction.ADVANCED_TRANSACTION_MARKER = 0x00 -Transaction.ADVANCED_TRANSACTION_FLAG = 0x01 - const EMPTY_SCRIPT = Buffer.allocUnsafe(0) const EMPTY_WITNESS = [] const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') @@ -46,42 +31,460 @@ const BLANK_OUTPUT = { valueBuffer: VALUE_UINT64_MAX } -Transaction.fromBuffer = function (buffer, __noStrict) { +class Transaction { + constructor () { + this.version = 1 + this.locktime = 0 + this.ins = [] + this.outs = [] + } + + static get DEFAULT_SEQUENCE () { + return 0xffffffff + } + static get SIGHASH_ALL () { + return 0x01 + } + static get SIGHASH_NONE () { + return 0x02 + } + static get SIGHASH_SINGLE () { + return 0x03 + } + static get SIGHASH_ANYONECANPAY () { + return 0x80 + } + static get ADVANCED_TRANSACTION_MARKER () { + return 0x00 + } + static get ADVANCED_TRANSACTION_FLAG () { + return 0x01 + } + + isCoinbase () { + return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) + } + + addInput (hash, index, sequence, scriptSig) { + typeforce(types.tuple( + types.Hash256bit, + types.UInt32, + types.maybe(types.UInt32), + types.maybe(types.Buffer) + ), arguments) + + if (types.Null(sequence)) { + sequence = Transaction.DEFAULT_SEQUENCE + } + + // Add the input and return the input's index + return (this.ins.push({ + hash: hash, + index: index, + script: scriptSig || EMPTY_SCRIPT, + sequence: sequence, + witness: EMPTY_WITNESS + }) - 1) + } + + addOutput (scriptPubKey, value) { + typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) + + // Add the output and return the output's index + return (this.outs.push({ + script: scriptPubKey, + value: value + }) - 1) + } + + hasWitnesses () { + return this.ins.some((x) => { + return x.witness.length !== 0 + }) + } + + weight () { + const base = this.__byteLength(false) + const total = this.__byteLength(true) + return base * 3 + total + } + + virtualSize () { + return Math.ceil(this.weight() / 4) + } + + byteLength () { + return this.__byteLength(true) + } + + __byteLength (__allowWitness) { + const hasWitnesses = __allowWitness && this.hasWitnesses() + + return ( + (hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script) + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script) + }, 0) + + (hasWitnesses ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness) + }, 0) : 0) + ) + } + + clone () { + const newTx = new Transaction() + newTx.version = this.version + newTx.locktime = this.locktime + + newTx.ins = this.ins.map((txIn) => { + return { + hash: txIn.hash, + index: txIn.index, + script: txIn.script, + sequence: txIn.sequence, + witness: txIn.witness + } + }) + + newTx.outs = this.outs.map((txOut) => { + return { + script: txOut.script, + value: txOut.value + } + }) + + return newTx + } + + /** + * Hash transaction for signing a specific input. + * + * Bitcoin uses a different hash for each signed transaction input. + * This method copies the transaction, makes the necessary changes based on the + * hashType, and then hashes the result. + * This hash can then be used to sign the provided transaction input. + */ + hashForSignature (inIndex, prevOutScript, hashType) { + typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) + + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 + if (inIndex >= this.ins.length) return ONE + + // ignore OP_CODESEPARATOR + const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter((x) => { + return x !== opcodes.OP_CODESEPARATOR + })) + + const txTmp = this.clone() + + // SIGHASH_NONE: ignore all outputs? (wildcard payee) + if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { + txTmp.outs = [] + + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, i) => { + if (i === inIndex) return + + input.sequence = 0 + }) + + // SIGHASH_SINGLE: ignore all outputs, except at the same index? + } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 + if (inIndex >= this.outs.length) return ONE + + // truncate outputs after + txTmp.outs.length = inIndex + 1 + + // "blank" outputs before + for (var i = 0; i < inIndex; i++) { + txTmp.outs[i] = BLANK_OUTPUT + } + + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, y) => { + if (y === inIndex) return + + input.sequence = 0 + }) + } + + // SIGHASH_ANYONECANPAY: ignore inputs entirely? + if (hashType & Transaction.SIGHASH_ANYONECANPAY) { + txTmp.ins = [txTmp.ins[inIndex]] + txTmp.ins[0].script = ourScript + + // SIGHASH_ALL: only ignore input scripts + } else { + // "blank" others input scripts + txTmp.ins.forEach((input) => { + input.script = EMPTY_SCRIPT + }) + txTmp.ins[inIndex].script = ourScript + } + + // serialize and hash + const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) + buffer.writeInt32LE(hashType, buffer.length - 4) + txTmp.__toBuffer(buffer, 0, false) + + return bcrypto.hash256(buffer) + } + + hashForWitnessV0 (inIndex, prevOutScript, value, hashType) { + typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) + + let tbuffer, toffset + + const writeSlice = (slice) => { + toffset += slice.copy(tbuffer, toffset) + } + + const writeUInt32 = (i) => { + toffset = tbuffer.writeUInt32LE(i, toffset) + } + + const writeUInt64 = (i) => { + toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) + } + + const writeVarInt = (i) => { + varuint.encode(i, tbuffer, toffset) + toffset += varuint.encode.bytes + } + + const writeVarSlice = (slice) => { + writeVarInt(slice.length) + writeSlice(slice) + } + + let hashOutputs = ZERO + let hashPrevouts = ZERO + let hashSequence = ZERO + + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { + tbuffer = Buffer.allocUnsafe(36 * this.ins.length) + toffset = 0 + + this.ins.forEach((txIn) => { + writeSlice(txIn.hash) + writeUInt32(txIn.index) + }) + + hashPrevouts = bcrypto.hash256(tbuffer) + } + + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && + (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + tbuffer = Buffer.allocUnsafe(4 * this.ins.length) + toffset = 0 + + this.ins.forEach((txIn) => { + writeUInt32(txIn.sequence) + }) + + hashSequence = bcrypto.hash256(tbuffer) + } + + if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + const txOutsSize = this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script) + }, 0) + + tbuffer = Buffer.allocUnsafe(txOutsSize) + toffset = 0 + + this.outs.forEach((out) => { + writeUInt64(out.value) + writeVarSlice(out.script) + }) + + hashOutputs = bcrypto.hash256(tbuffer) + } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { + const output = this.outs[inIndex] + + tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) + toffset = 0 + writeUInt64(output.value) + writeVarSlice(output.script) + + hashOutputs = bcrypto.hash256(tbuffer) + } + + tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) + toffset = 0 + + const input = this.ins[inIndex] + writeUInt32(this.version) + writeSlice(hashPrevouts) + writeSlice(hashSequence) + writeSlice(input.hash) + writeUInt32(input.index) + writeVarSlice(prevOutScript) + writeUInt64(value) + writeUInt32(input.sequence) + writeSlice(hashOutputs) + writeUInt32(this.locktime) + writeUInt32(hashType) + return bcrypto.hash256(tbuffer) + } + + getHash () { + return bcrypto.hash256(this.__toBuffer(undefined, undefined, false)) + } + + getId () { + // transaction hash's are displayed in reverse order + return this.getHash().reverse().toString('hex') + } + + toBuffer (buffer, initialOffset) { + return this.__toBuffer(buffer, initialOffset, true) + } + + __toBuffer (buffer, initialOffset, __allowWitness) { + if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)) + + let offset = initialOffset || 0 + + const writeSlice = (slice) => { + offset += slice.copy(buffer, offset) + } + + const writeUInt8 = (i) => { + offset = buffer.writeUInt8(i, offset) + } + + const writeUInt32 = (i) => { + offset = buffer.writeUInt32LE(i, offset) + } + + const writeInt32 = (i) => { + offset = buffer.writeInt32LE(i, offset) + } + + const writeUInt64 = (i) => { + offset = bufferutils.writeUInt64LE(buffer, i, offset) + } + + const writeVarInt = (i) => { + varuint.encode(i, buffer, offset) + offset += varuint.encode.bytes + } + + const writeVarSlice = (slice) => { + writeVarInt(slice.length) + writeSlice(slice) + } + + const writeVector = (vector) => { + writeVarInt(vector.length) + vector.forEach(writeVarSlice) + } + + writeInt32(this.version) + + const hasWitnesses = __allowWitness && this.hasWitnesses() + + if (hasWitnesses) { + writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) + writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) + } + + writeVarInt(this.ins.length) + + this.ins.forEach((txIn) => { + writeSlice(txIn.hash) + writeUInt32(txIn.index) + writeVarSlice(txIn.script) + writeUInt32(txIn.sequence) + }) + + writeVarInt(this.outs.length) + this.outs.forEach((txOut) => { + if (!txOut.valueBuffer) { + writeUInt64(txOut.value) + } else { + writeSlice(txOut.valueBuffer) + } + + writeVarSlice(txOut.script) + }) + + if (hasWitnesses) { + this.ins.forEach((input) => { + writeVector(input.witness) + }) + } + + writeUInt32(this.locktime) + + // avoid slicing unless necessary + if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) + return buffer + } + + toHex () { + return this.toBuffer().toString('hex') + } + + setInputScript (index, scriptSig) { + typeforce(types.tuple(types.Number, types.Buffer), arguments) + + this.ins[index].script = scriptSig + } + + setWitness (index, witness) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments) + + this.ins[index].witness = witness + } +} + +Transaction.fromBuffer = (buffer, __noStrict) => { let offset = 0 - function readSlice (n) { + + const readSlice = (n) => { offset += n return buffer.slice(offset - n, offset) } - function readUInt32 () { + const readUInt32 = () => { const i = buffer.readUInt32LE(offset) offset += 4 return i } - function readInt32 () { + const readInt32 = () => { const i = buffer.readInt32LE(offset) offset += 4 return i } - function readUInt64 () { + const readUInt64 = () => { const i = bufferutils.readUInt64LE(buffer, offset) offset += 8 return i } - function readVarInt () { + const readVarInt = () => { const vi = varuint.decode(buffer, offset) offset += varuint.decode.bytes return vi } - function readVarSlice () { + const readVarSlice = () => { return readSlice(readVarInt()) } - function readVector () { + const readVector = () => { const count = readVarInt() const vector = [] for (var i = 0; i < count; i++) vector.push(readVarSlice()) @@ -96,7 +499,7 @@ Transaction.fromBuffer = function (buffer, __noStrict) { let hasWitnesses = false if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && - flag === Transaction.ADVANCED_TRANSACTION_FLAG) { + flag === Transaction.ADVANCED_TRANSACTION_FLAG) { offset += 2 hasWitnesses = true } @@ -137,11 +540,11 @@ Transaction.fromBuffer = function (buffer, __noStrict) { return tx } -Transaction.fromHex = function (hex) { +Transaction.fromHex = (hex) => { return Transaction.fromBuffer(Buffer.from(hex, 'hex')) } -Transaction.isCoinbaseHash = function (buffer) { +Transaction.isCoinbaseHash = (buffer) => { typeforce(types.Hash256bit, buffer) for (var i = 0; i < 32; ++i) { if (buffer[i] !== 0) return false @@ -149,344 +552,4 @@ Transaction.isCoinbaseHash = function (buffer) { return true } -Transaction.prototype.isCoinbase = function () { - return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) -} - -Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) { - typeforce(types.tuple( - types.Hash256bit, - types.UInt32, - types.maybe(types.UInt32), - types.maybe(types.Buffer) - ), arguments) - - if (types.Null(sequence)) { - sequence = Transaction.DEFAULT_SEQUENCE - } - - // Add the input and return the input's index - return (this.ins.push({ - hash: hash, - index: index, - script: scriptSig || EMPTY_SCRIPT, - sequence: sequence, - witness: EMPTY_WITNESS - }) - 1) -} - -Transaction.prototype.addOutput = function (scriptPubKey, value) { - typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) - - // Add the output and return the output's index - return (this.outs.push({ - script: scriptPubKey, - value: value - }) - 1) -} - -Transaction.prototype.hasWitnesses = function () { - return this.ins.some(function (x) { - return x.witness.length !== 0 - }) -} - -Transaction.prototype.weight = function () { - const base = this.__byteLength(false) - const total = this.__byteLength(true) - return base * 3 + total -} - -Transaction.prototype.virtualSize = function () { - return Math.ceil(this.weight() / 4) -} - -Transaction.prototype.byteLength = function () { - return this.__byteLength(true) -} - -Transaction.prototype.__byteLength = function (__allowWitness) { - const hasWitnesses = __allowWitness && this.hasWitnesses() - - return ( - (hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce(function (sum, input) { return sum + 40 + varSliceSize(input.script) }, 0) + - this.outs.reduce(function (sum, output) { return sum + 8 + varSliceSize(output.script) }, 0) + - (hasWitnesses ? this.ins.reduce(function (sum, input) { return sum + vectorSize(input.witness) }, 0) : 0) - ) -} - -Transaction.prototype.clone = function () { - const newTx = new Transaction() - newTx.version = this.version - newTx.locktime = this.locktime - - newTx.ins = this.ins.map(function (txIn) { - return { - hash: txIn.hash, - index: txIn.index, - script: txIn.script, - sequence: txIn.sequence, - witness: txIn.witness - } - }) - - newTx.outs = this.outs.map(function (txOut) { - return { - script: txOut.script, - value: txOut.value - } - }) - - return newTx -} - -/** - * Hash transaction for signing a specific input. - * - * Bitcoin uses a different hash for each signed transaction input. - * This method copies the transaction, makes the necessary changes based on the - * hashType, and then hashes the result. - * This hash can then be used to sign the provided transaction input. - */ -Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) - - // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 - if (inIndex >= this.ins.length) return ONE - - // ignore OP_CODESEPARATOR - const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) { - return x !== opcodes.OP_CODESEPARATOR - })) - - const txTmp = this.clone() - - // SIGHASH_NONE: ignore all outputs? (wildcard payee) - if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { - txTmp.outs = [] - - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach(function (input, i) { - if (i === inIndex) return - - input.sequence = 0 - }) - - // SIGHASH_SINGLE: ignore all outputs, except at the same index? - } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { - // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 - if (inIndex >= this.outs.length) return ONE - - // truncate outputs after - txTmp.outs.length = inIndex + 1 - - // "blank" outputs before - for (var i = 0; i < inIndex; i++) { - txTmp.outs[i] = BLANK_OUTPUT - } - - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach(function (input, y) { - if (y === inIndex) return - - input.sequence = 0 - }) - } - - // SIGHASH_ANYONECANPAY: ignore inputs entirely? - if (hashType & Transaction.SIGHASH_ANYONECANPAY) { - txTmp.ins = [txTmp.ins[inIndex]] - txTmp.ins[0].script = ourScript - - // SIGHASH_ALL: only ignore input scripts - } else { - // "blank" others input scripts - txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT }) - txTmp.ins[inIndex].script = ourScript - } - - // serialize and hash - const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) - buffer.writeInt32LE(hashType, buffer.length - 4) - txTmp.__toBuffer(buffer, 0, false) - - return bcrypto.hash256(buffer) -} - -Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) - - let tbuffer, toffset - function writeSlice (slice) { toffset += slice.copy(tbuffer, toffset) } - function writeUInt32 (i) { toffset = tbuffer.writeUInt32LE(i, toffset) } - function writeUInt64 (i) { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) } - function writeVarInt (i) { - varuint.encode(i, tbuffer, toffset) - toffset += varuint.encode.bytes - } - function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } - - let hashOutputs = ZERO - let hashPrevouts = ZERO - let hashSequence = ZERO - - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { - tbuffer = Buffer.allocUnsafe(36 * this.ins.length) - toffset = 0 - - this.ins.forEach(function (txIn) { - writeSlice(txIn.hash) - writeUInt32(txIn.index) - }) - - hashPrevouts = bcrypto.hash256(tbuffer) - } - - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && - (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - tbuffer = Buffer.allocUnsafe(4 * this.ins.length) - toffset = 0 - - this.ins.forEach(function (txIn) { - writeUInt32(txIn.sequence) - }) - - hashSequence = bcrypto.hash256(tbuffer) - } - - if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - const txOutsSize = this.outs.reduce(function (sum, output) { - return sum + 8 + varSliceSize(output.script) - }, 0) - - tbuffer = Buffer.allocUnsafe(txOutsSize) - toffset = 0 - - this.outs.forEach(function (out) { - writeUInt64(out.value) - writeVarSlice(out.script) - }) - - hashOutputs = bcrypto.hash256(tbuffer) - } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { - const output = this.outs[inIndex] - - tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) - toffset = 0 - writeUInt64(output.value) - writeVarSlice(output.script) - - hashOutputs = bcrypto.hash256(tbuffer) - } - - tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) - toffset = 0 - - const input = this.ins[inIndex] - writeUInt32(this.version) - writeSlice(hashPrevouts) - writeSlice(hashSequence) - writeSlice(input.hash) - writeUInt32(input.index) - writeVarSlice(prevOutScript) - writeUInt64(value) - writeUInt32(input.sequence) - writeSlice(hashOutputs) - writeUInt32(this.locktime) - writeUInt32(hashType) - return bcrypto.hash256(tbuffer) -} - -Transaction.prototype.getHash = function () { - return bcrypto.hash256(this.__toBuffer(undefined, undefined, false)) -} - -Transaction.prototype.getId = function () { - // transaction hash's are displayed in reverse order - return this.getHash().reverse().toString('hex') -} - -Transaction.prototype.toBuffer = function (buffer, initialOffset) { - return this.__toBuffer(buffer, initialOffset, true) -} - -Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitness) { - if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)) - - let offset = initialOffset || 0 - function writeSlice (slice) { offset += slice.copy(buffer, offset) } - function writeUInt8 (i) { offset = buffer.writeUInt8(i, offset) } - function writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) } - function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) } - function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) } - function writeVarInt (i) { - varuint.encode(i, buffer, offset) - offset += varuint.encode.bytes - } - function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } - function writeVector (vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice) } - - writeInt32(this.version) - - const hasWitnesses = __allowWitness && this.hasWitnesses() - - if (hasWitnesses) { - writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) - writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) - } - - writeVarInt(this.ins.length) - - this.ins.forEach(function (txIn) { - writeSlice(txIn.hash) - writeUInt32(txIn.index) - writeVarSlice(txIn.script) - writeUInt32(txIn.sequence) - }) - - writeVarInt(this.outs.length) - this.outs.forEach(function (txOut) { - if (!txOut.valueBuffer) { - writeUInt64(txOut.value) - } else { - writeSlice(txOut.valueBuffer) - } - - writeVarSlice(txOut.script) - }) - - if (hasWitnesses) { - this.ins.forEach(function (input) { - writeVector(input.witness) - }) - } - - writeUInt32(this.locktime) - - // avoid slicing unless necessary - if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) - return buffer -} - -Transaction.prototype.toHex = function () { - return this.toBuffer().toString('hex') -} - -Transaction.prototype.setInputScript = function (index, scriptSig) { - typeforce(types.tuple(types.Number, types.Buffer), arguments) - - this.ins[index].script = scriptSig -} - -Transaction.prototype.setWitness = function (index, witness) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments) - - this.ins[index].witness = witness -} - module.exports = Transaction From df2a66c852546a273f33d7a997c46945202b7155 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 26 Sep 2018 16:02:39 +1000 Subject: [PATCH 141/568] tests/payments: fix redeem fixture mutation --- test/payments.utils.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/payments.utils.js b/test/payments.utils.js index 219d086..485bf03 100644 --- a/test/payments.utils.js +++ b/test/payments.utils.js @@ -1,6 +1,6 @@ const t = require('assert') const bscript = require('../src/script') -const bnetworks = require('../src/networks') +const BNETWORKS = require('../src/networks') function tryHex (x) { if (Buffer.isBuffer(x)) return x.toString('hex') @@ -58,7 +58,7 @@ function equate (a, b, args) { equateBase(a, b, '') if (b.redeem) equateBase(a.redeem, b.redeem, 'redeem.') - if (b.network) t.deepEqual(a.network, b.network, 'Inequal *.network') + if (b.network) t.deepEqual(a.network, BNETWORKS[b.network], 'Inequal *.network') // contextual if (b.signature === null) b.signature = undefined @@ -76,7 +76,7 @@ function equate (a, b, args) { function preform (x) { x = Object.assign({}, x) - if (x.network) x.network = bnetworks[x.network] + if (x.network) x.network = BNETWORKS[x.network] if (typeof x.inputHex === 'string') { x.input = Buffer.from(x.inputHex, 'hex') delete x.inputHex @@ -96,10 +96,11 @@ function preform (x) { if (x.pubkeys) x.pubkeys = x.pubkeys.map(fromHex) if (x.signatures) x.signatures = x.signatures.map(function (y) { return Number.isFinite(y) ? y : Buffer.from(y, 'hex') }) if (x.redeem) { + x.redeem = Object.assign({}, x.redeem) if (typeof x.redeem.input === 'string') x.redeem.input = asmToBuffer(x.redeem.input) if (typeof x.redeem.output === 'string') x.redeem.output = asmToBuffer(x.redeem.output) if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(fromHex) - if (x.redeem.network) x.redeem.network = bnetworks[x.redeem.network] + if (x.redeem.network) x.redeem.network = BNETWORKS[x.redeem.network] } return x From 6927f74b7aafc046bbca80ebe8658186149c598a Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 26 Sep 2018 15:44:55 +1000 Subject: [PATCH 142/568] add inverted Network mismatch tests --- test/fixtures/p2sh.json | 18 ++++++++++++++++++ test/fixtures/p2wsh.json | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/test/fixtures/p2sh.json b/test/fixtures/p2sh.json index e6fe2ae..e447e4a 100644 --- a/test/fixtures/p2sh.json +++ b/test/fixtures/p2sh.json @@ -325,6 +325,24 @@ } } }, + { + "exception": "Network mismatch", + "arguments": { + "network": "bitcoin", + "redeem": { + "network": "testnet" + } + } + }, + { + "exception": "Network mismatch", + "arguments": { + "network": "testnet", + "redeem": { + "network": "bitcoin" + } + } + }, { "exception": "Empty input", "arguments": { diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json index 0870278..2dfec75 100644 --- a/test/fixtures/p2wsh.json +++ b/test/fixtures/p2wsh.json @@ -306,6 +306,15 @@ } } }, + { + "exception": "Network mismatch", + "arguments": { + "network": "testnet", + "redeem": { + "network": "bitcoin" + } + } + }, { "exception": "Invalid prefix or Network mismatch", "arguments": { From 1b4dc48d75c65c854c23fa7abff7976b7814615a Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 26 Sep 2018 16:02:13 +1000 Subject: [PATCH 143/568] add failing tests for redeem.network derivation --- test/fixtures/p2sh.json | 18 ++++++++++++++++++ test/fixtures/p2wsh.json | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/test/fixtures/p2sh.json b/test/fixtures/p2sh.json index e447e4a..595b85e 100644 --- a/test/fixtures/p2sh.json +++ b/test/fixtures/p2sh.json @@ -166,6 +166,24 @@ ] } } + }, + { + "description": "p2sh-p2pkh, out (network derived from redeem)", + "arguments": { + "redeem": { + "address": "this is P2PKH context, unknown and ignored by P2SH", + "output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", + "network": "testnet" + } + }, + "expected": { + "address": "2N7nfc7zeWuADtpdR4MrR7Wq3dzr7LxTCgS", + "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", + "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", + "input": null, + "witness": null, + "network": "testnet" + } } ], "invalid": [ diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json index 2dfec75..e5ce0e0 100644 --- a/test/fixtures/p2wsh.json +++ b/test/fixtures/p2wsh.json @@ -179,6 +179,24 @@ ] } } + }, + { + "description": "p2wsh-p2pkh, out (network derived from redeem)", + "arguments": { + "redeem": { + "address": "this is P2PKH context, unknown and ignored by p2wsh", + "output": "OP_DUP OP_HASH160 c30afa58ae0673b00a45b5c17dff4633780f1400 OP_EQUALVERIFY OP_CHECKSIG", + "network": "testnet" + } + }, + "expected": { + "address": "tb1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ssaskm8v", + "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", + "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", + "input": null, + "witness": null, + "network": "testnet" + } } ], "invalid": [ From a976fba27b45c742207b7081565a4e35a3282c9f Mon Sep 17 00:00:00 2001 From: Daniel Cousens <github@dcousens.com> Date: Wed, 26 Sep 2018 16:02:52 +1000 Subject: [PATCH 144/568] add network derivation from redeem.network --- src/payments/p2sh.js | 6 +++++- src/payments/p2wsh.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index 4741936..0729c8b 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -45,7 +45,11 @@ function p2sh (a, opts) { witness: typef.maybe(typef.arrayOf(typef.Buffer)) }, a) - const network = a.network || BITCOIN_NETWORK + let network = a.network + if (!network) { + network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK + } + const o = { network } const _address = lazy.value(function () { diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index a26e706..d8e6e8a 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -59,7 +59,11 @@ function p2wsh (a, opts) { }) const _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input) }) - const network = a.network || BITCOIN_NETWORK + let network = a.network + if (!network) { + network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK + } + const o = { network } lazy.prop(o, 'address', function () { From a88aa3334876ba13b23a8dd348958148be8d7309 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 26 Sep 2018 16:30:33 +0900 Subject: [PATCH 145/568] Revert function hoisting removal --- src/transaction.js | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index c6a9909..e1be629 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -7,13 +7,13 @@ const typeforce = require('typeforce') const types = require('./types') const varuint = require('varuint-bitcoin') -const varSliceSize = (someScript) => { +function varSliceSize (someScript) { const length = someScript.length return varuint.encodingLength(length) + length } -const vectorSize = (someVector) => { +function vectorSize (someVector) { const length = someVector.length return varuint.encodingLength(length) + someVector.reduce((sum, witness) => { @@ -241,24 +241,24 @@ class Transaction { let tbuffer, toffset - const writeSlice = (slice) => { + function writeSlice (slice) { toffset += slice.copy(tbuffer, toffset) } - const writeUInt32 = (i) => { + function writeUInt32 (i) { toffset = tbuffer.writeUInt32LE(i, toffset) } - const writeUInt64 = (i) => { + function writeUInt64 (i) { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) } - const writeVarInt = (i) => { + function writeVarInt (i) { varuint.encode(i, tbuffer, toffset) toffset += varuint.encode.bytes } - const writeVarSlice = (slice) => { + function writeVarSlice (slice) { writeVarInt(slice.length) writeSlice(slice) } @@ -354,37 +354,37 @@ class Transaction { let offset = initialOffset || 0 - const writeSlice = (slice) => { + function writeSlice (slice) { offset += slice.copy(buffer, offset) } - const writeUInt8 = (i) => { + function writeUInt8 (i) { offset = buffer.writeUInt8(i, offset) } - const writeUInt32 = (i) => { + function writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) } - const writeInt32 = (i) => { + function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) } - const writeUInt64 = (i) => { + function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) } - const writeVarInt = (i) => { + function writeVarInt (i) { varuint.encode(i, buffer, offset) offset += varuint.encode.bytes } - const writeVarSlice = (slice) => { + function writeVarSlice (slice) { writeVarInt(slice.length) writeSlice(slice) } - const writeVector = (vector) => { + function writeVector (vector) { writeVarInt(vector.length) vector.forEach(writeVarSlice) } @@ -451,40 +451,40 @@ class Transaction { Transaction.fromBuffer = (buffer, __noStrict) => { let offset = 0 - const readSlice = (n) => { + function readSlice (n) { offset += n return buffer.slice(offset - n, offset) } - const readUInt32 = () => { + function readUInt32 () { const i = buffer.readUInt32LE(offset) offset += 4 return i } - const readInt32 = () => { + function readInt32 () { const i = buffer.readInt32LE(offset) offset += 4 return i } - const readUInt64 = () => { + function readUInt64 () { const i = bufferutils.readUInt64LE(buffer, offset) offset += 8 return i } - const readVarInt = () => { + function readVarInt () { const vi = varuint.decode(buffer, offset) offset += varuint.decode.bytes return vi } - const readVarSlice = () => { + function readVarSlice () { return readSlice(readVarInt()) } - const readVector = () => { + function readVector () { const count = readVarInt() const vector = [] for (var i = 0; i < count; i++) vector.push(readVarSlice()) From b93d9d99ccc240d3e022f1a680024469fcc04cf5 Mon Sep 17 00:00:00 2001 From: Apichan Chaiyutthasat <ak.apichan1103@gmail.com> Date: Sat, 6 Oct 2018 13:13:40 +0700 Subject: [PATCH 146/568] Update addresses.js --- test/integration/addresses.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 4bd71c8..9ad7502 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -19,6 +19,7 @@ function rng () { return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } describe('bitcoinjs-lib (addresses)', function () { it('can generate a random address', function () { + // in production: const keyPair = bitcoin.ECPair.makeRandom({}) const keyPair = bitcoin.ECPair.makeRandom({ rng: rng }) const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) @@ -121,6 +122,7 @@ describe('bitcoinjs-lib (addresses)', function () { // other networks it('can generate a Testnet address', function () { const testnet = bitcoin.networks.testnet + // in production: const keyPair = bitcoin.ECPair.makeRandom({ network: testnet }) const keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: rng }) const wif = keyPair.toWIF() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: testnet }) @@ -130,6 +132,7 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can generate a Litecoin address', function () { + // in production: const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }) const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: rng }) const wif = keyPair.toWIF() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: LITECOIN }) From 44a98c0fa6487eaf81500427366787a953ff890d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 15 Nov 2018 15:32:03 +0900 Subject: [PATCH 147/568] Add regtest network --- src/networks.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/networks.js b/src/networks.js index b168f7a..fb3957c 100644 --- a/src/networks.js +++ b/src/networks.js @@ -13,6 +13,17 @@ module.exports = { scriptHash: 0x05, wif: 0x80 }, + regtest: { + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'bcrt', + bip32: { + public: 0x043587cf, + private: 0x04358394 + }, + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef + }, testnet: { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'tb', From 28bfb71b5406646746fe4b591d8c697bcf704d28 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 15 Nov 2018 15:44:03 +0900 Subject: [PATCH 148/568] Add tests for regtest bech32 address --- test/fixtures/address.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/fixtures/address.json b/test/fixtures/address.json index 2b7e5fa..2668a08 100644 --- a/test/fixtures/address.json +++ b/test/fixtures/address.json @@ -63,6 +63,20 @@ "bech32": "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", "data": "000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", "script": "OP_0 000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433" + }, + { + "network": "regtest", + "version": 0, + "bech32": "bcrt1qjh3dnrafy8f2zszh5sdqn6c3ycfljh930yza9nt72v30dkw8mlvscn82zx", + "data": "95e2d98fa921d2a14057a41a09eb112613f95cb17905d2cd7e5322f6d9c7dfd9", + "script": "OP_0 95e2d98fa921d2a14057a41a09eb112613f95cb17905d2cd7e5322f6d9c7dfd9" + }, + { + "network": "regtest", + "version": 0, + "bech32": "bcrt1qqqqqqqqqqqqqqahrwf6d62emdxmpq8gu3xe9au9fjwc9sxxn4k2qujfh7u", + "data": "000000000000000076e37274dd2b3b69b6101d1c89b25ef0a993b05818d3ad94", + "script": "OP_0 000000000000000076e37274dd2b3b69b6101d1c89b25ef0a993b05818d3ad94" } ], "bech32": [ @@ -179,4 +193,3 @@ ] } } - From bd0be2f3430e7a5a81b776129befc004b48635da Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 15 Nov 2018 18:52:16 +0900 Subject: [PATCH 149/568] Fix errors for bitcoin core 0.17.0 --- test/integration/cltv.js | 2 +- test/integration/csv.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index e4a1133..2c39b2c 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -212,7 +212,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { regtestUtils.broadcast(tx.toHex(), function (err) { assert.throws(function () { if (err) throw err - }, /Error: 64: non-final/) + }, /Error: non-final \(code 64\)/) done() }) diff --git a/test/integration/csv.js b/test/integration/csv.js index 1662a1a..5996634 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -132,7 +132,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { regtestUtils.broadcast(tx.toHex(), function (err) { assert.throws(function () { if (err) throw err - }, /Error: 64: non-BIP68-final/) + }, /Error: non-BIP68-final \(code 64\)/) done() }) From 9c409b1274f0b671099214963a3638097ce83d77 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 20 Nov 2018 16:28:35 +0900 Subject: [PATCH 150/568] Change regtest server over to regtest.bitbank.cc --- test/integration/_regtest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 059cec2..f392a8a 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -3,12 +3,12 @@ const bitcoin = require('../../') const dhttp = require('dhttp/200') const APIPASS = process.env.APIPASS || 'satoshi' -const APIURL = 'https://api.dcousens.cloud/1' +const APIURL = 'https://regtest.bitbank.cc/1' const NETWORK = bitcoin.networks.testnet function broadcast (txHex, callback) { dhttp({ - method: 'PUT', + method: 'POST', url: APIURL + '/t/push', body: txHex }, callback) From 1e05fa085e5f4dfcec9a076d19dd08df0211e552 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <413395+dcousens@users.noreply.github.com> Date: Tue, 27 Nov 2018 01:20:08 +0000 Subject: [PATCH 151/568] README: emphasis dependencies --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a7b5b8..bbd40ae 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Master is not stable; it is our development branch, and [only tagged releases ma ## Can I trust this code? > Don't trust. Verify. -We recommend every user of this library and the [bitcoinjs](https://github.com/bitcoinjs) ecosystem audit and verify any underlying code for its validity and suitability. +We recommend every user of this library and the [bitcoinjs](https://github.com/bitcoinjs) ecosystem audit and verify any underlying code for its validity and suitability, including the dependencies. Mistakes and bugs happen, but with your help in resolving and reporting [issues](https://github.com/bitcoinjs/bitcoinjs-lib/issues), together we can produce open source software that is: From 661ec6b9184b8663d80c08c7ee14b68d72cfc12e Mon Sep 17 00:00:00 2001 From: Daniel Cousens <413395+dcousens@users.noreply.github.com> Date: Tue, 27 Nov 2018 01:21:01 +0000 Subject: [PATCH 152/568] README: more emphasis --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbd40ae..2d8e9bf 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Master is not stable; it is our development branch, and [only tagged releases ma ## Can I trust this code? > Don't trust. Verify. -We recommend every user of this library and the [bitcoinjs](https://github.com/bitcoinjs) ecosystem audit and verify any underlying code for its validity and suitability, including the dependencies. +We recommend every user of this library and the [bitcoinjs](https://github.com/bitcoinjs) ecosystem audit and verify any underlying code for its validity and suitability, including reviewing any and all of your project's dependencies. Mistakes and bugs happen, but with your help in resolving and reporting [issues](https://github.com/bitcoinjs/bitcoinjs-lib/issues), together we can produce open source software that is: From 06342a830fc963830b32df7358523d037302bdc6 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <413395+dcousens@users.noreply.github.com> Date: Tue, 27 Nov 2018 03:48:57 +0000 Subject: [PATCH 153/568] README: upgrade iOS warning --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d8e9bf..3ca8866 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ If you're familiar with how to use browserify, ignore this and carry on, otherwi **NOTE**: We use Node Maintenance LTS features, if you need strict ES5, use [`--transform babelify`](https://github.com/babel/babelify) in conjunction with your `browserify` step (using an [`es2015`](http://babeljs.io/docs/plugins/preset-es2015/) preset). -**NOTE**: If you expect this library to run on an iOS 10 device, ensure that you are using [buffer@5.0.5](https://github.com/feross/buffer/pull/155) or greater. +**WARNING**: iOS devices have [problems](https://github.com/feross/buffer/issues/136), use atleast [buffer@5.0.5](https://github.com/feross/buffer/pull/155) or greater, and enforce the test suites (for `Buffer`, and any other dependency) pass before use. ### Typescript or VSCode users Type declarations for Typescript [are available](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/0897921174860ec3d5318992d2323b3ae8100a68/types/bitcoinjs-lib) for version `^3.0.0` of the library. @@ -74,7 +74,7 @@ npm install -g flow-typed flow-typed install -f 0.27 bitcoinjs-lib@2.2.0 ``` -These definitions are maintained by [@runn1ng](https://github.com/runn1ng). +These flow bindings are not maintained by the maintainers of this repository. ## Examples From 47b5dfb4688638163daacc586cb3b720c5a06621 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <413395+dcousens@users.noreply.github.com> Date: Tue, 27 Nov 2018 03:51:08 +0000 Subject: [PATCH 154/568] README: add warning to flow type bindings --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ca8866..e8866c9 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ npm install -g flow-typed flow-typed install -f 0.27 bitcoinjs-lib@2.2.0 ``` -These flow bindings are not maintained by the maintainers of this repository. +**WARNING**: These flow-typed definitions are not maintained by the maintainers of this repository. ## Examples From 1b5d1fadba50f5ce795a58b38efb30db652fa693 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Wed, 26 Sep 2018 10:30:28 +1000 Subject: [PATCH 155/568] README: use https --- CHANGELOG.md | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 907c6d5..451a168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ __added__ __changed__ - `ECPair.prototype.sign` now returns a 64-byte signature `Buffer`, not an `ECSignature` object (#1084) -- `ECPair` (and all ECDSA code) now uses [`tiny-secp256k1`](http://github.com/bitcoinjs/tiny-secp256k1), which uses the [`libsecp256k1` library](https://github.com/bitcoin-core/secp256k1) (#1070) +- `ECPair` (and all ECDSA code) now uses [`tiny-secp256k1`](https://github.com/bitcoinjs/tiny-secp256k1), which uses the [`libsecp256k1` library](https://github.com/bitcoin-core/secp256k1) (#1070) - `TransactionBuilder` internal variables are now `__` prefixed to discourage public usage (#1038) - `TransactionBuilder` now defaults to version 2 transaction versions (#1036) - `script.decompile` now returns `[Buffer]` or `null`, if decompilation failed (#1039) diff --git a/README.md b/README.md index e8866c9..07c5ca4 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Mistakes and bugs happen, but with your help in resolving and reporting [issues] - Easy to audit and verify, - Tested, with test coverage >95%, - Advanced and feature rich, -- Standardized, using [standard](http://github.com/standard/standard) and Node `Buffer`'s throughout, and +- Standardized, using [standard](https://github.com/standard/standard) and Node `Buffer`'s throughout, and - Friendly, with a strong and helpful community, ready to answer questions. ## Documentation @@ -45,9 +45,9 @@ If in doubt, see the [.travis.yml](.travis.yml) for what versions are used by ou ### Browser The recommended method of using `bitcoinjs-lib` in your browser is through [Browserify](https://github.com/substack/node-browserify). -If you're familiar with how to use browserify, ignore this and carry on, otherwise, it is recommended to read the tutorial at http://browserify.org/. +If you're familiar with how to use browserify, ignore this and carry on, otherwise, it is recommended to read the tutorial at https://browserify.org/. -**NOTE**: We use Node Maintenance LTS features, if you need strict ES5, use [`--transform babelify`](https://github.com/babel/babelify) in conjunction with your `browserify` step (using an [`es2015`](http://babeljs.io/docs/plugins/preset-es2015/) preset). +**NOTE**: We use Node Maintenance LTS features, if you need strict ES5, use [`--transform babelify`](https://github.com/babel/babelify) in conjunction with your `browserify` step (using an [`es2015`](https://babeljs.io/docs/plugins/preset-es2015/) preset). **WARNING**: iOS devices have [problems](https://github.com/feross/buffer/issues/136), use atleast [buffer@5.0.5](https://github.com/feross/buffer/pull/155) or greater, and enforce the test suites (for `Buffer`, and any other dependency) pass before use. From b273deb265c2f58cdc1c7fa3af2cf2c2ff27a6a3 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Wed, 26 Sep 2018 11:03:21 +1000 Subject: [PATCH 156/568] README: add crypto is hard disclaimer --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 07c5ca4..2541b3b 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Mistakes and bugs happen, but with your help in resolving and reporting [issues] - Standardized, using [standard](https://github.com/standard/standard) and Node `Buffer`'s throughout, and - Friendly, with a strong and helpful community, ready to answer questions. + ## Documentation Presently, we do not have any formal documentation other than our [examples](#examples), please [ask for help](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new) if our examples aren't enough to guide you. @@ -42,6 +43,27 @@ If in doubt, see the [.travis.yml](.travis.yml) for what versions are used by ou ## Usage +Crypto is hard. + +When working with private keys, the random number generator is fundamentally one of the most important parts of any software you write. +For random number generation, we *default* to the [`randombytes`](https://github.com/crypto-browserify/randombytes) module, which uses [`window.crypto.getRandomValues`](https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues) in the browser, or Node js' [`crypto.randomBytes`](https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback), depending on your build system. +Although this default is ~OK, there is no simple way to detect if the underlying RNG provided is good enough, or if it is **catastrophically bad**. +You should always verify this yourself to your own standards. + +This library uses [tiny-secp256k1](https://github.com/bitcoinjs/tiny-secp256k1), which uses [RFC6979](https://tools.ietf.org/html/rfc6979) to help prevent `k` re-use and exploitation. +Unfortunately, this isn't a silver bullet. +Often, Javascript itself is working against us by bypassing these counter-measures. + +Problems in [`Buffer (UInt8Array)`](https://github.com/feross/buffer), for example, can trivially result in catastrophic fund loss without any warning. +It can do this through undermining your random number generation, accidentally producing a duplicate `k` value, sending Bitcoin to a malformed output script, or any of a million different ways. +Running tests in your target environment is important and a recommended step to verify continuously. + +Finally, **adhere to best practice**. We aren't an authorative source for best practice, but, at the very least: + +* Don't re-use addresses. Privacy is important, but, .... TODO +* Don't share BIP32 extended public keys. They are a liability, and [as shown in our examples](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L68), it only takes 1 mistake until **catastrophic failure**. +* TODO, anythign else of importance here? + ### Browser The recommended method of using `bitcoinjs-lib` in your browser is through [Browserify](https://github.com/substack/node-browserify). From e514bc73643ff63863c43896ea83f8a62fb72f8f Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Wed, 26 Sep 2018 17:26:04 +1000 Subject: [PATCH 157/568] README: add extra suggestions for best practice --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2541b3b..f2b2c8d 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,18 @@ Unfortunately, this isn't a silver bullet. Often, Javascript itself is working against us by bypassing these counter-measures. Problems in [`Buffer (UInt8Array)`](https://github.com/feross/buffer), for example, can trivially result in catastrophic fund loss without any warning. -It can do this through undermining your random number generation, accidentally producing a duplicate `k` value, sending Bitcoin to a malformed output script, or any of a million different ways. +It can do this through undermining your random number generation, [accidentally producing a duplicate `k` value](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L14), sending Bitcoin to a malformed output script, or any of a million different ways. Running tests in your target environment is important and a recommended step to verify continuously. -Finally, **adhere to best practice**. We aren't an authorative source for best practice, but, at the very least: +Finally, **adhere to best practice**. +We are not an authorative source of best practice, but, at the very least: -* Don't re-use addresses. Privacy is important, but, .... TODO -* Don't share BIP32 extended public keys. They are a liability, and [as shown in our examples](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L68), it only takes 1 mistake until **catastrophic failure**. -* TODO, anythign else of importance here? +* [Don't re-use addresses](https://en.bitcoin.it/wiki/Address_reuse). +* Don't share BIP32 extended public keys ('xpubs'). [They are a liability](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L68), and it only takes 1 misplaced private key (or a buggy implementation!) and you are vulnerable to **catastrophic fund loss**. +* [Don't use `Math.random`](https://security.stackexchange.com/questions/181580/why-is-math-random-not-designed-to-be-cryptographically-secure) - in any way - don't. +* Enforce that users always verify (manually) a freshly-decoded human-readable version of their intended transaction before broadcast. +* Don't *ask* users to generate mnemonics, or 'brain wallets', humans are terrible random number generators. +* Lastly, if you can, use [Typescript](https://www.typescriptlang.org/) or similar. ### Browser From 5fc673a8d6e7acd4b5767f1f0b633538d2c69429 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Wed, 26 Sep 2018 17:28:15 +1000 Subject: [PATCH 158/568] README: link to external explanations --- README.md | 7 +-- test/integration/crypto.js | 103 ------------------------------------- 2 files changed, 2 insertions(+), 108 deletions(-) delete mode 100644 test/integration/crypto.js diff --git a/README.md b/README.md index f2b2c8d..8a0885c 100644 --- a/README.md +++ b/README.md @@ -55,14 +55,14 @@ Unfortunately, this isn't a silver bullet. Often, Javascript itself is working against us by bypassing these counter-measures. Problems in [`Buffer (UInt8Array)`](https://github.com/feross/buffer), for example, can trivially result in catastrophic fund loss without any warning. -It can do this through undermining your random number generation, [accidentally producing a duplicate `k` value](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L14), sending Bitcoin to a malformed output script, or any of a million different ways. +It can do this through undermining your random number generation, [accidentally producing a duplicate `k` value](https://www.nilsschneider.net/2013/01/28/recovering-bitcoin-private-keys.html), sending Bitcoin to a malformed output script, or any of a million different ways. Running tests in your target environment is important and a recommended step to verify continuously. Finally, **adhere to best practice**. We are not an authorative source of best practice, but, at the very least: * [Don't re-use addresses](https://en.bitcoin.it/wiki/Address_reuse). -* Don't share BIP32 extended public keys ('xpubs'). [They are a liability](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L68), and it only takes 1 misplaced private key (or a buggy implementation!) and you are vulnerable to **catastrophic fund loss**. +* Don't share BIP32 extended public keys ('xpubs'). [They are a liability](https://bitcoin.stackexchange.com/questions/56916/derivation-of-parent-private-key-from-non-hardened-child), and it only takes 1 misplaced private key (or a buggy implementation!) and you are vulnerable to **catastrophic fund loss**. * [Don't use `Math.random`](https://security.stackexchange.com/questions/181580/why-is-math-random-not-designed-to-be-cryptographically-secure) - in any way - don't. * Enforce that users always verify (manually) a freshly-decoded human-readable version of their intended transaction before broadcast. * Don't *ask* users to generate mnemonics, or 'brain wallets', humans are terrible random number generators. @@ -140,11 +140,8 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). - [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L88) - [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L144) - [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L190) -- [Recover a private key from duplicate R values](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L14) -- [Recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L68) - [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L72) - [Generate a single-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L91) -- [Recover parent recipient.d, if a derived private key is leaked (and nonce was revealed)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L107) - [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L124) - [Generate a dual-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L147) diff --git a/test/integration/crypto.js b/test/integration/crypto.js deleted file mode 100644 index 3a40f6c..0000000 --- a/test/integration/crypto.js +++ /dev/null @@ -1,103 +0,0 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const BN = require('bn.js') -const bitcoin = require('../../') -const bip32 = require('bip32') -const crypto = require('crypto') -const tinysecp = require('tiny-secp256k1') - -describe('bitcoinjs-lib (crypto)', function () { - it('can recover a private key from duplicate R values', function () { - // https://blockchain.info/tx/f4c16475f2a6e9c602e4a287f9db3040e319eb9ece74761a4b84bc820fbeef50 - const tx = bitcoin.Transaction.fromHex('01000000020b668015b32a6178d8524cfef6dc6fc0a4751915c2e9b2ed2d2eab02424341c8000000006a47304402205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022024bf5f506968f5f23f1835574d5afe0e9021b4a5b65cf9742332d5e4acb68f41012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffffa95fa69f11dc1cbb77ef64f25a95d4b12ebda57d19d843333819d95c9172ff89000000006b48304502205e00298dc5265b7a914974c9d0298aa0e69a0ca932cb52a360436d6a622e5cd7022100832176b59e8f50c56631acbc824bcba936c9476c559c42a4468be98975d07562012103fd089f73735129f3d798a657aaaa4aa62a00fa15c76b61fc7f1b27ed1d0f35b8ffffffff02b000eb04000000001976a91472956eed9a8ecb19ae7e3ebd7b06cae4668696a788ac303db000000000001976a9146c0bd55dd2592287cd9992ce3ba3fc1208fb76da88ac00000000') - - tx.ins.forEach(function (input, vin) { - const { output: prevOutput, pubkey, signature } = bitcoin.payments.p2pkh({ input: input.script }) - - const scriptSignature = bitcoin.script.signature.decode(signature) - const m = tx.hashForSignature(vin, prevOutput, scriptSignature.hashType) - assert(bitcoin.ECPair.fromPublicKey(pubkey).verify(m, scriptSignature.signature), 'Invalid m') - - // store the required information - input.signature = scriptSignature.signature - input.z = new BN(m) - }) - - const n = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16) - - for (var i = 0; i < tx.ins.length; ++i) { - for (var j = i + 1; j < tx.ins.length; ++j) { - const inputA = tx.ins[i] - const inputB = tx.ins[j] - - // enforce matching r values - const r = inputA.signature.slice(0, 32) - const rB = inputB.signature.slice(0, 32) - assert.strictEqual(r.toString('hex'), rB.toString('hex')) - - const rInv = new BN(r).invm(n) - - const s1 = new BN(inputA.signature.slice(32, 64)) - const s2 = new BN(inputB.signature.slice(32, 64)) - const z1 = inputA.z - const z2 = inputB.z - - const zz = z1.sub(z2).mod(n) - const ss = s1.sub(s2).mod(n) - - // k = (z1 - z2) / (s1 - s2) - // d1 = (s1 * k - z1) / r - // d2 = (s2 * k - z2) / r - const k = zz.mul(ss.invm(n)).mod(n) - const d1 = ((s1.mul(k).mod(n)).sub(z1).mod(n)).mul(rInv).mod(n) - const d2 = ((s2.mul(k).mod(n)).sub(z2).mod(n)).mul(rInv).mod(n) - - // enforce matching private keys - assert.strictEqual(d1.toString(), d2.toString()) - } - } - }) - - it('can recover a BIP32 parent private key from the parent public key, and a derived, non-hardened child private key', function () { - function recoverParent (master, child) { - assert(master.isNeutered(), 'You already have the parent private key') - assert(!child.isNeutered(), 'Missing child private key') - - const serQP = master.publicKey - const d1 = child.privateKey - const data = Buffer.alloc(37) - serQP.copy(data, 0) - - // search index space until we find it - let d2 - for (var i = 0; i < 0x80000000; ++i) { - data.writeUInt32BE(i, 33) - - // calculate I - const I = crypto.createHmac('sha512', master.chainCode).update(data).digest() - const IL = I.slice(0, 32) - - // See bip32.js:273 to understand - d2 = tinysecp.privateSub(d1, IL) - - const Qp = bip32.fromPrivateKey(d2, Buffer.alloc(32, 0)).publicKey - if (Qp.equals(serQP)) break - } - - const node = bip32.fromPrivateKey(d2, master.chainCode, master.network) - node.depth = master.depth - node.index = master.index - node.masterFingerprint = master.masterFingerprint - return node - } - - const seed = crypto.randomBytes(32) - const master = bip32.fromSeed(seed) - const child = master.derive(6) // m/6 - - // now for the recovery - const neuteredMaster = master.neutered() - const recovered = recoverParent(neuteredMaster, child) - assert.strictEqual(recovered.toBase58(), master.toBase58()) - }) -}) From a908e909d1841500b5eeed4bee7a44240fc53b4c Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Wed, 26 Sep 2018 17:40:45 +1000 Subject: [PATCH 159/568] README: rm stealth address examples --- README.md | 4 - test/integration/stealth.js | 167 ------------------------------------ 2 files changed, 171 deletions(-) delete mode 100644 test/integration/stealth.js diff --git a/README.md b/README.md index 8a0885c..23dce36 100644 --- a/README.md +++ b/README.md @@ -140,10 +140,6 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). - [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L88) - [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L144) - [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L190) -- [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L72) -- [Generate a single-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L91) -- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L124) -- [Generate a dual-key stealth address (randomly)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/stealth.js#L147) If you have a use case that you feel could be listed here, please [ask for it](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new)! diff --git a/test/integration/stealth.js b/test/integration/stealth.js deleted file mode 100644 index dd99d63..0000000 --- a/test/integration/stealth.js +++ /dev/null @@ -1,167 +0,0 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const ecc = require('tiny-secp256k1') - -function getAddress (node, network) { - return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address -} - -// vG = (dG \+ sha256(e * dG)G) -function stealthSend (e, Q) { - const eQ = ecc.pointMultiply(Q, e, true) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const Qc = ecc.pointAddScalar(Q, c) - const vG = bitcoin.ECPair.fromPublicKey(Qc) - - return vG -} - -// v = (d + sha256(eG * d)) -function stealthReceive (d, eG) { - const eQ = ecc.pointMultiply(eG, d) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const dc = ecc.privateAdd(d, c) - const v = bitcoin.ECPair.fromPrivateKey(dc) - - return v -} - -// d = (v - sha256(e * dG)) -function stealthRecoverLeaked (v, e, Q) { - const eQ = ecc.pointMultiply(Q, e) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const vc = ecc.privateSub(v, c) - const d = bitcoin.ECPair.fromPrivateKey(vc) - - return d -} - -// vG = (rG \+ sha256(e * dG)G) -function stealthDualSend (e, R, Q) { - const eQ = ecc.pointMultiply(Q, e) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const Rc = ecc.pointAddScalar(R, c) - const vG = bitcoin.ECPair.fromPublicKey(Rc) - - return vG -} - -// vG = (rG \+ sha256(eG * d)G) -function stealthDualScan (d, R, eG) { - const eQ = ecc.pointMultiply(eG, d) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const Rc = ecc.pointAddScalar(R, c) - const vG = bitcoin.ECPair.fromPublicKey(Rc) - - return vG -} - -// v = (r + sha256(eG * d)) -function stealthDualReceive (d, r, eG) { - const eQ = ecc.pointMultiply(eG, d) // shared secret - const c = bitcoin.crypto.sha256(eQ) - const rc = ecc.privateAdd(r, c) - const v = bitcoin.ECPair.fromPrivateKey(rc) - - return v -} - -describe('bitcoinjs-lib (crypto)', function () { - it('can generate a single-key stealth address', function () { - // XXX: should be randomly generated, see next test for example - const recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient - const nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender - - // ... recipient reveals public key (recipient.Q) to sender - const forSender = stealthSend(nonce.privateKey, recipient.publicKey) - assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to recipient - const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) - assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') - assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n') - - // sender and recipient, both derived same address - assert.equal(getAddress(forSender), getAddress(forRecipient)) - }) - - it('can generate a single-key stealth address (randomly)', function () { - const recipient = bitcoin.ECPair.makeRandom() // private to recipient - const nonce = bitcoin.ECPair.makeRandom() // private to sender - - // ... recipient reveals public key (recipient.Q) to sender - const forSender = stealthSend(nonce.privateKey, recipient.publicKey) - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to recipient - const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) - assert.doesNotThrow(function () { forRecipient.toWIF() }) - - // sender and recipient, both derived same address - assert.equal(getAddress(forSender), getAddress(forRecipient)) - }) - - it('can recover parent recipient.d, if a derived private key is leaked [and nonce was revealed]', function () { - const recipient = bitcoin.ECPair.makeRandom() // private to recipient - const nonce = bitcoin.ECPair.makeRandom() // private to sender - - // ... recipient reveals public key (recipient.Q) to sender - const forSender = stealthSend(nonce.privateKey, recipient.publicKey) - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to recipient - const forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey) - assert.doesNotThrow(function () { forRecipient.toWIF() }) - - // ... recipient accidentally leaks forRecipient.d on the blockchain - const leaked = stealthRecoverLeaked(forRecipient.privateKey, nonce.privateKey, recipient.publicKey) - assert.equal(leaked.toWIF(), recipient.toWIF()) - }) - - it('can generate a dual-key stealth address', function () { - // XXX: should be randomly generated, see next test for example - const recipient = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // private to recipient - const scan = bitcoin.ECPair.fromWIF('L5DkCk3xLLoGKncqKsWQTdaPSR4V8gzc14WVghysQGkdryRudjBM') // private to scanner/recipient - const nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender - - // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - const forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to scanner - const forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) - assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) - - // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - const forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) - assert.doesNotThrow(function () { forRecipient.toWIF() }) - - // scanner, sender and recipient, all derived same address - assert.equal(getAddress(forSender), getAddress(forScanner)) - assert.equal(getAddress(forSender), getAddress(forRecipient)) - }) - - it('can generate a dual-key stealth address (randomly)', function () { - const recipient = bitcoin.ECPair.makeRandom() // private to recipient - const scan = bitcoin.ECPair.makeRandom() // private to scanner/recipient - const nonce = bitcoin.ECPair.makeRandom() // private to sender - - // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender - const forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey) - assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) - - // ... sender reveals nonce public key (nonce.Q) to scanner - const forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey) - assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) - - // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient - const forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey) - assert.doesNotThrow(function () { forRecipient.toWIF() }) - - // scanner, sender and recipient, all derived same address - assert.equal(getAddress(forSender), getAddress(forScanner)) - assert.equal(getAddress(forSender), getAddress(forRecipient)) - }) -}) From aac228011f9c8c75b17bc9d100bb67534172e366 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Wed, 26 Sep 2018 17:44:31 +1000 Subject: [PATCH 160/568] README: rm bad sha256 hash example --- README.md | 1 - test/integration/addresses.js | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/README.md b/README.md index 23dce36..28c1b21 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,6 @@ Otherwise, pull requests are appreciated. Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). - [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L22) -- [Generate an address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L29) - [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L40) - [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L47) - [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L60) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 4bd71c8..0024410 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -25,17 +25,6 @@ describe('bitcoinjs-lib (addresses)', function () { assert.strictEqual(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64') }) - it('can generate an address from a SHA256 hash', function () { - const hash = bitcoin.crypto.sha256(Buffer.from('correct horse battery staple')) - - const keyPair = bitcoin.ECPair.fromPrivateKey(hash) - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) - - // Generating addresses from SHA256 hashes is not secure if the input to the hash function is predictable - // Do not use with predictable inputs - assert.strictEqual(address, '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') - }) - it('can import an address via WIF', function () { const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) From e1049c1090b329a60e5d31679b77bb568e594b9f Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Wed, 26 Sep 2018 17:59:04 +1000 Subject: [PATCH 161/568] README: fix emphasis --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 28c1b21..9342696 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ This library uses [tiny-secp256k1](https://github.com/bitcoinjs/tiny-secp256k1), Unfortunately, this isn't a silver bullet. Often, Javascript itself is working against us by bypassing these counter-measures. -Problems in [`Buffer (UInt8Array)`](https://github.com/feross/buffer), for example, can trivially result in catastrophic fund loss without any warning. -It can do this through undermining your random number generation, [accidentally producing a duplicate `k` value](https://www.nilsschneider.net/2013/01/28/recovering-bitcoin-private-keys.html), sending Bitcoin to a malformed output script, or any of a million different ways. +Problems in [`Buffer (UInt8Array)`](https://github.com/feross/buffer), for example, can trivially result in **catastrophic fund loss** without any warning. +It can do this through undermining your random number generation, accidentally producing a [duplicate `k` value](https://www.nilsschneider.net/2013/01/28/recovering-bitcoin-private-keys.html), sending Bitcoin to a malformed output script, or any of a million different ways. Running tests in your target environment is important and a recommended step to verify continuously. Finally, **adhere to best practice**. From 959ba5ae96a3c04cb14a795fc64a4b0a1c67e866 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Thu, 6 Dec 2018 13:16:30 +1100 Subject: [PATCH 162/568] add P2SH(P2WPKH) signature verification example --- test/integration/transactions.js | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index e828414..0d88d78 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -258,7 +258,7 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) - it('can verify Transaction signatures', function () { + it('can verify Transaction (P2PKH) signatures', function () { const txHex = '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700' const keyPairs = [ '032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d', @@ -281,4 +281,34 @@ describe('bitcoinjs-lib (transactions)', function () { assert.strictEqual(keyPair.verify(hash, ss.signature), true) }) }) + + it('can verify Transaction (P2SH(P2WPKH)) signatures', function () { + const utxos = { + 'f72d1d83ac40fcedd01415751556a905844ab5f44bbb7728565ebb91b1590109:0': { + value: 50000 + } + } + + const txHex = '02000000000101090159b191bb5e562877bb4bf4b54a8405a95615751514d0edfc40ac831d2df7000000001716001435a179e5516947a39ae9c8a25e9fe62c0fc598edffffffff01204e0000000000001976a91431d43308d3c886d53e9ae8a45728370571ff456988ac0247304402206ec41f685b997a51f325b07ee852e82a535f6b52ef54485cc133e05168aa052a022070bafa86108acb51c77b2b259ae8fb7fd1efa10fef804fcfe9b13c2db719acf5012103fb03e9d0a9af86cbed94225dbb8bb70f6b82109bce0a61ddcf41dab6cbb4871100000000' + const tx = bitcoin.Transaction.fromHex(txHex) + + tx.ins.forEach(function (input, i) { + const txId = Buffer.from(input.hash).reverse().toString('hex') + const utxo = utxos[`${txId}:${i}`] + if (!utxo) throw new Error('Missing utxo') + + const p2sh = bitcoin.payments.p2sh({ + input: input.script, + witness: input.witness + }) + const p2wpkh = bitcoin.payments.p2wpkh(p2sh.redeem) + const p2pkh = bitcoin.payments.p2pkh({ pubkey: p2wpkh.pubkey }) // because P2WPKH is annoying + + const ss = bitcoin.script.signature.decode(p2wpkh.signature) + const hash = tx.hashForWitnessV0(i, p2pkh.output, utxo.value, ss.hashType) + const keyPair = bitcoin.ECPair.fromPublicKey(p2wpkh.pubkey) // aka, cQ3EtF4mApRcogNGSeyPTKbmfxxn3Yfb1wecfKSws9a8bnYuxoAk + + assert.strictEqual(keyPair.verify(hash, ss.signature), true) + }) + }) }) From 15289fe0cfc3c8b48a1b477a407dc0cc3ce7d044 Mon Sep 17 00:00:00 2001 From: Jonathan Underwood <junderwood@bitcoinbank.co.jp> Date: Mon, 10 Dec 2018 01:01:55 +0900 Subject: [PATCH 163/568] Add notes for learners about nSequence + LockTime --- test/integration/cltv.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 2c39b2c..8bf1c4a 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -50,6 +50,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { const txb = new bitcoin.TransactionBuilder(regtest) txb.setLockTime(lockTime) + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) @@ -96,6 +97,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { const txb = new bitcoin.TransactionBuilder(regtest) txb.setLockTime(lockTime) + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) @@ -147,6 +149,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { const txb = new bitcoin.TransactionBuilder(regtest) txb.setLockTime(lockTime) + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4) @@ -191,6 +194,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { const txb = new bitcoin.TransactionBuilder(regtest) txb.setLockTime(lockTime) + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) From 7c0e02ad488fa826cbfb69d0508aab0e427ce346 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 12 Dec 2018 21:07:16 +0900 Subject: [PATCH 164/568] Fix Block to allow regtest target (easiest possible target) --- src/block.js | 2 +- test/fixtures/block.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/block.js b/src/block.js index 8c7f3a6..eb7f708 100644 --- a/src/block.js +++ b/src/block.js @@ -145,7 +145,7 @@ Block.calculateTarget = function (bits) { const exponent = ((bits & 0xff000000) >> 24) - 3 const mantissa = bits & 0x007fffff const target = Buffer.alloc(32, 0) - target.writeUInt32BE(mantissa, 28 - exponent) + target.writeUIntBE(mantissa, 29 - exponent, 3) return target } diff --git a/test/fixtures/block.json b/test/fixtures/block.json index c7aa5f7..b4a1cfe 100644 --- a/test/fixtures/block.json +++ b/test/fixtures/block.json @@ -19,6 +19,10 @@ { "bits": "cffca00", "expected": "00000000000000000000000000000000000000007fca00000000000000000000" + }, + { + "bits": "207fffff", + "expected": "7fffff0000000000000000000000000000000000000000000000000000000000" } ], "valid": [ From f860d467d6993615c11e11e213da5337a0f4305a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 19 Dec 2018 00:16:48 +0900 Subject: [PATCH 165/568] Revert "Merge pull request #1086 from bitcoinjs/refactorTransaction" This reverts commit 5e1ae82a5d9569b8a14c3e526847b9904ff57c01, reversing changes made to 96240b636d99fb0553aa7fc7722a13942bff1c83. --- src/transaction.js | 783 +++++++++++++++++++++------------------------ 1 file changed, 360 insertions(+), 423 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index e1be629..751446f 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -16,11 +16,26 @@ function varSliceSize (someScript) { function vectorSize (someVector) { const length = someVector.length - return varuint.encodingLength(length) + someVector.reduce((sum, witness) => { + return varuint.encodingLength(length) + someVector.reduce(function (sum, witness) { return sum + varSliceSize(witness) }, 0) } +function Transaction () { + this.version = 1 + this.locktime = 0 + this.ins = [] + this.outs = [] +} + +Transaction.DEFAULT_SEQUENCE = 0xffffffff +Transaction.SIGHASH_ALL = 0x01 +Transaction.SIGHASH_NONE = 0x02 +Transaction.SIGHASH_SINGLE = 0x03 +Transaction.SIGHASH_ANYONECANPAY = 0x80 +Transaction.ADVANCED_TRANSACTION_MARKER = 0x00 +Transaction.ADVANCED_TRANSACTION_FLAG = 0x01 + const EMPTY_SCRIPT = Buffer.allocUnsafe(0) const EMPTY_WITNESS = [] const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') @@ -31,426 +46,8 @@ const BLANK_OUTPUT = { valueBuffer: VALUE_UINT64_MAX } -class Transaction { - constructor () { - this.version = 1 - this.locktime = 0 - this.ins = [] - this.outs = [] - } - - static get DEFAULT_SEQUENCE () { - return 0xffffffff - } - static get SIGHASH_ALL () { - return 0x01 - } - static get SIGHASH_NONE () { - return 0x02 - } - static get SIGHASH_SINGLE () { - return 0x03 - } - static get SIGHASH_ANYONECANPAY () { - return 0x80 - } - static get ADVANCED_TRANSACTION_MARKER () { - return 0x00 - } - static get ADVANCED_TRANSACTION_FLAG () { - return 0x01 - } - - isCoinbase () { - return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) - } - - addInput (hash, index, sequence, scriptSig) { - typeforce(types.tuple( - types.Hash256bit, - types.UInt32, - types.maybe(types.UInt32), - types.maybe(types.Buffer) - ), arguments) - - if (types.Null(sequence)) { - sequence = Transaction.DEFAULT_SEQUENCE - } - - // Add the input and return the input's index - return (this.ins.push({ - hash: hash, - index: index, - script: scriptSig || EMPTY_SCRIPT, - sequence: sequence, - witness: EMPTY_WITNESS - }) - 1) - } - - addOutput (scriptPubKey, value) { - typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) - - // Add the output and return the output's index - return (this.outs.push({ - script: scriptPubKey, - value: value - }) - 1) - } - - hasWitnesses () { - return this.ins.some((x) => { - return x.witness.length !== 0 - }) - } - - weight () { - const base = this.__byteLength(false) - const total = this.__byteLength(true) - return base * 3 + total - } - - virtualSize () { - return Math.ceil(this.weight() / 4) - } - - byteLength () { - return this.__byteLength(true) - } - - __byteLength (__allowWitness) { - const hasWitnesses = __allowWitness && this.hasWitnesses() - - return ( - (hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce((sum, input) => { - return sum + 40 + varSliceSize(input.script) - }, 0) + - this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script) - }, 0) + - (hasWitnesses ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness) - }, 0) : 0) - ) - } - - clone () { - const newTx = new Transaction() - newTx.version = this.version - newTx.locktime = this.locktime - - newTx.ins = this.ins.map((txIn) => { - return { - hash: txIn.hash, - index: txIn.index, - script: txIn.script, - sequence: txIn.sequence, - witness: txIn.witness - } - }) - - newTx.outs = this.outs.map((txOut) => { - return { - script: txOut.script, - value: txOut.value - } - }) - - return newTx - } - - /** - * Hash transaction for signing a specific input. - * - * Bitcoin uses a different hash for each signed transaction input. - * This method copies the transaction, makes the necessary changes based on the - * hashType, and then hashes the result. - * This hash can then be used to sign the provided transaction input. - */ - hashForSignature (inIndex, prevOutScript, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) - - // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 - if (inIndex >= this.ins.length) return ONE - - // ignore OP_CODESEPARATOR - const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter((x) => { - return x !== opcodes.OP_CODESEPARATOR - })) - - const txTmp = this.clone() - - // SIGHASH_NONE: ignore all outputs? (wildcard payee) - if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { - txTmp.outs = [] - - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach((input, i) => { - if (i === inIndex) return - - input.sequence = 0 - }) - - // SIGHASH_SINGLE: ignore all outputs, except at the same index? - } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { - // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 - if (inIndex >= this.outs.length) return ONE - - // truncate outputs after - txTmp.outs.length = inIndex + 1 - - // "blank" outputs before - for (var i = 0; i < inIndex; i++) { - txTmp.outs[i] = BLANK_OUTPUT - } - - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach((input, y) => { - if (y === inIndex) return - - input.sequence = 0 - }) - } - - // SIGHASH_ANYONECANPAY: ignore inputs entirely? - if (hashType & Transaction.SIGHASH_ANYONECANPAY) { - txTmp.ins = [txTmp.ins[inIndex]] - txTmp.ins[0].script = ourScript - - // SIGHASH_ALL: only ignore input scripts - } else { - // "blank" others input scripts - txTmp.ins.forEach((input) => { - input.script = EMPTY_SCRIPT - }) - txTmp.ins[inIndex].script = ourScript - } - - // serialize and hash - const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) - buffer.writeInt32LE(hashType, buffer.length - 4) - txTmp.__toBuffer(buffer, 0, false) - - return bcrypto.hash256(buffer) - } - - hashForWitnessV0 (inIndex, prevOutScript, value, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) - - let tbuffer, toffset - - function writeSlice (slice) { - toffset += slice.copy(tbuffer, toffset) - } - - function writeUInt32 (i) { - toffset = tbuffer.writeUInt32LE(i, toffset) - } - - function writeUInt64 (i) { - toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) - } - - function writeVarInt (i) { - varuint.encode(i, tbuffer, toffset) - toffset += varuint.encode.bytes - } - - function writeVarSlice (slice) { - writeVarInt(slice.length) - writeSlice(slice) - } - - let hashOutputs = ZERO - let hashPrevouts = ZERO - let hashSequence = ZERO - - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { - tbuffer = Buffer.allocUnsafe(36 * this.ins.length) - toffset = 0 - - this.ins.forEach((txIn) => { - writeSlice(txIn.hash) - writeUInt32(txIn.index) - }) - - hashPrevouts = bcrypto.hash256(tbuffer) - } - - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && - (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - tbuffer = Buffer.allocUnsafe(4 * this.ins.length) - toffset = 0 - - this.ins.forEach((txIn) => { - writeUInt32(txIn.sequence) - }) - - hashSequence = bcrypto.hash256(tbuffer) - } - - if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - const txOutsSize = this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script) - }, 0) - - tbuffer = Buffer.allocUnsafe(txOutsSize) - toffset = 0 - - this.outs.forEach((out) => { - writeUInt64(out.value) - writeVarSlice(out.script) - }) - - hashOutputs = bcrypto.hash256(tbuffer) - } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { - const output = this.outs[inIndex] - - tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) - toffset = 0 - writeUInt64(output.value) - writeVarSlice(output.script) - - hashOutputs = bcrypto.hash256(tbuffer) - } - - tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) - toffset = 0 - - const input = this.ins[inIndex] - writeUInt32(this.version) - writeSlice(hashPrevouts) - writeSlice(hashSequence) - writeSlice(input.hash) - writeUInt32(input.index) - writeVarSlice(prevOutScript) - writeUInt64(value) - writeUInt32(input.sequence) - writeSlice(hashOutputs) - writeUInt32(this.locktime) - writeUInt32(hashType) - return bcrypto.hash256(tbuffer) - } - - getHash () { - return bcrypto.hash256(this.__toBuffer(undefined, undefined, false)) - } - - getId () { - // transaction hash's are displayed in reverse order - return this.getHash().reverse().toString('hex') - } - - toBuffer (buffer, initialOffset) { - return this.__toBuffer(buffer, initialOffset, true) - } - - __toBuffer (buffer, initialOffset, __allowWitness) { - if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)) - - let offset = initialOffset || 0 - - function writeSlice (slice) { - offset += slice.copy(buffer, offset) - } - - function writeUInt8 (i) { - offset = buffer.writeUInt8(i, offset) - } - - function writeUInt32 (i) { - offset = buffer.writeUInt32LE(i, offset) - } - - function writeInt32 (i) { - offset = buffer.writeInt32LE(i, offset) - } - - function writeUInt64 (i) { - offset = bufferutils.writeUInt64LE(buffer, i, offset) - } - - function writeVarInt (i) { - varuint.encode(i, buffer, offset) - offset += varuint.encode.bytes - } - - function writeVarSlice (slice) { - writeVarInt(slice.length) - writeSlice(slice) - } - - function writeVector (vector) { - writeVarInt(vector.length) - vector.forEach(writeVarSlice) - } - - writeInt32(this.version) - - const hasWitnesses = __allowWitness && this.hasWitnesses() - - if (hasWitnesses) { - writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) - writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) - } - - writeVarInt(this.ins.length) - - this.ins.forEach((txIn) => { - writeSlice(txIn.hash) - writeUInt32(txIn.index) - writeVarSlice(txIn.script) - writeUInt32(txIn.sequence) - }) - - writeVarInt(this.outs.length) - this.outs.forEach((txOut) => { - if (!txOut.valueBuffer) { - writeUInt64(txOut.value) - } else { - writeSlice(txOut.valueBuffer) - } - - writeVarSlice(txOut.script) - }) - - if (hasWitnesses) { - this.ins.forEach((input) => { - writeVector(input.witness) - }) - } - - writeUInt32(this.locktime) - - // avoid slicing unless necessary - if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) - return buffer - } - - toHex () { - return this.toBuffer().toString('hex') - } - - setInputScript (index, scriptSig) { - typeforce(types.tuple(types.Number, types.Buffer), arguments) - - this.ins[index].script = scriptSig - } - - setWitness (index, witness) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments) - - this.ins[index].witness = witness - } -} - -Transaction.fromBuffer = (buffer, __noStrict) => { +Transaction.fromBuffer = function (buffer, __noStrict) { let offset = 0 - function readSlice (n) { offset += n return buffer.slice(offset - n, offset) @@ -499,7 +96,7 @@ Transaction.fromBuffer = (buffer, __noStrict) => { let hasWitnesses = false if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && - flag === Transaction.ADVANCED_TRANSACTION_FLAG) { + flag === Transaction.ADVANCED_TRANSACTION_FLAG) { offset += 2 hasWitnesses = true } @@ -540,11 +137,11 @@ Transaction.fromBuffer = (buffer, __noStrict) => { return tx } -Transaction.fromHex = (hex) => { +Transaction.fromHex = function (hex) { return Transaction.fromBuffer(Buffer.from(hex, 'hex')) } -Transaction.isCoinbaseHash = (buffer) => { +Transaction.isCoinbaseHash = function (buffer) { typeforce(types.Hash256bit, buffer) for (var i = 0; i < 32; ++i) { if (buffer[i] !== 0) return false @@ -552,4 +149,344 @@ Transaction.isCoinbaseHash = (buffer) => { return true } +Transaction.prototype.isCoinbase = function () { + return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) +} + +Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) { + typeforce(types.tuple( + types.Hash256bit, + types.UInt32, + types.maybe(types.UInt32), + types.maybe(types.Buffer) + ), arguments) + + if (types.Null(sequence)) { + sequence = Transaction.DEFAULT_SEQUENCE + } + + // Add the input and return the input's index + return (this.ins.push({ + hash: hash, + index: index, + script: scriptSig || EMPTY_SCRIPT, + sequence: sequence, + witness: EMPTY_WITNESS + }) - 1) +} + +Transaction.prototype.addOutput = function (scriptPubKey, value) { + typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) + + // Add the output and return the output's index + return (this.outs.push({ + script: scriptPubKey, + value: value + }) - 1) +} + +Transaction.prototype.hasWitnesses = function () { + return this.ins.some(function (x) { + return x.witness.length !== 0 + }) +} + +Transaction.prototype.weight = function () { + const base = this.__byteLength(false) + const total = this.__byteLength(true) + return base * 3 + total +} + +Transaction.prototype.virtualSize = function () { + return Math.ceil(this.weight() / 4) +} + +Transaction.prototype.byteLength = function () { + return this.__byteLength(true) +} + +Transaction.prototype.__byteLength = function (__allowWitness) { + const hasWitnesses = __allowWitness && this.hasWitnesses() + + return ( + (hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce(function (sum, input) { return sum + 40 + varSliceSize(input.script) }, 0) + + this.outs.reduce(function (sum, output) { return sum + 8 + varSliceSize(output.script) }, 0) + + (hasWitnesses ? this.ins.reduce(function (sum, input) { return sum + vectorSize(input.witness) }, 0) : 0) + ) +} + +Transaction.prototype.clone = function () { + const newTx = new Transaction() + newTx.version = this.version + newTx.locktime = this.locktime + + newTx.ins = this.ins.map(function (txIn) { + return { + hash: txIn.hash, + index: txIn.index, + script: txIn.script, + sequence: txIn.sequence, + witness: txIn.witness + } + }) + + newTx.outs = this.outs.map(function (txOut) { + return { + script: txOut.script, + value: txOut.value + } + }) + + return newTx +} + +/** + * Hash transaction for signing a specific input. + * + * Bitcoin uses a different hash for each signed transaction input. + * This method copies the transaction, makes the necessary changes based on the + * hashType, and then hashes the result. + * This hash can then be used to sign the provided transaction input. + */ +Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashType) { + typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) + + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 + if (inIndex >= this.ins.length) return ONE + + // ignore OP_CODESEPARATOR + const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) { + return x !== opcodes.OP_CODESEPARATOR + })) + + const txTmp = this.clone() + + // SIGHASH_NONE: ignore all outputs? (wildcard payee) + if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { + txTmp.outs = [] + + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach(function (input, i) { + if (i === inIndex) return + + input.sequence = 0 + }) + + // SIGHASH_SINGLE: ignore all outputs, except at the same index? + } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 + if (inIndex >= this.outs.length) return ONE + + // truncate outputs after + txTmp.outs.length = inIndex + 1 + + // "blank" outputs before + for (var i = 0; i < inIndex; i++) { + txTmp.outs[i] = BLANK_OUTPUT + } + + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach(function (input, y) { + if (y === inIndex) return + + input.sequence = 0 + }) + } + + // SIGHASH_ANYONECANPAY: ignore inputs entirely? + if (hashType & Transaction.SIGHASH_ANYONECANPAY) { + txTmp.ins = [txTmp.ins[inIndex]] + txTmp.ins[0].script = ourScript + + // SIGHASH_ALL: only ignore input scripts + } else { + // "blank" others input scripts + txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT }) + txTmp.ins[inIndex].script = ourScript + } + + // serialize and hash + const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) + buffer.writeInt32LE(hashType, buffer.length - 4) + txTmp.__toBuffer(buffer, 0, false) + + return bcrypto.hash256(buffer) +} + +Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value, hashType) { + typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) + + let tbuffer, toffset + function writeSlice (slice) { toffset += slice.copy(tbuffer, toffset) } + function writeUInt32 (i) { toffset = tbuffer.writeUInt32LE(i, toffset) } + function writeUInt64 (i) { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) } + function writeVarInt (i) { + varuint.encode(i, tbuffer, toffset) + toffset += varuint.encode.bytes + } + function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } + + let hashOutputs = ZERO + let hashPrevouts = ZERO + let hashSequence = ZERO + + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { + tbuffer = Buffer.allocUnsafe(36 * this.ins.length) + toffset = 0 + + this.ins.forEach(function (txIn) { + writeSlice(txIn.hash) + writeUInt32(txIn.index) + }) + + hashPrevouts = bcrypto.hash256(tbuffer) + } + + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && + (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + tbuffer = Buffer.allocUnsafe(4 * this.ins.length) + toffset = 0 + + this.ins.forEach(function (txIn) { + writeUInt32(txIn.sequence) + }) + + hashSequence = bcrypto.hash256(tbuffer) + } + + if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + const txOutsSize = this.outs.reduce(function (sum, output) { + return sum + 8 + varSliceSize(output.script) + }, 0) + + tbuffer = Buffer.allocUnsafe(txOutsSize) + toffset = 0 + + this.outs.forEach(function (out) { + writeUInt64(out.value) + writeVarSlice(out.script) + }) + + hashOutputs = bcrypto.hash256(tbuffer) + } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { + const output = this.outs[inIndex] + + tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) + toffset = 0 + writeUInt64(output.value) + writeVarSlice(output.script) + + hashOutputs = bcrypto.hash256(tbuffer) + } + + tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) + toffset = 0 + + const input = this.ins[inIndex] + writeUInt32(this.version) + writeSlice(hashPrevouts) + writeSlice(hashSequence) + writeSlice(input.hash) + writeUInt32(input.index) + writeVarSlice(prevOutScript) + writeUInt64(value) + writeUInt32(input.sequence) + writeSlice(hashOutputs) + writeUInt32(this.locktime) + writeUInt32(hashType) + return bcrypto.hash256(tbuffer) +} + +Transaction.prototype.getHash = function () { + return bcrypto.hash256(this.__toBuffer(undefined, undefined, false)) +} + +Transaction.prototype.getId = function () { + // transaction hash's are displayed in reverse order + return this.getHash().reverse().toString('hex') +} + +Transaction.prototype.toBuffer = function (buffer, initialOffset) { + return this.__toBuffer(buffer, initialOffset, true) +} + +Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitness) { + if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)) + + let offset = initialOffset || 0 + function writeSlice (slice) { offset += slice.copy(buffer, offset) } + function writeUInt8 (i) { offset = buffer.writeUInt8(i, offset) } + function writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) } + function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) } + function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) } + function writeVarInt (i) { + varuint.encode(i, buffer, offset) + offset += varuint.encode.bytes + } + function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } + function writeVector (vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice) } + + writeInt32(this.version) + + const hasWitnesses = __allowWitness && this.hasWitnesses() + + if (hasWitnesses) { + writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) + writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) + } + + writeVarInt(this.ins.length) + + this.ins.forEach(function (txIn) { + writeSlice(txIn.hash) + writeUInt32(txIn.index) + writeVarSlice(txIn.script) + writeUInt32(txIn.sequence) + }) + + writeVarInt(this.outs.length) + this.outs.forEach(function (txOut) { + if (!txOut.valueBuffer) { + writeUInt64(txOut.value) + } else { + writeSlice(txOut.valueBuffer) + } + + writeVarSlice(txOut.script) + }) + + if (hasWitnesses) { + this.ins.forEach(function (input) { + writeVector(input.witness) + }) + } + + writeUInt32(this.locktime) + + // avoid slicing unless necessary + if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) + return buffer +} + +Transaction.prototype.toHex = function () { + return this.toBuffer().toString('hex') +} + +Transaction.prototype.setInputScript = function (index, scriptSig) { + typeforce(types.tuple(types.Number, types.Buffer), arguments) + + this.ins[index].script = scriptSig +} + +Transaction.prototype.setWitness = function (index, witness) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments) + + this.ins[index].witness = witness +} + module.exports = Transaction From bb982895012446de41f76ea5a19ea9ced52c2616 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 21 Dec 2018 17:55:03 +0900 Subject: [PATCH 166/568] Renamed files to ts --- src/{address.js => address.ts} | 0 src/{block.js => block.ts} | 0 src/{bufferutils.js => bufferutils.ts} | 0 src/{classify.js => classify.ts} | 0 src/{crypto.js => crypto.ts} | 0 src/{ecpair.js => ecpair.ts} | 0 src/{index.js => index.ts} | 0 src/{networks.js => networks.ts} | 0 src/payments/{embed.js => embed.ts} | 0 src/payments/{index.js => index.ts} | 0 src/payments/{lazy.js => lazy.ts} | 0 src/payments/{p2ms.js => p2ms.ts} | 0 src/payments/{p2pk.js => p2pk.ts} | 0 src/payments/{p2pkh.js => p2pkh.ts} | 0 src/payments/{p2sh.js => p2sh.ts} | 0 src/payments/{p2wpkh.js => p2wpkh.ts} | 0 src/payments/{p2wsh.js => p2wsh.ts} | 0 src/{script.js => script.ts} | 0 src/{script_number.js => script_number.ts} | 0 src/{script_signature.js => script_signature.ts} | 0 src/templates/multisig/{index.js => index.ts} | 0 src/templates/multisig/{input.js => input.ts} | 0 src/templates/multisig/{output.js => output.ts} | 0 src/templates/{nulldata.js => nulldata.ts} | 0 src/templates/pubkey/{index.js => index.ts} | 0 src/templates/pubkey/{input.js => input.ts} | 0 src/templates/pubkey/{output.js => output.ts} | 0 src/templates/pubkeyhash/{index.js => index.ts} | 0 src/templates/pubkeyhash/{input.js => input.ts} | 0 src/templates/pubkeyhash/{output.js => output.ts} | 0 src/templates/scripthash/{index.js => index.ts} | 0 src/templates/scripthash/{input.js => input.ts} | 0 src/templates/scripthash/{output.js => output.ts} | 0 src/templates/witnesscommitment/{index.js => index.ts} | 0 src/templates/witnesscommitment/{output.js => output.ts} | 0 src/templates/witnesspubkeyhash/{index.js => index.ts} | 0 src/templates/witnesspubkeyhash/{input.js => input.ts} | 0 src/templates/witnesspubkeyhash/{output.js => output.ts} | 0 src/templates/witnessscripthash/{index.js => index.ts} | 0 src/templates/witnessscripthash/{input.js => input.ts} | 0 src/templates/witnessscripthash/{output.js => output.ts} | 0 src/{transaction.js => transaction.ts} | 0 src/{transaction_builder.js => transaction_builder.ts} | 0 src/{types.js => types.ts} | 0 44 files changed, 0 insertions(+), 0 deletions(-) rename src/{address.js => address.ts} (100%) rename src/{block.js => block.ts} (100%) rename src/{bufferutils.js => bufferutils.ts} (100%) rename src/{classify.js => classify.ts} (100%) rename src/{crypto.js => crypto.ts} (100%) rename src/{ecpair.js => ecpair.ts} (100%) rename src/{index.js => index.ts} (100%) rename src/{networks.js => networks.ts} (100%) rename src/payments/{embed.js => embed.ts} (100%) rename src/payments/{index.js => index.ts} (100%) rename src/payments/{lazy.js => lazy.ts} (100%) rename src/payments/{p2ms.js => p2ms.ts} (100%) rename src/payments/{p2pk.js => p2pk.ts} (100%) rename src/payments/{p2pkh.js => p2pkh.ts} (100%) rename src/payments/{p2sh.js => p2sh.ts} (100%) rename src/payments/{p2wpkh.js => p2wpkh.ts} (100%) rename src/payments/{p2wsh.js => p2wsh.ts} (100%) rename src/{script.js => script.ts} (100%) rename src/{script_number.js => script_number.ts} (100%) rename src/{script_signature.js => script_signature.ts} (100%) rename src/templates/multisig/{index.js => index.ts} (100%) rename src/templates/multisig/{input.js => input.ts} (100%) rename src/templates/multisig/{output.js => output.ts} (100%) rename src/templates/{nulldata.js => nulldata.ts} (100%) rename src/templates/pubkey/{index.js => index.ts} (100%) rename src/templates/pubkey/{input.js => input.ts} (100%) rename src/templates/pubkey/{output.js => output.ts} (100%) rename src/templates/pubkeyhash/{index.js => index.ts} (100%) rename src/templates/pubkeyhash/{input.js => input.ts} (100%) rename src/templates/pubkeyhash/{output.js => output.ts} (100%) rename src/templates/scripthash/{index.js => index.ts} (100%) rename src/templates/scripthash/{input.js => input.ts} (100%) rename src/templates/scripthash/{output.js => output.ts} (100%) rename src/templates/witnesscommitment/{index.js => index.ts} (100%) rename src/templates/witnesscommitment/{output.js => output.ts} (100%) rename src/templates/witnesspubkeyhash/{index.js => index.ts} (100%) rename src/templates/witnesspubkeyhash/{input.js => input.ts} (100%) rename src/templates/witnesspubkeyhash/{output.js => output.ts} (100%) rename src/templates/witnessscripthash/{index.js => index.ts} (100%) rename src/templates/witnessscripthash/{input.js => input.ts} (100%) rename src/templates/witnessscripthash/{output.js => output.ts} (100%) rename src/{transaction.js => transaction.ts} (100%) rename src/{transaction_builder.js => transaction_builder.ts} (100%) rename src/{types.js => types.ts} (100%) diff --git a/src/address.js b/src/address.ts similarity index 100% rename from src/address.js rename to src/address.ts diff --git a/src/block.js b/src/block.ts similarity index 100% rename from src/block.js rename to src/block.ts diff --git a/src/bufferutils.js b/src/bufferutils.ts similarity index 100% rename from src/bufferutils.js rename to src/bufferutils.ts diff --git a/src/classify.js b/src/classify.ts similarity index 100% rename from src/classify.js rename to src/classify.ts diff --git a/src/crypto.js b/src/crypto.ts similarity index 100% rename from src/crypto.js rename to src/crypto.ts diff --git a/src/ecpair.js b/src/ecpair.ts similarity index 100% rename from src/ecpair.js rename to src/ecpair.ts diff --git a/src/index.js b/src/index.ts similarity index 100% rename from src/index.js rename to src/index.ts diff --git a/src/networks.js b/src/networks.ts similarity index 100% rename from src/networks.js rename to src/networks.ts diff --git a/src/payments/embed.js b/src/payments/embed.ts similarity index 100% rename from src/payments/embed.js rename to src/payments/embed.ts diff --git a/src/payments/index.js b/src/payments/index.ts similarity index 100% rename from src/payments/index.js rename to src/payments/index.ts diff --git a/src/payments/lazy.js b/src/payments/lazy.ts similarity index 100% rename from src/payments/lazy.js rename to src/payments/lazy.ts diff --git a/src/payments/p2ms.js b/src/payments/p2ms.ts similarity index 100% rename from src/payments/p2ms.js rename to src/payments/p2ms.ts diff --git a/src/payments/p2pk.js b/src/payments/p2pk.ts similarity index 100% rename from src/payments/p2pk.js rename to src/payments/p2pk.ts diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.ts similarity index 100% rename from src/payments/p2pkh.js rename to src/payments/p2pkh.ts diff --git a/src/payments/p2sh.js b/src/payments/p2sh.ts similarity index 100% rename from src/payments/p2sh.js rename to src/payments/p2sh.ts diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.ts similarity index 100% rename from src/payments/p2wpkh.js rename to src/payments/p2wpkh.ts diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.ts similarity index 100% rename from src/payments/p2wsh.js rename to src/payments/p2wsh.ts diff --git a/src/script.js b/src/script.ts similarity index 100% rename from src/script.js rename to src/script.ts diff --git a/src/script_number.js b/src/script_number.ts similarity index 100% rename from src/script_number.js rename to src/script_number.ts diff --git a/src/script_signature.js b/src/script_signature.ts similarity index 100% rename from src/script_signature.js rename to src/script_signature.ts diff --git a/src/templates/multisig/index.js b/src/templates/multisig/index.ts similarity index 100% rename from src/templates/multisig/index.js rename to src/templates/multisig/index.ts diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.ts similarity index 100% rename from src/templates/multisig/input.js rename to src/templates/multisig/input.ts diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.ts similarity index 100% rename from src/templates/multisig/output.js rename to src/templates/multisig/output.ts diff --git a/src/templates/nulldata.js b/src/templates/nulldata.ts similarity index 100% rename from src/templates/nulldata.js rename to src/templates/nulldata.ts diff --git a/src/templates/pubkey/index.js b/src/templates/pubkey/index.ts similarity index 100% rename from src/templates/pubkey/index.js rename to src/templates/pubkey/index.ts diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.ts similarity index 100% rename from src/templates/pubkey/input.js rename to src/templates/pubkey/input.ts diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.ts similarity index 100% rename from src/templates/pubkey/output.js rename to src/templates/pubkey/output.ts diff --git a/src/templates/pubkeyhash/index.js b/src/templates/pubkeyhash/index.ts similarity index 100% rename from src/templates/pubkeyhash/index.js rename to src/templates/pubkeyhash/index.ts diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.ts similarity index 100% rename from src/templates/pubkeyhash/input.js rename to src/templates/pubkeyhash/input.ts diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.ts similarity index 100% rename from src/templates/pubkeyhash/output.js rename to src/templates/pubkeyhash/output.ts diff --git a/src/templates/scripthash/index.js b/src/templates/scripthash/index.ts similarity index 100% rename from src/templates/scripthash/index.js rename to src/templates/scripthash/index.ts diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.ts similarity index 100% rename from src/templates/scripthash/input.js rename to src/templates/scripthash/input.ts diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.ts similarity index 100% rename from src/templates/scripthash/output.js rename to src/templates/scripthash/output.ts diff --git a/src/templates/witnesscommitment/index.js b/src/templates/witnesscommitment/index.ts similarity index 100% rename from src/templates/witnesscommitment/index.js rename to src/templates/witnesscommitment/index.ts diff --git a/src/templates/witnesscommitment/output.js b/src/templates/witnesscommitment/output.ts similarity index 100% rename from src/templates/witnesscommitment/output.js rename to src/templates/witnesscommitment/output.ts diff --git a/src/templates/witnesspubkeyhash/index.js b/src/templates/witnesspubkeyhash/index.ts similarity index 100% rename from src/templates/witnesspubkeyhash/index.js rename to src/templates/witnesspubkeyhash/index.ts diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.ts similarity index 100% rename from src/templates/witnesspubkeyhash/input.js rename to src/templates/witnesspubkeyhash/input.ts diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.ts similarity index 100% rename from src/templates/witnesspubkeyhash/output.js rename to src/templates/witnesspubkeyhash/output.ts diff --git a/src/templates/witnessscripthash/index.js b/src/templates/witnessscripthash/index.ts similarity index 100% rename from src/templates/witnessscripthash/index.js rename to src/templates/witnessscripthash/index.ts diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.ts similarity index 100% rename from src/templates/witnessscripthash/input.js rename to src/templates/witnessscripthash/input.ts diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.ts similarity index 100% rename from src/templates/witnessscripthash/output.js rename to src/templates/witnessscripthash/output.ts diff --git a/src/transaction.js b/src/transaction.ts similarity index 100% rename from src/transaction.js rename to src/transaction.ts diff --git a/src/transaction_builder.js b/src/transaction_builder.ts similarity index 100% rename from src/transaction_builder.js rename to src/transaction_builder.ts diff --git a/src/types.js b/src/types.ts similarity index 100% rename from src/types.js rename to src/types.ts From e855bde3b37698825f729051731c78c176795fe7 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 21 Dec 2018 18:17:54 +0900 Subject: [PATCH 167/568] Add tsconfig.json and types for node as well as typescript --- .gitignore | 1 + package.json | 4 +++- tsconfig.json | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index a6c0ab8..3d940fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ coverage +dist node_modules .nyc_output npm-debug.log diff --git a/package.json b/package.json index 755be28..52c7e8d 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "wif": "^2.0.1" }, "devDependencies": { + "@types/node": "^10.12.18", "bip39": "^2.3.0", "bip65": "^1.0.1", "bip68": "^1.0.3", @@ -58,7 +59,8 @@ "mocha": "^5.2.0", "nyc": "^11.8.0", "proxyquire": "^2.0.1", - "standard": "^11.0.1" + "standard": "^11.0.1", + "typescript": "^3.2.2" }, "license": "MIT" } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8e1edb4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2015", + "module": "commonjs", + "outDir": "./dist", + "rootDir": "./", + "types": [ + "node" + ], + "allowJs": true, + "strict": false, + "noImplicitAny": false, + "strictNullChecks": false, + "strictFunctionTypes": false, + "strictBindCallApply": false, + "strictPropertyInitialization": false, + "noImplicitThis": false, + "alwaysStrict": false, + "esModuleInterop": true + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "**/*.spec.ts" + ] +} From d684a6b2ef96054ab6270107450a68d127c8e9c5 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 26 Dec 2018 14:25:33 +0900 Subject: [PATCH 168/568] Fix compiler errors, tests, and scripts --- package.json | 11 ++++++----- src/address.ts | 1 + src/block.ts | 1 + src/bufferutils.ts | 1 + src/classify.ts | 1 + src/crypto.ts | 1 + src/ecpair.ts | 1 + src/index.ts | 1 + src/networks.ts | 1 + src/payments/embed.ts | 6 +++++- src/payments/index.ts | 1 + src/payments/lazy.ts | 1 + src/payments/p2ms.ts | 13 +++++++++++-- src/payments/p2pk.ts | 8 +++++++- src/payments/p2pkh.ts | 8 +++++++- src/payments/p2sh.ts | 8 +++++++- src/payments/p2wpkh.ts | 8 +++++++- src/payments/p2wsh.ts | 8 +++++++- src/script.ts | 1 + src/script_number.ts | 1 + src/script_signature.ts | 1 + src/templates/multisig/index.ts | 1 + src/templates/multisig/input.ts | 1 + src/templates/multisig/output.ts | 1 + src/templates/nulldata.ts | 1 + src/templates/pubkey/index.ts | 1 + src/templates/pubkey/input.ts | 1 + src/templates/pubkey/output.ts | 1 + src/templates/pubkeyhash/index.ts | 1 + src/templates/pubkeyhash/input.ts | 1 + src/templates/pubkeyhash/output.ts | 1 + src/templates/scripthash/index.ts | 1 + src/templates/scripthash/input.ts | 1 + src/templates/scripthash/output.ts | 1 + src/templates/witnesscommitment/index.ts | 1 + src/templates/witnesscommitment/output.ts | 1 + src/templates/witnesspubkeyhash/index.ts | 1 + src/templates/witnesspubkeyhash/input.ts | 1 + src/templates/witnesspubkeyhash/output.ts | 1 + src/templates/witnessscripthash/index.ts | 1 + src/templates/witnessscripthash/input.ts | 1 + src/templates/witnessscripthash/output.ts | 1 + src/transaction.ts | 3 ++- src/transaction_builder.ts | 21 ++++++++++++++------- src/types.ts | 1 + test/address.js | 6 +++--- test/block.js | 2 +- test/bufferutils.js | 2 +- test/classify.js | 20 ++++++++++---------- test/crypto.js | 2 +- test/ecpair.js | 6 +++--- test/payments.js | 2 +- test/payments.utils.js | 4 ++-- test/script.js | 2 +- test/script_number.js | 2 +- test/script_signature.js | 2 +- test/transaction.js | 4 ++-- test/transaction_builder.js | 14 +++++++------- test/types.js | 2 +- tsconfig.json | 3 ++- 60 files changed, 145 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 52c7e8d..669233e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "bitcoinjs-lib", "version": "4.0.2", "description": "Client-side Bitcoin JavaScript library", - "main": "./src/index.js", + "main": "./dist/src/index.js", "engines": { "node": ">=8.0.0" }, @@ -17,17 +17,18 @@ "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", "coverage": "nyc --check-coverage --branches 90 --functions 90 mocha", - "integration": "mocha --timeout 50000 test/integration/", + "integration": "npm run build && mocha --timeout 50000 test/integration/", "standard": "standard", - "test": "npm run standard && npm run coverage", - "unit": "mocha" + "test": "npm run build && npm run standard && npm run coverage", + "unit": "npm run build && mocha", + "build": "tsc -p tsconfig.json" }, "repository": { "type": "git", "url": "https://github.com/bitcoinjs/bitcoinjs-lib.git" }, "files": [ - "src" + "dist/src" ], "dependencies": { "bech32": "^1.1.2", diff --git a/src/address.ts b/src/address.ts index e71bd46..e6f60fc 100644 --- a/src/address.ts +++ b/src/address.ts @@ -95,3 +95,4 @@ module.exports = { toBech32: toBech32, toOutputScript: toOutputScript } +export {} diff --git a/src/block.ts b/src/block.ts index eb7f708..1d69d0b 100644 --- a/src/block.ts +++ b/src/block.ts @@ -175,3 +175,4 @@ Block.prototype.checkProofOfWork = function () { } module.exports = Block +export {} diff --git a/src/bufferutils.ts b/src/bufferutils.ts index 48647d6..a78b07b 100644 --- a/src/bufferutils.ts +++ b/src/bufferutils.ts @@ -27,3 +27,4 @@ module.exports = { readUInt64LE: readUInt64LE, writeUInt64LE: writeUInt64LE } +export {} diff --git a/src/classify.ts b/src/classify.ts index 8350e07..3f824c4 100644 --- a/src/classify.ts +++ b/src/classify.ts @@ -68,3 +68,4 @@ module.exports = { witness: classifyWitness, types: types } +export {} diff --git a/src/crypto.ts b/src/crypto.ts index a11d061..a04c67f 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -27,3 +27,4 @@ module.exports = { sha1: sha1, sha256: sha256 } +export {} diff --git a/src/ecpair.ts b/src/ecpair.ts index 0e2cf26..0c9fd83 100644 --- a/src/ecpair.ts +++ b/src/ecpair.ts @@ -104,3 +104,4 @@ module.exports = { fromPublicKey, fromWIF } +export {} diff --git a/src/index.ts b/src/index.ts index 213e98a..967fddc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,3 +14,4 @@ module.exports = { payments: require('./payments'), script: script } +export {} diff --git a/src/networks.ts b/src/networks.ts index fb3957c..0aac048 100644 --- a/src/networks.ts +++ b/src/networks.ts @@ -36,3 +36,4 @@ module.exports = { wif: 0xef } } +export {} diff --git a/src/payments/embed.ts b/src/payments/embed.ts index ea2c8e9..bfb70e5 100644 --- a/src/payments/embed.ts +++ b/src/payments/embed.ts @@ -28,7 +28,10 @@ function p2data (a, opts) { }, a) const network = a.network || BITCOIN_NETWORK - const o = { network } + const o = { + network, + data: undefined + } lazy.prop(o, 'output', function () { if (!a.data) return @@ -54,3 +57,4 @@ function p2data (a, opts) { } module.exports = p2data +export {} diff --git a/src/payments/index.ts b/src/payments/index.ts index d445466..bb4e0e6 100644 --- a/src/payments/index.ts +++ b/src/payments/index.ts @@ -10,3 +10,4 @@ module.exports = { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } // TODO // witness commitment +export {} diff --git a/src/payments/lazy.ts b/src/payments/lazy.ts index 8538752..4a9611c 100644 --- a/src/payments/lazy.ts +++ b/src/payments/lazy.ts @@ -28,3 +28,4 @@ function value (f) { } module.exports = { prop, value } +export {} diff --git a/src/payments/p2ms.ts b/src/payments/p2ms.ts index 5c90a4d..39b38f1 100644 --- a/src/payments/p2ms.ts +++ b/src/payments/p2ms.ts @@ -42,7 +42,15 @@ function p2ms (a, opts) { }, a) const network = a.network || BITCOIN_NETWORK - const o = { network } + const o = { + network, + m: undefined, + n: undefined, + pubkeys: undefined, + output: undefined, + input: undefined, + signatures: undefined, + } let chunks let decoded = false @@ -129,7 +137,7 @@ function p2ms (a, opts) { if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') - if (a.signatures && !stacksEqual(a.signatures.equals(o.signatures))) throw new TypeError('Signature mismatch') + if (a.signatures && !stacksEqual(a.signatures.equals(o.signatures), undefined)) throw new TypeError('Signature mismatch') if (a.m !== undefined && a.m !== a.signatures.length) throw new TypeError('Signature count mismatch') } } @@ -138,3 +146,4 @@ function p2ms (a, opts) { } module.exports = p2ms +export {} diff --git a/src/payments/p2pk.ts b/src/payments/p2pk.ts index b930612..b810d48 100644 --- a/src/payments/p2pk.ts +++ b/src/payments/p2pk.ts @@ -30,7 +30,12 @@ function p2pk (a, opts) { const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) const network = a.network || BITCOIN_NETWORK - const o = { network } + const o = { + network, + input: undefined, + pubkey: undefined, + signature: undefined, + } lazy.prop(o, 'output', function () { if (!a.pubkey) return @@ -78,3 +83,4 @@ function p2pk (a, opts) { } module.exports = p2pk +export {} diff --git a/src/payments/p2pkh.ts b/src/payments/p2pkh.ts index e9248a2..c322fc7 100644 --- a/src/payments/p2pkh.ts +++ b/src/payments/p2pkh.ts @@ -40,7 +40,12 @@ function p2pkh (a, opts) { const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) const network = a.network || BITCOIN_NETWORK - const o = { network } + const o = { + network, + hash: undefined, + pubkey: undefined, + input: undefined, + } lazy.prop(o, 'address', function () { if (!o.hash) return @@ -135,3 +140,4 @@ function p2pkh (a, opts) { } module.exports = p2pkh +export {} diff --git a/src/payments/p2sh.ts b/src/payments/p2sh.ts index 0729c8b..07bb32e 100644 --- a/src/payments/p2sh.ts +++ b/src/payments/p2sh.ts @@ -50,7 +50,12 @@ function p2sh (a, opts) { network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK } - const o = { network } + const o = { + network, + hash: undefined, + redeem: undefined, + input: undefined, + } const _address = lazy.value(function () { const payload = bs58check.decode(a.address) @@ -191,3 +196,4 @@ function p2sh (a, opts) { } module.exports = p2sh +export {} diff --git a/src/payments/p2wpkh.ts b/src/payments/p2wpkh.ts index 1483bd8..1ab21b5 100644 --- a/src/payments/p2wpkh.ts +++ b/src/payments/p2wpkh.ts @@ -46,7 +46,12 @@ function p2wpkh (a, opts) { }) const network = a.network || BITCOIN_NETWORK - const o = { network } + const o = { + network, + hash: undefined, + pubkey: undefined, + witness: undefined, + } lazy.prop(o, 'address', function () { if (!o.hash) return @@ -133,3 +138,4 @@ function p2wpkh (a, opts) { } module.exports = p2wpkh +export {} diff --git a/src/payments/p2wsh.ts b/src/payments/p2wsh.ts index d8e6e8a..fe19314 100644 --- a/src/payments/p2wsh.ts +++ b/src/payments/p2wsh.ts @@ -64,7 +64,12 @@ function p2wsh (a, opts) { network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK } - const o = { network } + const o = { + network, + hash: undefined, + redeem: undefined, + witness: undefined, + } lazy.prop(o, 'address', function () { if (!o.hash) return @@ -178,3 +183,4 @@ function p2wsh (a, opts) { } module.exports = p2wsh +export {} diff --git a/src/script.ts b/src/script.ts index ad7c4e4..514e992 100644 --- a/src/script.ts +++ b/src/script.ts @@ -203,3 +203,4 @@ module.exports = { isPushOnly: isPushOnly, isDefinedHashType: isDefinedHashType } +export {} diff --git a/src/script_number.ts b/src/script_number.ts index 3440797..4df4704 100644 --- a/src/script_number.ts +++ b/src/script_number.ts @@ -65,3 +65,4 @@ module.exports = { decode: decode, encode: encode } +export {} diff --git a/src/script_signature.ts b/src/script_signature.ts index bdb3ddb..b1f933a 100644 --- a/src/script_signature.ts +++ b/src/script_signature.ts @@ -62,3 +62,4 @@ module.exports = { decode: decode, encode: encode } +export {} diff --git a/src/templates/multisig/index.ts b/src/templates/multisig/index.ts index 46863d6..7192cfd 100644 --- a/src/templates/multisig/index.ts +++ b/src/templates/multisig/index.ts @@ -2,3 +2,4 @@ module.exports = { input: require('./input'), output: require('./output') } +export {} diff --git a/src/templates/multisig/input.ts b/src/templates/multisig/input.ts index a66f05f..d395108 100644 --- a/src/templates/multisig/input.ts +++ b/src/templates/multisig/input.ts @@ -21,3 +21,4 @@ function check (script, allowIncomplete) { check.toJSON = function () { return 'multisig input' } module.exports = { check } +export {} diff --git a/src/templates/multisig/output.ts b/src/templates/multisig/output.ts index 5c9d81f..5ff18f1 100644 --- a/src/templates/multisig/output.ts +++ b/src/templates/multisig/output.ts @@ -27,3 +27,4 @@ function check (script, allowIncomplete) { check.toJSON = function () { return 'multi-sig output' } module.exports = { check } +export {} diff --git a/src/templates/nulldata.ts b/src/templates/nulldata.ts index d42fd71..145b504 100644 --- a/src/templates/nulldata.ts +++ b/src/templates/nulldata.ts @@ -12,3 +12,4 @@ function check (script) { check.toJSON = function () { return 'null data output' } module.exports = { output: { check: check } } +export {} diff --git a/src/templates/pubkey/index.ts b/src/templates/pubkey/index.ts index 46863d6..7192cfd 100644 --- a/src/templates/pubkey/index.ts +++ b/src/templates/pubkey/index.ts @@ -2,3 +2,4 @@ module.exports = { input: require('./input'), output: require('./output') } +export {} diff --git a/src/templates/pubkey/input.ts b/src/templates/pubkey/input.ts index ec21155..b6963e7 100644 --- a/src/templates/pubkey/input.ts +++ b/src/templates/pubkey/input.ts @@ -13,3 +13,4 @@ check.toJSON = function () { return 'pubKey input' } module.exports = { check: check } +export {} diff --git a/src/templates/pubkey/output.ts b/src/templates/pubkey/output.ts index b25c8c1..b64c596 100644 --- a/src/templates/pubkey/output.ts +++ b/src/templates/pubkey/output.ts @@ -13,3 +13,4 @@ function check (script) { check.toJSON = function () { return 'pubKey output' } module.exports = { check } +export {} diff --git a/src/templates/pubkeyhash/index.ts b/src/templates/pubkeyhash/index.ts index 46863d6..7192cfd 100644 --- a/src/templates/pubkeyhash/index.ts +++ b/src/templates/pubkeyhash/index.ts @@ -2,3 +2,4 @@ module.exports = { input: require('./input'), output: require('./output') } +export {} diff --git a/src/templates/pubkeyhash/input.ts b/src/templates/pubkeyhash/input.ts index f5bc452..cab6f0a 100644 --- a/src/templates/pubkeyhash/input.ts +++ b/src/templates/pubkeyhash/input.ts @@ -12,3 +12,4 @@ function check (script) { check.toJSON = function () { return 'pubKeyHash input' } module.exports = { check } +export {} diff --git a/src/templates/pubkeyhash/output.ts b/src/templates/pubkeyhash/output.ts index fbb6ed1..bf34b83 100644 --- a/src/templates/pubkeyhash/output.ts +++ b/src/templates/pubkeyhash/output.ts @@ -16,3 +16,4 @@ function check (script) { check.toJSON = function () { return 'pubKeyHash output' } module.exports = { check } +export {} diff --git a/src/templates/scripthash/index.ts b/src/templates/scripthash/index.ts index 46863d6..7192cfd 100644 --- a/src/templates/scripthash/index.ts +++ b/src/templates/scripthash/index.ts @@ -2,3 +2,4 @@ module.exports = { input: require('./input'), output: require('./output') } +export {} diff --git a/src/templates/scripthash/input.ts b/src/templates/scripthash/input.ts index 5164370..ad53df9 100644 --- a/src/templates/scripthash/input.ts +++ b/src/templates/scripthash/input.ts @@ -46,3 +46,4 @@ function check (script, allowIncomplete) { check.toJSON = function () { return 'scriptHash input' } module.exports = { check } +export {} diff --git a/src/templates/scripthash/output.ts b/src/templates/scripthash/output.ts index b5b6e7a..9f91f17 100644 --- a/src/templates/scripthash/output.ts +++ b/src/templates/scripthash/output.ts @@ -14,3 +14,4 @@ function check (script) { check.toJSON = function () { return 'scriptHash output' } module.exports = { check } +export {} diff --git a/src/templates/witnesscommitment/index.ts b/src/templates/witnesscommitment/index.ts index d459038..d274d6b 100644 --- a/src/templates/witnesscommitment/index.ts +++ b/src/templates/witnesscommitment/index.ts @@ -1,3 +1,4 @@ module.exports = { output: require('./output') } +export {} diff --git a/src/templates/witnesscommitment/output.ts b/src/templates/witnesscommitment/output.ts index 8938f45..db92e54 100644 --- a/src/templates/witnesscommitment/output.ts +++ b/src/templates/witnesscommitment/output.ts @@ -40,3 +40,4 @@ module.exports = { decode: decode, encode: encode } +export {} diff --git a/src/templates/witnesspubkeyhash/index.ts b/src/templates/witnesspubkeyhash/index.ts index 46863d6..7192cfd 100644 --- a/src/templates/witnesspubkeyhash/index.ts +++ b/src/templates/witnesspubkeyhash/index.ts @@ -2,3 +2,4 @@ module.exports = { input: require('./input'), output: require('./output') } +export {} diff --git a/src/templates/witnesspubkeyhash/input.ts b/src/templates/witnesspubkeyhash/input.ts index 488e5e6..ec93c47 100644 --- a/src/templates/witnesspubkeyhash/input.ts +++ b/src/templates/witnesspubkeyhash/input.ts @@ -16,3 +16,4 @@ function check (script) { check.toJSON = function () { return 'witnessPubKeyHash input' } module.exports = { check } +export {} diff --git a/src/templates/witnesspubkeyhash/output.ts b/src/templates/witnesspubkeyhash/output.ts index 08af3bc..a289038 100644 --- a/src/templates/witnesspubkeyhash/output.ts +++ b/src/templates/witnesspubkeyhash/output.ts @@ -15,3 +15,4 @@ check.toJSON = function () { return 'Witness pubKeyHash output' } module.exports = { check } +export {} diff --git a/src/templates/witnessscripthash/index.ts b/src/templates/witnessscripthash/index.ts index 46863d6..7192cfd 100644 --- a/src/templates/witnessscripthash/index.ts +++ b/src/templates/witnessscripthash/index.ts @@ -2,3 +2,4 @@ module.exports = { input: require('./input'), output: require('./output') } +export {} diff --git a/src/templates/witnessscripthash/input.ts b/src/templates/witnessscripthash/input.ts index 072a289..c6d47e3 100644 --- a/src/templates/witnessscripthash/input.ts +++ b/src/templates/witnessscripthash/input.ts @@ -37,3 +37,4 @@ function check (chunks, allowIncomplete) { check.toJSON = function () { return 'witnessScriptHash input' } module.exports = { check } +export {} diff --git a/src/templates/witnessscripthash/output.ts b/src/templates/witnessscripthash/output.ts index c9fc21a..3bc327f 100644 --- a/src/templates/witnessscripthash/output.ts +++ b/src/templates/witnessscripthash/output.ts @@ -13,3 +13,4 @@ function check (script) { check.toJSON = function () { return 'Witness scriptHash output' } module.exports = { check } +export {} diff --git a/src/transaction.ts b/src/transaction.ts index 751446f..4ee08d3 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -138,7 +138,7 @@ Transaction.fromBuffer = function (buffer, __noStrict) { } Transaction.fromHex = function (hex) { - return Transaction.fromBuffer(Buffer.from(hex, 'hex')) + return Transaction.fromBuffer(Buffer.from(hex, 'hex'), undefined) } Transaction.isCoinbaseHash = function (buffer) { @@ -490,3 +490,4 @@ Transaction.prototype.setWitness = function (index, witness) { } module.exports = Transaction +export {} diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index bea1ded..56368bf 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -102,7 +102,7 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) { const outputType = classify.output(redeem.output) let expanded if (outputType === SCRIPT_TYPES.P2WPKH) { - expanded = expandInput(redeem.input, redeem.witness, outputType) + expanded = expandInput(redeem.input, redeem.witness, outputType, undefined) } else { expanded = expandInput(bscript.compile(redeem.witness), [], outputType, redeem.output) } @@ -473,7 +473,7 @@ TransactionBuilder.prototype.setVersion = function (version) { } TransactionBuilder.fromTransaction = function (transaction, network) { - const txb = new TransactionBuilder(network) + const txb = new TransactionBuilder(network, undefined) // Copy transaction fields txb.setVersion(transaction.version) @@ -537,11 +537,17 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) const prevTxOut = txHash.toString('hex') + ':' + vout if (this.__prevTxSet[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut) - let input = {} + let input = { + value: undefined, + prevOutScript: undefined, + pubkeys: undefined, + signatures: undefined, + prevOutType: undefined, + } // derive what we can from the scriptSig if (options.script !== undefined) { - input = expandInput(options.script, options.witness || []) + input = expandInput(options.script, options.witness || [], undefined, undefined) } // if an input value was given, retain it @@ -554,7 +560,7 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) let prevOutType if (!input.pubkeys && !input.signatures) { - const expanded = expandOutput(options.prevOutScript) + const expanded = expandOutput(options.prevOutScript, undefined) if (expanded.pubkeys) { input.pubkeys = expanded.pubkeys input.signatures = expanded.signatures @@ -705,10 +711,10 @@ function signatureHashType (buffer) { } TransactionBuilder.prototype.__canModifyInputs = function () { - return this.__inputs.every(function (input) { + return (this.__inputs || []).every(function (input) { if (!input.signatures) return true - return input.signatures.every(function (signature) { + return (input.signatures || []).every(function (signature) { if (!signature) return true const hashType = signatureHashType(signature) @@ -775,3 +781,4 @@ TransactionBuilder.prototype.__overMaximumFees = function (bytes) { } module.exports = TransactionBuilder +export {} diff --git a/src/types.ts b/src/types.ts index 2d1ec6f..9fff4a6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -47,3 +47,4 @@ for (var typeName in typeforce) { } module.exports = types +export {} diff --git a/test/address.js b/test/address.js index a0f4df0..19093b6 100644 --- a/test/address.js +++ b/test/address.js @@ -1,7 +1,7 @@ const { describe, it } = require('mocha') const assert = require('assert') -const baddress = require('../src/address') -const bscript = require('../src/script') +const baddress = require('../dist/src/address') +const bscript = require('../dist/src/script') const fixtures = require('./fixtures/address.json') const NETWORKS = Object.assign({ litecoin: { @@ -14,7 +14,7 @@ const NETWORKS = Object.assign({ scriptHash: 0x32, wif: 0xb0 } -}, require('../src/networks')) +}, require('../dist/src/networks')) describe('address', function () { describe('fromBase58Check', function () { diff --git a/test/block.js b/test/block.js index d9ff405..eb52c0c 100644 --- a/test/block.js +++ b/test/block.js @@ -1,6 +1,6 @@ const { describe, it, beforeEach } = require('mocha') const assert = require('assert') -const Block = require('../src/block') +const Block = require('../dist/src/block') const fixtures = require('./fixtures/block') diff --git a/test/bufferutils.js b/test/bufferutils.js index 5f2c39e..ceb649a 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bufferutils = require('../src/bufferutils') +const bufferutils = require('../dist/src/bufferutils') const fixtures = require('./fixtures/bufferutils.json') diff --git a/test/classify.js b/test/classify.js index 3efcc74..33225b5 100644 --- a/test/classify.js +++ b/test/classify.js @@ -1,18 +1,18 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bscript = require('../src/script') -const classify = require('../src/classify') +const bscript = require('../dist/src/script') +const classify = require('../dist/src/classify') const fixtures = require('./fixtures/templates.json') -const multisig = require('../src/templates/multisig') -const nullData = require('../src/templates/nulldata') -const pubKey = require('../src/templates/pubkey') -const pubKeyHash = require('../src/templates/pubkeyhash') -const scriptHash = require('../src/templates/scripthash') -const witnessPubKeyHash = require('../src/templates/witnesspubkeyhash') -const witnessScriptHash = require('../src/templates/witnessscripthash') -const witnessCommitment = require('../src/templates/witnesscommitment') +const multisig = require('../dist/src/templates/multisig') +const nullData = require('../dist/src/templates/nulldata') +const pubKey = require('../dist/src/templates/pubkey') +const pubKeyHash = require('../dist/src/templates/pubkeyhash') +const scriptHash = require('../dist/src/templates/scripthash') +const witnessPubKeyHash = require('../dist/src/templates/witnesspubkeyhash') +const witnessScriptHash = require('../dist/src/templates/witnessscripthash') +const witnessCommitment = require('../dist/src/templates/witnesscommitment') const tmap = { pubKey, diff --git a/test/crypto.js b/test/crypto.js index 3f7802a..0a01c98 100644 --- a/test/crypto.js +++ b/test/crypto.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bcrypto = require('../src/crypto') +const bcrypto = require('../dist/src/crypto') const fixtures = require('./fixtures/crypto') diff --git a/test/ecpair.js b/test/ecpair.js index 45e646d..75a2e81 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -5,12 +5,12 @@ const assert = require('assert') const proxyquire = require('proxyquire') const hoodwink = require('hoodwink') -const ECPair = require('../src/ecpair') +const ECPair = require('../dist/src/ecpair') const tinysecp = require('tiny-secp256k1') const fixtures = require('./fixtures/ecpair.json') -const NETWORKS = require('../src/networks') +const NETWORKS = require('../dist/src/networks') const NETWORKS_LIST = [] // Object.values(NETWORKS) for (let networkName in NETWORKS) { NETWORKS_LIST.push(NETWORKS[networkName]) @@ -144,7 +144,7 @@ describe('ECPair', function () { describe('uses randombytes RNG', function () { it('generates a ECPair', function () { const stub = { randombytes: function () { return d } } - const ProxiedECPair = proxyquire('../src/ecpair', stub) + const ProxiedECPair = proxyquire('../dist/src/ecpair', stub) const keyPair = ProxiedECPair.makeRandom() assert.strictEqual(keyPair.toWIF(), exWIF) diff --git a/test/payments.js b/test/payments.js index 3c07bf3..53e3f16 100644 --- a/test/payments.js +++ b/test/payments.js @@ -4,7 +4,7 @@ const u = require('./payments.utils') ;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { describe(p, function () { - const fn = require('../src/payments/' + p) + const fn = require('../dist/src/payments/' + p) const fixtures = require('./fixtures/' + p) fixtures.valid.forEach(function (f, i) { diff --git a/test/payments.utils.js b/test/payments.utils.js index 485bf03..086561d 100644 --- a/test/payments.utils.js +++ b/test/payments.utils.js @@ -1,6 +1,6 @@ const t = require('assert') -const bscript = require('../src/script') -const BNETWORKS = require('../src/networks') +const bscript = require('../dist/src/script') +const BNETWORKS = require('../dist/src/networks') function tryHex (x) { if (Buffer.isBuffer(x)) return x.toString('hex') diff --git a/test/script.js b/test/script.js index c2a60ad..013d50a 100644 --- a/test/script.js +++ b/test/script.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bscript = require('../src/script') +const bscript = require('../dist/src/script') const minimalData = require('minimaldata') const fixtures = require('./fixtures/script.json') diff --git a/test/script_number.js b/test/script_number.js index bc8f395..1d38e48 100644 --- a/test/script_number.js +++ b/test/script_number.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const scriptNumber = require('../src/script_number') +const scriptNumber = require('../dist/src/script_number') const fixtures = require('./fixtures/script_number.json') describe('script-number', function () { diff --git a/test/script_signature.js b/test/script_signature.js index cee69bd..e56ff68 100644 --- a/test/script_signature.js +++ b/test/script_signature.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bscriptSig = require('../src/script').signature +const bscriptSig = require('../dist/src/script').signature const Buffer = require('safe-buffer').Buffer const fixtures = require('./fixtures/signature.json') diff --git a/test/transaction.js b/test/transaction.js index f8b7de9..f315a7d 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -1,8 +1,8 @@ const { describe, it, beforeEach } = require('mocha') const assert = require('assert') -const bscript = require('../src/script') +const bscript = require('../dist/src/script') const fixtures = require('./fixtures/transaction') -const Transaction = require('../src/transaction') +const Transaction = require('../dist/src/transaction') describe('Transaction', function () { function fromRaw (raw, noWitness) { diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 85adcff..a4b309a 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -1,13 +1,13 @@ const { describe, it, beforeEach } = require('mocha') const assert = require('assert') -const baddress = require('../src/address') -const bscript = require('../src/script') -const payments = require('../src/payments') +const baddress = require('../dist/src/address') +const bscript = require('../dist/src/script') +const payments = require('../dist/src/payments') -const ECPair = require('../src/ecpair') -const Transaction = require('../src/transaction') -const TransactionBuilder = require('../src/transaction_builder') -const NETWORKS = require('../src/networks') +const ECPair = require('../dist/src/ecpair') +const Transaction = require('../dist/src/transaction') +const TransactionBuilder = require('../dist/src/transaction_builder') +const NETWORKS = require('../dist/src/networks') const fixtures = require('./fixtures/transaction_builder') diff --git a/test/types.js b/test/types.js index d245d53..831d415 100644 --- a/test/types.js +++ b/test/types.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const types = require('../src/types') +const types = require('../dist/src/types') const typeforce = require('typeforce') describe('types', function () { diff --git a/tsconfig.json b/tsconfig.json index 8e1edb4..f358d14 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,7 @@ "src/**/*" ], "exclude": [ - "**/*.spec.ts" + "**/*.spec.ts", + "node_modules/**/*" ] } From b4d54af0fe438031b348b52c18554c24f191e10d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 26 Dec 2018 18:37:09 +0900 Subject: [PATCH 169/568] Remove AllowJS and get definitions --- package.json | 2 +- src/templates/nulldata.ts | 9 ++++++--- src/types.ts | 38 ++++++++++++-------------------------- tsconfig.json | 6 +++--- 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 669233e..e704249 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "standard": "standard", "test": "npm run build && npm run standard && npm run coverage", "unit": "npm run build && mocha", - "build": "tsc -p tsconfig.json" + "build": "tsc -d -p tsconfig.json" }, "repository": { "type": "git", diff --git a/src/templates/nulldata.ts b/src/templates/nulldata.ts index 145b504..48b0365 100644 --- a/src/templates/nulldata.ts +++ b/src/templates/nulldata.ts @@ -3,7 +3,7 @@ const bscript = require('../script') const OPS = require('bitcoin-ops') -function check (script) { +export function check (script) { const buffer = bscript.compile(script) return buffer.length > 1 && @@ -11,5 +11,8 @@ function check (script) { } check.toJSON = function () { return 'null data output' } -module.exports = { output: { check: check } } -export {} +const output = { check } + +export { + output +} diff --git a/src/types.ts b/src/types.ts index 9fff4a6..57431ca 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,25 +1,25 @@ const typeforce = require('typeforce') -const UINT31_MAX = Math.pow(2, 31) - 1 -function UInt31 (value) { +const UINT31_MAX: number = Math.pow(2, 31) - 1 +export function UInt31 (value: number): boolean { return typeforce.UInt32(value) && value <= UINT31_MAX } -function BIP32Path (value) { - return typeforce.String(value) && value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) +export function BIP32Path (value: string): boolean { + return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) } BIP32Path.toJSON = function () { return 'BIP32 derivation path' } -const SATOSHI_MAX = 21 * 1e14 -function Satoshi (value) { +const SATOSHI_MAX: number = 21 * 1e14 +export function Satoshi (value: number): boolean { return typeforce.UInt53(value) && value <= SATOSHI_MAX } // external dependent types -const ECPoint = typeforce.quacksLike('Point') +export const ECPoint = typeforce.quacksLike('Point') // exposed, external API -const Network = typeforce.compile({ +export const Network = typeforce.compile({ messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), bip32: { public: typeforce.UInt32, @@ -30,21 +30,7 @@ const Network = typeforce.compile({ wif: typeforce.UInt8 }) -// extend typeforce types with ours -const types = { - BIP32Path: BIP32Path, - Buffer256bit: typeforce.BufferN(32), - ECPoint: ECPoint, - Hash160bit: typeforce.BufferN(20), - Hash256bit: typeforce.BufferN(32), - Network: Network, - Satoshi: Satoshi, - UInt31: UInt31 -} - -for (var typeName in typeforce) { - types[typeName] = typeforce[typeName] -} - -module.exports = types -export {} +export const Buffer256bit = typeforce.BufferN(32) +export const Hash160bit = typeforce.BufferN(20) +export const Hash256bit = typeforce.BufferN(32) +export * from 'typeforce' diff --git a/tsconfig.json b/tsconfig.json index f358d14..8803b83 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,12 +7,12 @@ "types": [ "node" ], - "allowJs": true, + "allowJs": false, "strict": false, "noImplicitAny": false, "strictNullChecks": false, - "strictFunctionTypes": false, - "strictBindCallApply": false, + "strictFunctionTypes": true, + "strictBindCallApply": true, "strictPropertyInitialization": false, "noImplicitThis": false, "alwaysStrict": false, From 037fbd898427926b83609473cd2420e4ee6469fd Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 26 Dec 2018 16:13:43 +0900 Subject: [PATCH 170/568] # This is a combination of 2 commits. # The first commit's message is: Add types to Networks and Addresses # This is the 2nd commit message: Added types --- src/address.ts | 48 +++++++++++++++++---------- src/bufferutils.ts | 12 ++----- src/classify.ts | 35 ++++++++++---------- src/crypto.ts | 19 +++-------- src/networks.ts | 82 ++++++++++++++++++++++++++-------------------- 5 files changed, 102 insertions(+), 94 deletions(-) diff --git a/src/address.ts b/src/address.ts index e6f60fc..b758a66 100644 --- a/src/address.ts +++ b/src/address.ts @@ -6,8 +6,21 @@ const networks = require('./networks') const typeforce = require('typeforce') const types = require('./types') const payments = require('./payments') +import * as Networks from './networks' +import { Network } from './networks' -function fromBase58Check (address) { +export declare type Base58CheckResult = { + hash: Buffer; + version: number; +} + +export declare type Bech32Result = { + version: number; + prefix: string; + data: Buffer; +} + +function fromBase58Check (address: string): Base58CheckResult { const payload = bs58check.decode(address) // TODO: 4.0.0, move to "toOutputScript" @@ -20,7 +33,7 @@ function fromBase58Check (address) { return { version: version, hash: hash } } -function fromBech32 (address) { +function fromBech32 (address: string): Bech32Result { const result = bech32.decode(address) const data = bech32.fromWords(result.words.slice(1)) @@ -31,7 +44,7 @@ function fromBech32 (address) { } } -function toBase58Check (hash, version) { +function toBase58Check (hash: Buffer, version: number): string { typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments) const payload = Buffer.allocUnsafe(21) @@ -41,14 +54,14 @@ function toBase58Check (hash, version) { return bs58check.encode(payload) } -function toBech32 (data, version, prefix) { +function toBech32 (data: Buffer, version: number, prefix: string): string { const words = bech32.toWords(data) words.unshift(version) return bech32.encode(prefix, words) } -function fromOutputScript (output, network) { +function fromOutputScript (output: Buffer, network: Network): string { //TODO: Network network = network || networks.bitcoin try { return payments.p2pkh({ output, network }).address } catch (e) {} @@ -59,27 +72,28 @@ function fromOutputScript (output, network) { throw new Error(bscript.toASM(output) + ' has no matching Address') } -function toOutputScript (address, network) { +function toOutputScript (address: string, network: Network): Buffer { network = network || networks.bitcoin - let decode + let decodeBase58: Base58CheckResult + let decodeBech32: Bech32Result try { - decode = fromBase58Check(address) + decodeBase58 = fromBase58Check(address) } catch (e) {} - if (decode) { - if (decode.version === network.pubKeyHash) return payments.p2pkh({ hash: decode.hash }).output - if (decode.version === network.scriptHash) return payments.p2sh({ hash: decode.hash }).output + if (decodeBase58) { + if (decodeBase58.version === network.pubKeyHash) return payments.p2pkh({ hash: decodeBase58.hash }).output + if (decodeBase58.version === network.scriptHash) return payments.p2sh({ hash: decodeBase58.hash }).output } else { try { - decode = fromBech32(address) + decodeBech32 = fromBech32(address) } catch (e) {} - if (decode) { - if (decode.prefix !== network.bech32) throw new Error(address + ' has an invalid prefix') - if (decode.version === 0) { - if (decode.data.length === 20) return payments.p2wpkh({ hash: decode.data }).output - if (decode.data.length === 32) return payments.p2wsh({ hash: decode.data }).output + if (decodeBech32) { + if (decodeBech32.prefix !== network.bech32) throw new Error(address + ' has an invalid prefix') + if (decodeBech32.version === 0) { + if (decodeBech32.data.length === 20) return payments.p2wpkh({ hash: decodeBech32.data }).output + if (decodeBech32.data.length === 32) return payments.p2wsh({ hash: decodeBech32.data }).output } } } diff --git a/src/bufferutils.ts b/src/bufferutils.ts index a78b07b..b66c84a 100644 --- a/src/bufferutils.ts +++ b/src/bufferutils.ts @@ -1,12 +1,12 @@ // https://github.com/feross/buffer/blob/master/index.js#L1127 -function verifuint (value, max) { +function verifuint (value: number, max: number): void { if (typeof value !== 'number') throw new Error('cannot write a non-number as a number') if (value < 0) throw new Error('specified a negative value for writing an unsigned value') if (value > max) throw new Error('RangeError: value out of range') if (Math.floor(value) !== value) throw new Error('value has a fractional component') } -function readUInt64LE (buffer, offset) { +export function readUInt64LE (buffer: Buffer, offset: number): number { const a = buffer.readUInt32LE(offset) let b = buffer.readUInt32LE(offset + 4) b *= 0x100000000 @@ -15,16 +15,10 @@ function readUInt64LE (buffer, offset) { return b + a } -function writeUInt64LE (buffer, value, offset) { +export function writeUInt64LE (buffer: Buffer, value: number, offset: number): number { verifuint(value, 0x001fffffffffffff) buffer.writeInt32LE(value & -1, offset) buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4) return offset + 8 } - -module.exports = { - readUInt64LE: readUInt64LE, - writeUInt64LE: writeUInt64LE -} -export {} diff --git a/src/classify.ts b/src/classify.ts index 3f824c4..5eabc7e 100644 --- a/src/classify.ts +++ b/src/classify.ts @@ -9,18 +9,18 @@ const witnessScriptHash = require('./templates/witnessscripthash') const witnessCommitment = require('./templates/witnesscommitment') const types = { - P2MS: 'multisig', - NONSTANDARD: 'nonstandard', - NULLDATA: 'nulldata', - P2PK: 'pubkey', - P2PKH: 'pubkeyhash', - P2SH: 'scripthash', - P2WPKH: 'witnesspubkeyhash', - P2WSH: 'witnessscripthash', - WITNESS_COMMITMENT: 'witnesscommitment' + P2MS: <string> 'multisig', + NONSTANDARD: <string> 'nonstandard', + NULLDATA: <string> 'nulldata', + P2PK: <string> 'pubkey', + P2PKH: <string> 'pubkeyhash', + P2SH: <string> 'scripthash', + P2WPKH: <string> 'witnesspubkeyhash', + P2WSH: <string> 'witnessscripthash', + WITNESS_COMMITMENT: <string> 'witnesscommitment' } -function classifyOutput (script) { +function classifyOutput (script: Buffer): string { if (witnessPubKeyHash.output.check(script)) return types.P2WPKH if (witnessScriptHash.output.check(script)) return types.P2WSH if (pubKeyHash.output.check(script)) return types.P2PKH @@ -38,7 +38,7 @@ function classifyOutput (script) { return types.NONSTANDARD } -function classifyInput (script, allowIncomplete) { +function classifyInput (script: Buffer, allowIncomplete: boolean): string { // XXX: optimization, below functions .decompile before use const chunks = decompile(script) if (!chunks) throw new TypeError('Invalid script') @@ -51,7 +51,7 @@ function classifyInput (script, allowIncomplete) { return types.NONSTANDARD } -function classifyWitness (script, allowIncomplete) { +function classifyWitness (script: Buffer, allowIncomplete: boolean): string { // XXX: optimization, below functions .decompile before use const chunks = decompile(script) if (!chunks) throw new TypeError('Invalid script') @@ -62,10 +62,9 @@ function classifyWitness (script, allowIncomplete) { return types.NONSTANDARD } -module.exports = { - input: classifyInput, - output: classifyOutput, - witness: classifyWitness, - types: types +export { + classifyInput as input, + classifyOutput as output, + classifyWitness as witness, + types, } -export {} diff --git a/src/crypto.ts b/src/crypto.ts index a04c67f..6aabb5b 100644 --- a/src/crypto.ts +++ b/src/crypto.ts @@ -1,30 +1,21 @@ const createHash = require('create-hash') -function ripemd160 (buffer) { +export function ripemd160 (buffer: Buffer): Buffer { return createHash('rmd160').update(buffer).digest() } -function sha1 (buffer) { +export function sha1 (buffer: Buffer): Buffer { return createHash('sha1').update(buffer).digest() } -function sha256 (buffer) { +export function sha256 (buffer: Buffer): Buffer { return createHash('sha256').update(buffer).digest() } -function hash160 (buffer) { +export function hash160 (buffer: Buffer): Buffer { return ripemd160(sha256(buffer)) } -function hash256 (buffer) { +export function hash256 (buffer: Buffer): Buffer { return sha256(sha256(buffer)) } - -module.exports = { - hash160: hash160, - hash256: hash256, - ripemd160: ripemd160, - sha1: sha1, - sha256: sha256 -} -export {} diff --git a/src/networks.ts b/src/networks.ts index 0aac048..f8ee1aa 100644 --- a/src/networks.ts +++ b/src/networks.ts @@ -1,39 +1,49 @@ // https://en.bitcoin.it/wiki/List_of_address_prefixes // Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731 - -module.exports = { - bitcoin: { - messagePrefix: '\x18Bitcoin Signed Message:\n', - bech32: 'bc', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4 - }, - pubKeyHash: 0x00, - scriptHash: 0x05, - wif: 0x80 - }, - regtest: { - messagePrefix: '\x18Bitcoin Signed Message:\n', - bech32: 'bcrt', - bip32: { - public: 0x043587cf, - private: 0x04358394 - }, - pubKeyHash: 0x6f, - scriptHash: 0xc4, - wif: 0xef - }, - testnet: { - messagePrefix: '\x18Bitcoin Signed Message:\n', - bech32: 'tb', - bip32: { - public: 0x043587cf, - private: 0x04358394 - }, - pubKeyHash: 0x6f, - scriptHash: 0xc4, - wif: 0xef - } +export declare type Network = { + messagePrefix: string; + bech32: string; + bip32: bip32; + pubKeyHash: number; + scriptHash: number; + wif: number; +} + +declare type bip32 = { + public: number; + private: number; +} + +export const bitcoin: Network = { + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'bc', + bip32: { + public: 0x0488b21e, + private: 0x0488ade4 + }, + pubKeyHash: 0x00, + scriptHash: 0x05, + wif: 0x80 +} +export const regtest: Network = { + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'bcrt', + bip32: { + public: 0x043587cf, + private: 0x04358394 + }, + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef +} +export const testnet: Network = { + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'tb', + bip32: { + public: 0x043587cf, + private: 0x04358394 + }, + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef } -export {} From 91d3037cf3b58420f7cfc535cd8b128eab8c0f0f Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 27 Dec 2018 17:15:24 +0900 Subject: [PATCH 171/568] Fix some exports --- src/address.ts | 26 ++++++++------------------ src/index.ts | 37 +++++++++++++++++++++++-------------- src/networks.ts | 4 ++-- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/address.ts b/src/address.ts index b758a66..e657d24 100644 --- a/src/address.ts +++ b/src/address.ts @@ -9,18 +9,18 @@ const payments = require('./payments') import * as Networks from './networks' import { Network } from './networks' -export declare type Base58CheckResult = { +export type Base58CheckResult = { hash: Buffer; version: number; } -export declare type Bech32Result = { +export type Bech32Result = { version: number; prefix: string; data: Buffer; } -function fromBase58Check (address: string): Base58CheckResult { +export function fromBase58Check (address: string): Base58CheckResult { const payload = bs58check.decode(address) // TODO: 4.0.0, move to "toOutputScript" @@ -33,7 +33,7 @@ function fromBase58Check (address: string): Base58CheckResult { return { version: version, hash: hash } } -function fromBech32 (address: string): Bech32Result { +export function fromBech32 (address: string): Bech32Result { const result = bech32.decode(address) const data = bech32.fromWords(result.words.slice(1)) @@ -44,7 +44,7 @@ function fromBech32 (address: string): Bech32Result { } } -function toBase58Check (hash: Buffer, version: number): string { +export function toBase58Check (hash: Buffer, version: number): string { typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments) const payload = Buffer.allocUnsafe(21) @@ -54,14 +54,14 @@ function toBase58Check (hash: Buffer, version: number): string { return bs58check.encode(payload) } -function toBech32 (data: Buffer, version: number, prefix: string): string { +export function toBech32 (data: Buffer, version: number, prefix: string): string { const words = bech32.toWords(data) words.unshift(version) return bech32.encode(prefix, words) } -function fromOutputScript (output: Buffer, network: Network): string { //TODO: Network +export function fromOutputScript (output: Buffer, network: Network): string { //TODO: Network network = network || networks.bitcoin try { return payments.p2pkh({ output, network }).address } catch (e) {} @@ -72,7 +72,7 @@ function fromOutputScript (output: Buffer, network: Network): string { //TODO: N throw new Error(bscript.toASM(output) + ' has no matching Address') } -function toOutputScript (address: string, network: Network): Buffer { +export function toOutputScript (address: string, network: Network): Buffer { network = network || networks.bitcoin let decodeBase58: Base58CheckResult @@ -100,13 +100,3 @@ function toOutputScript (address: string, network: Network): Buffer { throw new Error(address + ' has no matching Script') } - -module.exports = { - fromBase58Check: fromBase58Check, - fromBech32: fromBech32, - fromOutputScript: fromOutputScript, - toBase58Check: toBase58Check, - toBech32: toBech32, - toOutputScript: toOutputScript -} -export {} diff --git a/src/index.ts b/src/index.ts index 967fddc..d1b897b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,26 @@ -const script = require('./script') +const opcodes = require('bitcoin-ops') -module.exports = { - Block: require('./block'), - ECPair: require('./ecpair'), - Transaction: require('./transaction'), - TransactionBuilder: require('./transaction_builder'), +import * as Block from './block' +import * as ECPair from './ecpair' +import * as Transaction from './transaction' +import * as TransactionBuilder from './transaction_builder' +import * as address from './address' +import * as bip32 from 'bip32' +import * as crypto from './crypto' +import * as networks from './networks' +import * as payments from './payments' +import * as script from './script' - address: require('./address'), - bip32: require('bip32'), - crypto: require('./crypto'), - networks: require('./networks'), - opcodes: require('bitcoin-ops'), - payments: require('./payments'), - script: script +export { + Block, + ECPair, + Transaction, + TransactionBuilder, + address, + bip32, + crypto, + networks, + opcodes, + payments, + script, } -export {} diff --git a/src/networks.ts b/src/networks.ts index f8ee1aa..11147af 100644 --- a/src/networks.ts +++ b/src/networks.ts @@ -1,6 +1,6 @@ // https://en.bitcoin.it/wiki/List_of_address_prefixes // Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731 -export declare type Network = { +export type Network = { messagePrefix: string; bech32: string; bip32: bip32; @@ -9,7 +9,7 @@ export declare type Network = { wif: number; } -declare type bip32 = { +type bip32 = { public: number; private: number; } From 2eb95349398ba011f63b9fcf03328ee9185281e6 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 27 Dec 2018 17:49:53 +0900 Subject: [PATCH 172/568] Move Block Class from ES6 PR --- src/block.ts | 342 +++++++++++++++++++++++++++++--------------------- src/index.ts | 2 +- test/block.js | 2 +- 3 files changed, 198 insertions(+), 148 deletions(-) diff --git a/src/block.ts b/src/block.ts index 1d69d0b..b2602dd 100644 --- a/src/block.ts +++ b/src/block.ts @@ -5,174 +5,224 @@ const typeforce = require('typeforce') const types = require('./types') const varuint = require('varuint-bitcoin') +const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions') +const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block') +const errorBufferTooSmall = new Error('Buffer too small (< 80 bytes)') + +function txesHaveWitness (transactions: Array<any>): boolean { + return transactions !== undefined && + transactions instanceof Array && + transactions[0] && + transactions[0].ins && + transactions[0].ins instanceof Array && + transactions[0].ins[0] && + transactions[0].ins[0].witness && + transactions[0].ins[0].witness instanceof Array && + transactions[0].ins[0].witness.length > 0 +} + const Transaction = require('./transaction') -function Block () { - this.version = 1 - this.prevHash = null - this.merkleRoot = null - this.timestamp = 0 - this.bits = 0 - this.nonce = 0 -} +export class Block { + version: number + prevHash: Buffer + merkleRoot: Buffer + timestamp: number + witnessCommit: Buffer + bits: number + nonce: number + transactions: Array<any> -Block.fromBuffer = function (buffer) { - if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)') - - let offset = 0 - function readSlice (n) { - offset += n - return buffer.slice(offset - n, offset) + constructor () { + this.version = 1 + this.timestamp = 0 + this.bits = 0 + this.nonce = 0 } - function readUInt32 () { - const i = buffer.readUInt32LE(offset) - offset += 4 - return i + static fromBuffer (buffer: Buffer): Block { + if (buffer.length < 80) throw errorBufferTooSmall + + let offset: number = 0 + const readSlice = (n: number): Buffer => { + offset += n + return buffer.slice(offset - n, offset) + } + + const readUInt32 = (): number => { + const i = buffer.readUInt32LE(offset) + offset += 4 + return i + } + + const readInt32 = (): number => { + const i = buffer.readInt32LE(offset) + offset += 4 + return i + } + + const block = new Block() + block.version = readInt32() + block.prevHash = readSlice(32) + block.merkleRoot = readSlice(32) + block.timestamp = readUInt32() + block.bits = readUInt32() + block.nonce = readUInt32() + + if (buffer.length === 80) return block + + const readVarInt = (): number => { + const vi = varuint.decode(buffer, offset) + offset += varuint.decode.bytes + return vi + } + + const readTransaction = (): any => { + const tx = Transaction.fromBuffer(buffer.slice(offset), true) + offset += tx.byteLength() + return tx + } + + const nTransactions = readVarInt() + block.transactions = [] + + for (var i = 0; i < nTransactions; ++i) { + const tx = readTransaction() + block.transactions.push(tx) + } + + // This Block contains a witness commit + if (block.hasWitnessCommit()) { + // The merkle root for the witness data is in an OP_RETURN output. + // There is no rule for the index of the output, so use filter to find it. + // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed + // If multiple commits are found, the output with highest index is assumed. + let witnessCommits = block.transactions[0].outs + .filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))) + .map(out => out.script.slice(6, 38)) + + // Use the commit with the highest output (should only be one though) + block.witnessCommit = witnessCommits[witnessCommits.length - 1] + } + + return block } - function readInt32 () { - const i = buffer.readInt32LE(offset) - offset += 4 - return i + static fromHex (hex: string): Block { + return Block.fromBuffer(Buffer.from(hex, 'hex')) } - const block = new Block() - block.version = readInt32() - block.prevHash = readSlice(32) - block.merkleRoot = readSlice(32) - block.timestamp = readUInt32() - block.bits = readUInt32() - block.nonce = readUInt32() - - if (buffer.length === 80) return block - - function readVarInt () { - const vi = varuint.decode(buffer, offset) - offset += varuint.decode.bytes - return vi + static calculateTarget (bits: number): Buffer { + const exponent = ((bits & 0xff000000) >> 24) - 3 + const mantissa = bits & 0x007fffff + const target = Buffer.alloc(32, 0) + target.writeUIntBE(mantissa, 29 - exponent, 3) + return target } - function readTransaction () { - const tx = Transaction.fromBuffer(buffer.slice(offset), true) - offset += tx.byteLength() - return tx + static calculateMerkleRoot (transactions: Array<any>, forWitness: boolean | void): Buffer { + typeforce([{ getHash: types.Function }], transactions) + if (transactions.length === 0) throw errorMerkleNoTxes + if (forWitness && !txesHaveWitness(transactions)) throw errorWitnessNotSegwit + + const hashes = transactions.map(transaction => transaction.getHash(forWitness)) + + const rootHash = fastMerkleRoot(hashes, bcrypto.hash256) + + return forWitness + ? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) + : rootHash } - const nTransactions = readVarInt() - block.transactions = [] - - for (var i = 0; i < nTransactions; ++i) { - const tx = readTransaction() - block.transactions.push(tx) + hasWitnessCommit (): boolean { + return txesHaveWitness(this.transactions) } - return block -} + byteLength (headersOnly: boolean): number { + if (headersOnly || !this.transactions) return 80 -Block.prototype.byteLength = function (headersOnly) { - if (headersOnly || !this.transactions) return 80 - - return 80 + varuint.encodingLength(this.transactions.length) + this.transactions.reduce(function (a, x) { - return a + x.byteLength() - }, 0) -} - -Block.fromHex = function (hex) { - return Block.fromBuffer(Buffer.from(hex, 'hex')) -} - -Block.prototype.getHash = function () { - return bcrypto.hash256(this.toBuffer(true)) -} - -Block.prototype.getId = function () { - return this.getHash().reverse().toString('hex') -} - -Block.prototype.getUTCDate = function () { - const date = new Date(0) // epoch - date.setUTCSeconds(this.timestamp) - - return date -} - -// TODO: buffer, offset compatibility -Block.prototype.toBuffer = function (headersOnly) { - const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)) - - let offset = 0 - function writeSlice (slice) { - slice.copy(buffer, offset) - offset += slice.length + return 80 + varuint.encodingLength(this.transactions.length) + + this.transactions.reduce((a, x) => a + x.byteLength(), 0) } - function writeInt32 (i) { - buffer.writeInt32LE(i, offset) - offset += 4 - } - function writeUInt32 (i) { - buffer.writeUInt32LE(i, offset) - offset += 4 + getHash (): Buffer { + return bcrypto.hash256(this.toBuffer(true)) } - writeInt32(this.version) - writeSlice(this.prevHash) - writeSlice(this.merkleRoot) - writeUInt32(this.timestamp) - writeUInt32(this.bits) - writeUInt32(this.nonce) + getId (): string { + return Buffer.from(this.getHash().reverse()).toString('hex') + } - if (headersOnly || !this.transactions) return buffer + getUTCDate (): Date { + const date = new Date(0) // epoch + date.setUTCSeconds(this.timestamp) - varuint.encode(this.transactions.length, buffer, offset) - offset += varuint.encode.bytes + return date + } - this.transactions.forEach(function (tx) { - const txSize = tx.byteLength() // TODO: extract from toBuffer? - tx.toBuffer(buffer, offset) - offset += txSize - }) + // TODO: buffer, offset compatibility + toBuffer (headersOnly: boolean): Buffer { + const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)) - return buffer + let offset: number = 0 + const writeSlice = (slice: Buffer): void => { + slice.copy(buffer, offset) + offset += slice.length + } + + const writeInt32 = (i: number): void => { + buffer.writeInt32LE(i, offset) + offset += 4 + } + const writeUInt32 = (i: number): void => { + buffer.writeUInt32LE(i, offset) + offset += 4 + } + + writeInt32(this.version) + writeSlice(this.prevHash) + writeSlice(this.merkleRoot) + writeUInt32(this.timestamp) + writeUInt32(this.bits) + writeUInt32(this.nonce) + + if (headersOnly || !this.transactions) return buffer + + varuint.encode(this.transactions.length, buffer, offset) + offset += varuint.encode.bytes + + this.transactions.forEach(tx => { + const txSize = tx.byteLength() // TODO: extract from toBuffer? + tx.toBuffer(buffer, offset) + offset += txSize + }) + + return buffer + } + + toHex (headersOnly: boolean): string { + return this.toBuffer(headersOnly).toString('hex') + } + + checkMerkleRoot (): boolean { + if (!this.transactions) throw errorMerkleNoTxes + + const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions) + return this.merkleRoot.compare(actualMerkleRoot) === 0 + } + + checkWitnessCommit (): boolean { + if (!this.transactions) throw errorMerkleNoTxes + if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit + + const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true) + return this.witnessCommit.compare(actualWitnessCommit) === 0 + } + + checkProofOfWork (): boolean { + const hash: Buffer = Buffer.from(this.getHash().reverse()) + const target = Block.calculateTarget(this.bits) + + return hash.compare(target) <= 0 + } } - -Block.prototype.toHex = function (headersOnly) { - return this.toBuffer(headersOnly).toString('hex') -} - -Block.calculateTarget = function (bits) { - const exponent = ((bits & 0xff000000) >> 24) - 3 - const mantissa = bits & 0x007fffff - const target = Buffer.alloc(32, 0) - target.writeUIntBE(mantissa, 29 - exponent, 3) - return target -} - -Block.calculateMerkleRoot = function (transactions) { - typeforce([{ getHash: types.Function }], transactions) - if (transactions.length === 0) throw TypeError('Cannot compute merkle root for zero transactions') - - const hashes = transactions.map(function (transaction) { - return transaction.getHash() - }) - - return fastMerkleRoot(hashes, bcrypto.hash256) -} - -Block.prototype.checkMerkleRoot = function () { - if (!this.transactions) return false - - const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions) - return this.merkleRoot.compare(actualMerkleRoot) === 0 -} - -Block.prototype.checkProofOfWork = function () { - const hash = this.getHash().reverse() - const target = Block.calculateTarget(this.bits) - - return hash.compare(target) <= 0 -} - -module.exports = Block -export {} diff --git a/src/index.ts b/src/index.ts index d1b897b..4d840a9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ const opcodes = require('bitcoin-ops') -import * as Block from './block' +import { Block } from './block' import * as ECPair from './ecpair' import * as Transaction from './transaction' import * as TransactionBuilder from './transaction_builder' diff --git a/test/block.js b/test/block.js index eb52c0c..07d0741 100644 --- a/test/block.js +++ b/test/block.js @@ -1,6 +1,6 @@ const { describe, it, beforeEach } = require('mocha') const assert = require('assert') -const Block = require('../dist/src/block') +const Block = require('..').Block const fixtures = require('./fixtures/block') From a652d0492dac9a8c05ae77da54471ff6eda13b8b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 27 Dec 2018 18:26:08 +0900 Subject: [PATCH 173/568] Added Transaction --- src/block.ts | 11 +- src/index.ts | 2 +- src/transaction.ts | 898 +++++++++++++++++++----------------- src/transaction_builder.ts | 7 +- test/block.js | 15 + test/fixtures/block.json | 15 + test/transaction.js | 2 +- test/transaction_builder.js | 2 +- 8 files changed, 529 insertions(+), 423 deletions(-) diff --git a/src/block.ts b/src/block.ts index b2602dd..92c4fb1 100644 --- a/src/block.ts +++ b/src/block.ts @@ -1,3 +1,4 @@ +import { Transaction } from './transaction' const Buffer = require('safe-buffer').Buffer const bcrypto = require('./crypto') const fastMerkleRoot = require('merkle-lib/fastRoot') @@ -9,7 +10,7 @@ const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero tra const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block') const errorBufferTooSmall = new Error('Buffer too small (< 80 bytes)') -function txesHaveWitness (transactions: Array<any>): boolean { +function txesHaveWitness (transactions: Array<Transaction>): boolean { return transactions !== undefined && transactions instanceof Array && transactions[0] && @@ -21,8 +22,6 @@ function txesHaveWitness (transactions: Array<any>): boolean { transactions[0].ins[0].witness.length > 0 } -const Transaction = require('./transaction') - export class Block { version: number prevHash: Buffer @@ -31,7 +30,7 @@ export class Block { witnessCommit: Buffer bits: number nonce: number - transactions: Array<any> + transactions: Array<Transaction> constructor () { this.version = 1 @@ -120,12 +119,12 @@ export class Block { return target } - static calculateMerkleRoot (transactions: Array<any>, forWitness: boolean | void): Buffer { + static calculateMerkleRoot (transactions: Array<Transaction>, forWitness: boolean | void): Buffer { typeforce([{ getHash: types.Function }], transactions) if (transactions.length === 0) throw errorMerkleNoTxes if (forWitness && !txesHaveWitness(transactions)) throw errorWitnessNotSegwit - const hashes = transactions.map(transaction => transaction.getHash(forWitness)) + const hashes = transactions.map(transaction => transaction.getHash((<boolean>forWitness))) const rootHash = fastMerkleRoot(hashes, bcrypto.hash256) diff --git a/src/index.ts b/src/index.ts index 4d840a9..389e2b1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ const opcodes = require('bitcoin-ops') import { Block } from './block' import * as ECPair from './ecpair' -import * as Transaction from './transaction' +import { Transaction } from './transaction' import * as TransactionBuilder from './transaction_builder' import * as address from './address' import * as bip32 from 'bip32' diff --git a/src/transaction.ts b/src/transaction.ts index 4ee08d3..88e4280 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -1,5 +1,5 @@ +import * as bcrypto from './crypto' const Buffer = require('safe-buffer').Buffer -const bcrypto = require('./crypto') const bscript = require('./script') const bufferutils = require('./bufferutils') const opcodes = require('bitcoin-ops') @@ -7,487 +7,563 @@ const typeforce = require('typeforce') const types = require('./types') const varuint = require('varuint-bitcoin') -function varSliceSize (someScript) { +function varSliceSize (someScript: Buffer): number { const length = someScript.length return varuint.encodingLength(length) + length } -function vectorSize (someVector) { +function vectorSize (someVector: Array<Buffer>): number { const length = someVector.length - return varuint.encodingLength(length) + someVector.reduce(function (sum, witness) { + return varuint.encodingLength(length) + someVector.reduce((sum, witness) => { return sum + varSliceSize(witness) }, 0) } -function Transaction () { - this.version = 1 - this.locktime = 0 - this.ins = [] - this.outs = [] -} - -Transaction.DEFAULT_SEQUENCE = 0xffffffff -Transaction.SIGHASH_ALL = 0x01 -Transaction.SIGHASH_NONE = 0x02 -Transaction.SIGHASH_SINGLE = 0x03 -Transaction.SIGHASH_ANYONECANPAY = 0x80 -Transaction.ADVANCED_TRANSACTION_MARKER = 0x00 -Transaction.ADVANCED_TRANSACTION_FLAG = 0x01 - -const EMPTY_SCRIPT = Buffer.allocUnsafe(0) -const EMPTY_WITNESS = [] -const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') -const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') -const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex') -const BLANK_OUTPUT = { +const EMPTY_SCRIPT: Buffer = Buffer.allocUnsafe(0) +const EMPTY_WITNESS: Array<Buffer> = [] +const ZERO: Buffer = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') +const ONE: Buffer = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') +const VALUE_UINT64_MAX: Buffer = Buffer.from('ffffffffffffffff', 'hex') +const BLANK_OUTPUT: BlankOutput = { script: EMPTY_SCRIPT, valueBuffer: VALUE_UINT64_MAX } -Transaction.fromBuffer = function (buffer, __noStrict) { - let offset = 0 - function readSlice (n) { - offset += n - return buffer.slice(offset - n, offset) +function isOutput(out: Output | BlankOutput): out is Output { + return (<Output>out).value !== undefined +} + +export type BlankOutput = { + script: Buffer + valueBuffer: Buffer +} + +export type Output = { + script: Buffer + value: number +} + +export type Input = { + hash: Buffer + index: number + script: Buffer + sequence: number + witness: Array<Buffer> +} + +export class Transaction { + version: number + locktime: number + ins: Array<Input> + outs: Array<Output | BlankOutput> + + static readonly DEFAULT_SEQUENCE = 0xffffffff + static readonly SIGHASH_ALL = 0x01 + static readonly SIGHASH_NONE = 0x02 + static readonly SIGHASH_SINGLE = 0x03 + static readonly SIGHASH_ANYONECANPAY = 0x80 + static readonly ADVANCED_TRANSACTION_MARKER = 0x00 + static readonly ADVANCED_TRANSACTION_FLAG = 0x01 + + constructor () { + this.version = 1 + this.locktime = 0 + this.ins = [] + this.outs = [] } + + static fromBuffer (buffer: Buffer, __noStrict: boolean): Transaction { + let offset: number = 0 - function readUInt32 () { - const i = buffer.readUInt32LE(offset) - offset += 4 - return i - } + function readSlice (n: number): Buffer { + offset += n + return buffer.slice(offset - n, offset) + } - function readInt32 () { - const i = buffer.readInt32LE(offset) - offset += 4 - return i - } + function readUInt32 (): number { + const i = buffer.readUInt32LE(offset) + offset += 4 + return i + } - function readUInt64 () { - const i = bufferutils.readUInt64LE(buffer, offset) - offset += 8 - return i - } + function readInt32 (): number { + const i = buffer.readInt32LE(offset) + offset += 4 + return i + } - function readVarInt () { - const vi = varuint.decode(buffer, offset) - offset += varuint.decode.bytes - return vi - } + function readUInt64 (): number { + const i = bufferutils.readUInt64LE(buffer, offset) + offset += 8 + return i + } - function readVarSlice () { - return readSlice(readVarInt()) - } + function readVarInt (): number { + const vi = varuint.decode(buffer, offset) + offset += varuint.decode.bytes + return vi + } - function readVector () { - const count = readVarInt() - const vector = [] - for (var i = 0; i < count; i++) vector.push(readVarSlice()) - return vector - } + function readVarSlice (): Buffer { + return readSlice(readVarInt()) + } - const tx = new Transaction() - tx.version = readInt32() + function readVector (): Array<Buffer> { + const count = readVarInt() + const vector: Array<Buffer> = [] + for (var i = 0; i < count; i++) vector.push(readVarSlice()) + return vector + } - const marker = buffer.readUInt8(offset) - const flag = buffer.readUInt8(offset + 1) + const tx = new Transaction() + tx.version = readInt32() - let hasWitnesses = false - if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && + const marker = buffer.readUInt8(offset) + const flag = buffer.readUInt8(offset + 1) + + let hasWitnesses = false + if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && flag === Transaction.ADVANCED_TRANSACTION_FLAG) { - offset += 2 - hasWitnesses = true + offset += 2 + hasWitnesses = true + } + + const vinLen = readVarInt() + for (var i = 0; i < vinLen; ++i) { + tx.ins.push({ + hash: readSlice(32), + index: readUInt32(), + script: readVarSlice(), + sequence: readUInt32(), + witness: EMPTY_WITNESS + }) + } + + const voutLen = readVarInt() + for (i = 0; i < voutLen; ++i) { + tx.outs.push({ + value: readUInt64(), + script: readVarSlice() + }) + } + + if (hasWitnesses) { + for (i = 0; i < vinLen; ++i) { + tx.ins[i].witness = readVector() + } + + // was this pointless? + if (!tx.hasWitnesses()) throw new Error('Transaction has superfluous witness data') + } + + tx.locktime = readUInt32() + + if (__noStrict) return tx + if (offset !== buffer.length) throw new Error('Transaction has unexpected data') + + return tx } - const vinLen = readVarInt() - for (var i = 0; i < vinLen; ++i) { - tx.ins.push({ - hash: readSlice(32), - index: readUInt32(), - script: readVarSlice(), - sequence: readUInt32(), + static fromHex (hex: string): Transaction { + return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false) + } + + static isCoinbaseHash (buffer: Buffer): boolean { + typeforce(types.Hash256bit, buffer) + for (var i = 0; i < 32; ++i) { + if (buffer[i] !== 0) return false + } + return true + } + + isCoinbase (): boolean { + return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) + } + + addInput (hash: Buffer, index: number, sequence: number, scriptSig: Buffer): number { + typeforce(types.tuple( + types.Hash256bit, + types.UInt32, + types.maybe(types.UInt32), + types.maybe(types.Buffer) + ), arguments) + + if (types.Null(sequence)) { + sequence = Transaction.DEFAULT_SEQUENCE + } + + // Add the input and return the input's index + return (this.ins.push({ + hash: hash, + index: index, + script: scriptSig || EMPTY_SCRIPT, + sequence: sequence, witness: EMPTY_WITNESS + }) - 1) + } + + addOutput (scriptPubKey: Buffer, value: number): number { + typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) + + // Add the output and return the output's index + return (this.outs.push({ + script: scriptPubKey, + value: value + }) - 1) + } + + hasWitnesses (): boolean { + return this.ins.some((x) => { + return x.witness.length !== 0 }) } - const voutLen = readVarInt() - for (i = 0; i < voutLen; ++i) { - tx.outs.push({ - value: readUInt64(), - script: readVarSlice() - }) + weight (): number { + const base = this.__byteLength(false) + const total = this.__byteLength(true) + return base * 3 + total } - if (hasWitnesses) { - for (i = 0; i < vinLen; ++i) { - tx.ins[i].witness = readVector() - } - - // was this pointless? - if (!tx.hasWitnesses()) throw new Error('Transaction has superfluous witness data') + virtualSize (): number { + return Math.ceil(this.weight() / 4) } - tx.locktime = readUInt32() - - if (__noStrict) return tx - if (offset !== buffer.length) throw new Error('Transaction has unexpected data') - - return tx -} - -Transaction.fromHex = function (hex) { - return Transaction.fromBuffer(Buffer.from(hex, 'hex'), undefined) -} - -Transaction.isCoinbaseHash = function (buffer) { - typeforce(types.Hash256bit, buffer) - for (var i = 0; i < 32; ++i) { - if (buffer[i] !== 0) return false - } - return true -} - -Transaction.prototype.isCoinbase = function () { - return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) -} - -Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) { - typeforce(types.tuple( - types.Hash256bit, - types.UInt32, - types.maybe(types.UInt32), - types.maybe(types.Buffer) - ), arguments) - - if (types.Null(sequence)) { - sequence = Transaction.DEFAULT_SEQUENCE + byteLength (): number { + return this.__byteLength(true) } - // Add the input and return the input's index - return (this.ins.push({ - hash: hash, - index: index, - script: scriptSig || EMPTY_SCRIPT, - sequence: sequence, - witness: EMPTY_WITNESS - }) - 1) -} + __byteLength (__allowWitness: boolean): number { + const hasWitnesses = __allowWitness && this.hasWitnesses() -Transaction.prototype.addOutput = function (scriptPubKey, value) { - typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) + return ( + (hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script) + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script) + }, 0) + + (hasWitnesses ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness) + }, 0) : 0) + ) + } - // Add the output and return the output's index - return (this.outs.push({ - script: scriptPubKey, - value: value - }) - 1) -} + clone (): Transaction { + const newTx = new Transaction() + newTx.version = this.version + newTx.locktime = this.locktime -Transaction.prototype.hasWitnesses = function () { - return this.ins.some(function (x) { - return x.witness.length !== 0 - }) -} - -Transaction.prototype.weight = function () { - const base = this.__byteLength(false) - const total = this.__byteLength(true) - return base * 3 + total -} - -Transaction.prototype.virtualSize = function () { - return Math.ceil(this.weight() / 4) -} - -Transaction.prototype.byteLength = function () { - return this.__byteLength(true) -} - -Transaction.prototype.__byteLength = function (__allowWitness) { - const hasWitnesses = __allowWitness && this.hasWitnesses() - - return ( - (hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce(function (sum, input) { return sum + 40 + varSliceSize(input.script) }, 0) + - this.outs.reduce(function (sum, output) { return sum + 8 + varSliceSize(output.script) }, 0) + - (hasWitnesses ? this.ins.reduce(function (sum, input) { return sum + vectorSize(input.witness) }, 0) : 0) - ) -} - -Transaction.prototype.clone = function () { - const newTx = new Transaction() - newTx.version = this.version - newTx.locktime = this.locktime - - newTx.ins = this.ins.map(function (txIn) { - return { - hash: txIn.hash, - index: txIn.index, - script: txIn.script, - sequence: txIn.sequence, - witness: txIn.witness - } - }) - - newTx.outs = this.outs.map(function (txOut) { - return { - script: txOut.script, - value: txOut.value - } - }) - - return newTx -} - -/** - * Hash transaction for signing a specific input. - * - * Bitcoin uses a different hash for each signed transaction input. - * This method copies the transaction, makes the necessary changes based on the - * hashType, and then hashes the result. - * This hash can then be used to sign the provided transaction input. - */ -Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) - - // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 - if (inIndex >= this.ins.length) return ONE - - // ignore OP_CODESEPARATOR - const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) { - return x !== opcodes.OP_CODESEPARATOR - })) - - const txTmp = this.clone() - - // SIGHASH_NONE: ignore all outputs? (wildcard payee) - if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { - txTmp.outs = [] - - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach(function (input, i) { - if (i === inIndex) return - - input.sequence = 0 + newTx.ins = this.ins.map((txIn) => { + return { + hash: txIn.hash, + index: txIn.index, + script: txIn.script, + sequence: txIn.sequence, + witness: txIn.witness + } }) - // SIGHASH_SINGLE: ignore all outputs, except at the same index? - } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { - // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 - if (inIndex >= this.outs.length) return ONE + newTx.outs = this.outs.map((txOut) => { + return { + script: txOut.script, + value: (<Output>txOut).value + } + }) - // truncate outputs after - txTmp.outs.length = inIndex + 1 + return newTx + } - // "blank" outputs before - for (var i = 0; i < inIndex; i++) { - txTmp.outs[i] = BLANK_OUTPUT + /** + * Hash transaction for signing a specific input. + * + * Bitcoin uses a different hash for each signed transaction input. + * This method copies the transaction, makes the necessary changes based on the + * hashType, and then hashes the result. + * This hash can then be used to sign the provided transaction input. + */ + hashForSignature (inIndex: number, prevOutScript: Buffer, hashType: number): Buffer { + typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) + + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 + if (inIndex >= this.ins.length) return ONE + + // ignore OP_CODESEPARATOR + const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter((x) => { + return x !== opcodes.OP_CODESEPARATOR + })) + + const txTmp = this.clone() + + // SIGHASH_NONE: ignore all outputs? (wildcard payee) + if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { + txTmp.outs = [] + + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, i) => { + if (i === inIndex) return + + input.sequence = 0 + }) + + // SIGHASH_SINGLE: ignore all outputs, except at the same index? + } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 + if (inIndex >= this.outs.length) return ONE + + // truncate outputs after + txTmp.outs.length = inIndex + 1 + + // "blank" outputs before + for (var i = 0; i < inIndex; i++) { + txTmp.outs[i] = BLANK_OUTPUT + } + + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, y) => { + if (y === inIndex) return + + input.sequence = 0 + }) } - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach(function (input, y) { - if (y === inIndex) return + // SIGHASH_ANYONECANPAY: ignore inputs entirely? + if (hashType & Transaction.SIGHASH_ANYONECANPAY) { + txTmp.ins = [txTmp.ins[inIndex]] + txTmp.ins[0].script = ourScript - input.sequence = 0 - }) + // SIGHASH_ALL: only ignore input scripts + } else { + // "blank" others input scripts + txTmp.ins.forEach((input) => { + input.script = EMPTY_SCRIPT + }) + txTmp.ins[inIndex].script = ourScript + } + + // serialize and hash + const buffer: Buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) + buffer.writeInt32LE(hashType, buffer.length - 4) + txTmp.__toBuffer(buffer, 0, false) + + return bcrypto.hash256(buffer) } - // SIGHASH_ANYONECANPAY: ignore inputs entirely? - if (hashType & Transaction.SIGHASH_ANYONECANPAY) { - txTmp.ins = [txTmp.ins[inIndex]] - txTmp.ins[0].script = ourScript + hashForWitnessV0 (inIndex: number, prevOutScript: Buffer, value: number, hashType: number): Buffer { + typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) - // SIGHASH_ALL: only ignore input scripts - } else { - // "blank" others input scripts - txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT }) - txTmp.ins[inIndex].script = ourScript - } + let tbuffer: Buffer = Buffer.from([]) + let toffset: number = 0 - // serialize and hash - const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) - buffer.writeInt32LE(hashType, buffer.length - 4) - txTmp.__toBuffer(buffer, 0, false) + function writeSlice (slice: Buffer): void { + toffset += slice.copy(tbuffer, toffset) + } - return bcrypto.hash256(buffer) -} + function writeUInt32 (i: number): void { + toffset = tbuffer.writeUInt32LE(i, toffset) + } -Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) + function writeUInt64 (i: number): void { + toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) + } - let tbuffer, toffset - function writeSlice (slice) { toffset += slice.copy(tbuffer, toffset) } - function writeUInt32 (i) { toffset = tbuffer.writeUInt32LE(i, toffset) } - function writeUInt64 (i) { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) } - function writeVarInt (i) { - varuint.encode(i, tbuffer, toffset) - toffset += varuint.encode.bytes - } - function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } + function writeVarInt (i: number): void { + varuint.encode(i, tbuffer, toffset) + toffset += varuint.encode.bytes + } - let hashOutputs = ZERO - let hashPrevouts = ZERO - let hashSequence = ZERO + function writeVarSlice (slice: Buffer): void { + writeVarInt(slice.length) + writeSlice(slice) + } - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { - tbuffer = Buffer.allocUnsafe(36 * this.ins.length) + let hashOutputs = ZERO + let hashPrevouts = ZERO + let hashSequence = ZERO + + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { + tbuffer = Buffer.allocUnsafe(36 * this.ins.length) + toffset = 0 + + this.ins.forEach((txIn) => { + writeSlice(txIn.hash) + writeUInt32(txIn.index) + }) + + hashPrevouts = bcrypto.hash256(tbuffer) + } + + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && + (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + tbuffer = Buffer.allocUnsafe(4 * this.ins.length) + toffset = 0 + + this.ins.forEach((txIn) => { + writeUInt32(txIn.sequence) + }) + + hashSequence = bcrypto.hash256(tbuffer) + } + + if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + const txOutsSize = this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script) + }, 0) + + tbuffer = Buffer.allocUnsafe(txOutsSize) + toffset = 0 + + this.outs.forEach((out) => { + writeUInt64((<Output>out).value) + writeVarSlice(out.script) + }) + + hashOutputs = bcrypto.hash256(tbuffer) + } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { + const output = this.outs[inIndex] + + tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) + toffset = 0 + writeUInt64((<Output>output).value) + writeVarSlice(output.script) + + hashOutputs = bcrypto.hash256(tbuffer) + } + + tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) toffset = 0 - this.ins.forEach(function (txIn) { + const input = this.ins[inIndex] + writeUInt32(this.version) + writeSlice(hashPrevouts) + writeSlice(hashSequence) + writeSlice(input.hash) + writeUInt32(input.index) + writeVarSlice(prevOutScript) + writeUInt64(value) + writeUInt32(input.sequence) + writeSlice(hashOutputs) + writeUInt32(this.locktime) + writeUInt32(hashType) + return bcrypto.hash256(tbuffer) + } + + getHash (forWitness: boolean): Buffer { + // wtxid for coinbase is always 32 bytes of 0x00 + if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0) + return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness)) + } + + getId (): string { + // transaction hash's are displayed in reverse order + return Buffer.from(this.getHash(false).reverse()).toString('hex') + } + + toBuffer (buffer: Buffer | void, initialOffset: number | void): Buffer { + return this.__toBuffer(buffer, initialOffset, true) + } + + __toBuffer (buffer: Buffer | void, initialOffset: number | void, __allowWitness: boolean | void): Buffer { + if (!buffer) buffer = <Buffer> Buffer.allocUnsafe(this.__byteLength((<boolean>__allowWitness))) + + let offset = initialOffset || 0 + + function writeSlice (slice) { + offset += slice.copy(buffer, offset) + } + + function writeUInt8 (i) { + offset = (<Buffer>buffer).writeUInt8(i, offset) + } + + function writeUInt32 (i) { + offset = (<Buffer>buffer).writeUInt32LE(i, offset) + } + + function writeInt32 (i) { + offset = (<Buffer>buffer).writeInt32LE(i, offset) + } + + function writeUInt64 (i) { + offset = bufferutils.writeUInt64LE(buffer, i, offset) + } + + function writeVarInt (i) { + varuint.encode(i, buffer, offset) + offset += varuint.encode.bytes + } + + function writeVarSlice (slice) { + writeVarInt(slice.length) + writeSlice(slice) + } + + function writeVector (vector) { + writeVarInt(vector.length) + vector.forEach(writeVarSlice) + } + + writeInt32(this.version) + + const hasWitnesses = __allowWitness && this.hasWitnesses() + + if (hasWitnesses) { + writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) + writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) + } + + writeVarInt(this.ins.length) + + this.ins.forEach((txIn) => { writeSlice(txIn.hash) writeUInt32(txIn.index) - }) - - hashPrevouts = bcrypto.hash256(tbuffer) - } - - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && - (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - tbuffer = Buffer.allocUnsafe(4 * this.ins.length) - toffset = 0 - - this.ins.forEach(function (txIn) { + writeVarSlice(txIn.script) writeUInt32(txIn.sequence) }) - hashSequence = bcrypto.hash256(tbuffer) - } + writeVarInt(this.outs.length) + this.outs.forEach((txOut) => { + if (isOutput(txOut)) { + writeUInt64(txOut.value) + } else { + writeSlice(txOut.valueBuffer) + } - if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - const txOutsSize = this.outs.reduce(function (sum, output) { - return sum + 8 + varSliceSize(output.script) - }, 0) - - tbuffer = Buffer.allocUnsafe(txOutsSize) - toffset = 0 - - this.outs.forEach(function (out) { - writeUInt64(out.value) - writeVarSlice(out.script) + writeVarSlice(txOut.script) }) - hashOutputs = bcrypto.hash256(tbuffer) - } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { - const output = this.outs[inIndex] - - tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) - toffset = 0 - writeUInt64(output.value) - writeVarSlice(output.script) - - hashOutputs = bcrypto.hash256(tbuffer) - } - - tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) - toffset = 0 - - const input = this.ins[inIndex] - writeUInt32(this.version) - writeSlice(hashPrevouts) - writeSlice(hashSequence) - writeSlice(input.hash) - writeUInt32(input.index) - writeVarSlice(prevOutScript) - writeUInt64(value) - writeUInt32(input.sequence) - writeSlice(hashOutputs) - writeUInt32(this.locktime) - writeUInt32(hashType) - return bcrypto.hash256(tbuffer) -} - -Transaction.prototype.getHash = function () { - return bcrypto.hash256(this.__toBuffer(undefined, undefined, false)) -} - -Transaction.prototype.getId = function () { - // transaction hash's are displayed in reverse order - return this.getHash().reverse().toString('hex') -} - -Transaction.prototype.toBuffer = function (buffer, initialOffset) { - return this.__toBuffer(buffer, initialOffset, true) -} - -Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitness) { - if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)) - - let offset = initialOffset || 0 - function writeSlice (slice) { offset += slice.copy(buffer, offset) } - function writeUInt8 (i) { offset = buffer.writeUInt8(i, offset) } - function writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) } - function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) } - function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) } - function writeVarInt (i) { - varuint.encode(i, buffer, offset) - offset += varuint.encode.bytes - } - function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } - function writeVector (vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice) } - - writeInt32(this.version) - - const hasWitnesses = __allowWitness && this.hasWitnesses() - - if (hasWitnesses) { - writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) - writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) - } - - writeVarInt(this.ins.length) - - this.ins.forEach(function (txIn) { - writeSlice(txIn.hash) - writeUInt32(txIn.index) - writeVarSlice(txIn.script) - writeUInt32(txIn.sequence) - }) - - writeVarInt(this.outs.length) - this.outs.forEach(function (txOut) { - if (!txOut.valueBuffer) { - writeUInt64(txOut.value) - } else { - writeSlice(txOut.valueBuffer) + if (hasWitnesses) { + this.ins.forEach((input) => { + writeVector(input.witness) + }) } - writeVarSlice(txOut.script) - }) + writeUInt32(this.locktime) - if (hasWitnesses) { - this.ins.forEach(function (input) { - writeVector(input.witness) - }) + // avoid slicing unless necessary + if (initialOffset !== undefined) return buffer.slice((<number>initialOffset), offset) + return buffer } - writeUInt32(this.locktime) + toHex () { + return this.toBuffer(undefined, undefined).toString('hex') + } - // avoid slicing unless necessary - if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) - return buffer + setInputScript (index, scriptSig) { + typeforce(types.tuple(types.Number, types.Buffer), arguments) + + this.ins[index].script = scriptSig + } + + setWitness (index, witness) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments) + + this.ins[index].witness = witness + } } - -Transaction.prototype.toHex = function () { - return this.toBuffer().toString('hex') -} - -Transaction.prototype.setInputScript = function (index, scriptSig) { - typeforce(types.tuple(types.Number, types.Buffer), arguments) - - this.ins[index].script = scriptSig -} - -Transaction.prototype.setWitness = function (index, witness) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments) - - this.ins[index].witness = witness -} - -module.exports = Transaction -export {} diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index 56368bf..83fbe2b 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -1,3 +1,5 @@ +import { Transaction, Output } from './transaction' + const Buffer = require('safe-buffer').Buffer const baddress = require('./address') const bcrypto = require('./crypto') @@ -11,7 +13,6 @@ const classify = require('./classify') const SCRIPT_TYPES = classify.types const ECPair = require('./ecpair') -const Transaction = require('./transaction') function expandInput (scriptSig, witnessStack, type, scriptPubKey) { if (scriptSig.length === 0 && witnessStack.length === 0) return {} @@ -517,9 +518,9 @@ TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOu } else if (txHash instanceof Transaction) { const txOut = txHash.outs[vout] prevOutScript = txOut.script - value = txOut.value + value = (<Output>txOut).value - txHash = txHash.getHash() + txHash = txHash.getHash(false) } return this.__addInputUnsafe(txHash, vout, { diff --git a/test/block.js b/test/block.js index 07d0741..76b043d 100644 --- a/test/block.js +++ b/test/block.js @@ -32,6 +32,9 @@ describe('Block', function () { assert.strictEqual(block.version, f.version) assert.strictEqual(block.prevHash.toString('hex'), f.prevHash) assert.strictEqual(block.merkleRoot.toString('hex'), f.merkleRoot) + if (block.witnessCommit) { + assert.strictEqual(block.witnessCommit.toString('hex'), f.witnessCommit) + } assert.strictEqual(block.timestamp, f.timestamp) assert.strictEqual(block.bits, f.bits) assert.strictEqual(block.nonce, f.nonce) @@ -113,6 +116,12 @@ describe('Block', function () { it('returns ' + f.merkleRoot + ' for ' + f.id, function () { assert.strictEqual(Block.calculateMerkleRoot(block.transactions).toString('hex'), f.merkleRoot) }) + + if (f.witnessCommit) { + it('returns witness commit ' + f.witnessCommit + ' for ' + f.id, function () { + assert.strictEqual(Block.calculateMerkleRoot(block.transactions, true).toString('hex'), f.witnessCommit) + }) + } }) }) @@ -129,6 +138,12 @@ describe('Block', function () { it('returns ' + f.valid + ' for ' + f.id, function () { assert.strictEqual(block.checkMerkleRoot(), true) }) + + if (f.witnessCommit) { + it('validates witness commit for ' + f.id, function () { + assert.strictEqual(block.checkWitnessCommit(), true) + }) + } }) }) diff --git a/test/fixtures/block.json b/test/fixtures/block.json index b4a1cfe..c60685e 100644 --- a/test/fixtures/block.json +++ b/test/fixtures/block.json @@ -119,6 +119,21 @@ "timestamp": 1231006505, "valid": true, "version": 1 + }, + { + "description": "Block with witness commit", + "bits": 388503969, + "hash": "ec61d8d62a4945034a1df40dd3dfda364221c0562c3a14000000000000000000", + "height": 542213, + "hex": "000000208980ebb11236bacc66c447d5ad961bc546c0f9cc385a08000000000000000000135e9b653aee9a70028aff2db53d4f43f4484ad52558c3ae300410b79cb6a864df50a35ba11928171396e30104010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff5003054608174d696e656420627920416e74506f6f6c393b205ba350dffabe6d6d3a3e92d9efff857664de632e89fa3182f1e793d00be2e71a117b273145945a810400000000000000db250000acba0500ffffffff028a8f814a000000001976a914edf10a7fac6b32e24daa5305c723f3de58db1bc888ac0000000000000000266a24aa21a9ed4a657fcaa2149342376247e2e283a55a6b92dcc35d4d89e4ac7b74488cb63be201200000000000000000000000000000000000000000000000000000000000000000000000000100000001b898273f98d49399ecb5194ffdb1ed15c2fb37cf6d7696b4389bc7d1b76b63db010000006b483045022100e2e9bc1f6bae2deed086e935bb49fd6ac1e13dc3a44c36cd8b9a6f4257efb70d022076537c7021f12d761e1202796029f13798503bc22ab8c2ee8cb98207cbfeb414012102071c2c88e4560b47a03c033c736149a2ddd6071aea54ab85c5169cee156712f8ffffffff02b0710b00000000001976a914849a95fc65eeaa2ac47b6b6fc1f1883edb2c6c9788ace6b62501000000001976a9142964198f7ae9f7b920a2ab7c0b96b90e4ec9b14d88ac000000000100000000010152405e2660055b3540f63424a1b0b3b7bb9bbef10ceec970cd18f6f86f84a7880000000017160014dde2f1a9a4bfda011ba9ec4062990c7e1a531585ffffffff0266d418000000000017a914adc5ec550548f087371e645047170864d5fbdc03877e0400000000000016001441f6746110cc0fec102e83053d4c0ae56fab1bdd02483045022100f93bd5d3529418f60dddd477f169c32ea5ab340c99573438ee7c40d705db9ba20220323f1b4d8840098b1271c95365cdc7e8f81d0bf1fcc568c68fc7de8b871b4bee012103211d047d92547bca4aa116bc51ec4b09188e5991f69f0432fe1b5dfd8947859700000000010000000ac1a19854e5b92792ae96d08eb9f7fc016fb57c51a9047161c342e6b8de8ce721000000006b483045022100fa00a6651015ac807b03d8d54559831db40d80329f46a886b93ca6b3996daf9b02201d0fafccc88c4654a9c3d205ef0828e32376ecb42f79587615203f23fc8f2d42012102a2cda42f6954605e40cbc5601f65673621c057f8c12f16149fbbf632e8be8ed8ffffffffda16ff4a7324f78f16d5e5d4a740168de9df426cf53df569c9a33d1b94e2c25f000000006a4730440220167b4777a23db304f50ac75febfae5db0b1578c90d85769bc76e0f5458484319022023f763e95ea7771c15ea0df91c17519014b2223cd726a3b0a8b1a6f67f99b2bf012103b9492a823f03b70a1750e0fac44f58f5c81e09f4c18d0b28755b44c911ffab5ffffffffff1533f2ea34b859d2be05bbb6859d6105ad4389c911545487187437acae21b70000000006b483045022100be0b8dc174f4136e3fb4f3ccf1a2be67a44d2086179c5726c61a1af010d5232f02205c0fb4cdd7c9cb8698ceed05e688e10a0cbdd0ee5db1a0a2ef92f322e1a60e9f0121020b9f404317cc6ab5a699f607a9bb0acd0bf5588777672f8f7f4c1a13304e9f76ffffffff1395191837aa5b1cfa4cc2a79d6d6bda7d198e8a795a0f7a85f556c446119572000000006b483045022100ef3c61b5ba155b94fd02a96bf788e9a9be2de0ee53e3fe1fa6c24dd9d9aaee12022044c07be54a9c59fed96dfb778da0079f6753ab634d1920bf0fac25ebf7ac49d0012103f93e29be8b393773228f704151964662a91df4c1a3e15364e4cfb38a8cacbda9ffffffff55120f684d5fbb845fd0198cd734e46fc29f40dc8f906bf36743d7f740f5fd7c000000006a4730440220421aef290bbd39a18d1281ecfbb420b43daf2cc1c315b6bb44c895f88cb3cfcc022007a512c65b7768b1505f789b6d78479063d04ffb243072f2061eeb66fb1ade81012102712eea19c72fd644b2698c5480ebe77ce6face0bed64238a34a32085567f2f8efffffffff3553d19ce3156464e9cfa06a5260f9d9d01b16ccad6e2ebaab233ba10472ba6000000006b483045022100a29aea775d2c46028f40dceb1707b23e720bb314cef455854313569200c83162022009d0cdcef2e1f0a9217794991fa838fe6ce2bdc6e3c04ad490343c7e4a0ee7d3012102ed59ec6d98f9c2a4dd1324d46d74c56ff7e15935925f69799e547969a523ea98ffffffff530ab6d95f0d83669bfb12cbe983febe6ca638255139ce2ba0e35887f2fe3db6000000006b483045022100fad9f10989ab4ab019da4f6e430f9fe9ebe76941a9200d7346447358e93b1dce0220756c864b029a64ed9d6eedc13cf8b7d602572fcd7a3d3cb528350bb032d7e3ec012102e3e0c78d034627b1616cf196aa69bfaf20009ba9ebf09cf069453cc0423239e2ffffffff4b8e70824941d965df99703cacadfabcf79bc040029e528ff931e9ba4a7ee4d8000000006b48304502210095f37fb2700c9f96d5e5c02d4b043a7cd804f79dd071d8b221b7ae781f0f5b4e02200ae3135b8bebf813449956ae19e7c02db5eff2472da023afa8b8d4e9baba0cdd01210226cc53dfc0a41cc0cf7117dfd406db2b87161e89e2bab9908a2382173fc6dbe1ffffffff5262129e9881722b217f7e4882ed2b83ee53d9e23b9bc647494c9487ae9fabdf000000006a473044022032361f724fe006079cf37b3df61cb6c51cb0c5fd77f29b61a318134ca4948d0e02202fbe6d484a78730899230244f3662fd5578b87e9eaaccdf9bf8f05d23917de8301210254e84223b3d7f7cfd14315be8fa0c7d7eb1a1a34a672f08e2b8ec134472a66d4ffffffff067040e03288282ebe5db7b705130849c9984458b13ea7ca3c755f07b9d1b9f4000000006b483045022100b78b9c24f5f3e950aba637b827d5b11615c17ae2f43105941d172cc4a8b73c80022064998c17becd06abbcfde47c91b9d87da5ac67873ed9a412010b08b954e0b5cd01210329a0acc2b0d60dd243eef46073a672ed0caf467c92f63ef7293f2036a3851a1effffffff02027f0000000000001976a914c6a396ae979670eeaa6929df3dd1c2d8fba31c3d88ac85a19b020000000017a914409dbd0e9a1ab27853186367130e6aab2509e47f8700000000", + "id": "000000000000000000143a2c56c0214236dadfd30df41d4a0345492ad6d861ec", + "merkleRoot": "135e9b653aee9a70028aff2db53d4f43f4484ad52558c3ae300410b79cb6a864", + "witnessCommit": "4a657fcaa2149342376247e2e283a55a6b92dcc35d4d89e4ac7b74488cb63be2", + "nonce": 31692307, + "prevHash": "8980ebb11236bacc66c447d5ad961bc546c0f9cc385a08000000000000000000", + "timestamp": 1537429727, + "valid": true, + "version": 536870912 } ], "invalid": [ diff --git a/test/transaction.js b/test/transaction.js index f315a7d..74afc8f 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -2,7 +2,7 @@ const { describe, it, beforeEach } = require('mocha') const assert = require('assert') const bscript = require('../dist/src/script') const fixtures = require('./fixtures/transaction') -const Transaction = require('../dist/src/transaction') +const Transaction = require('..').Transaction describe('Transaction', function () { function fromRaw (raw, noWitness) { diff --git a/test/transaction_builder.js b/test/transaction_builder.js index a4b309a..c416846 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -5,7 +5,7 @@ const bscript = require('../dist/src/script') const payments = require('../dist/src/payments') const ECPair = require('../dist/src/ecpair') -const Transaction = require('../dist/src/transaction') +const Transaction = require('..').Transaction const TransactionBuilder = require('../dist/src/transaction_builder') const NETWORKS = require('../dist/src/networks') From 58a6b0e54532036bd5a9f7ea1bbf5dbddf0c78fd Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 10:35:28 +0900 Subject: [PATCH 174/568] Convert ECPair --- src/ecpair.ts | 84 ++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/src/ecpair.ts b/src/ecpair.ts index 0c9fd83..15b3d0c 100644 --- a/src/ecpair.ts +++ b/src/ecpair.ts @@ -1,51 +1,62 @@ +import { Network } from './networks' +import * as NETWORKS from './networks' const ecc = require('tiny-secp256k1') const randomBytes = require('randombytes') const typeforce = require('typeforce') const types = require('./types') const wif = require('wif') -const NETWORKS = require('./networks') const isOptions = typeforce.maybe(typeforce.compile({ compressed: types.maybe(types.Boolean), network: types.maybe(types.Network) })) -function ECPair (d, Q, options) { - options = options || {} - - this.compressed = options.compressed === undefined ? true : options.compressed - this.network = options.network || NETWORKS.bitcoin - - this.__d = d || null - this.__Q = null - if (Q) this.__Q = ecc.pointCompress(Q, this.compressed) +export interface ECPairOptions { + compressed?: boolean + network?: Network + rng?(Buffer): Buffer } -Object.defineProperty(ECPair.prototype, 'privateKey', { - enumerable: false, - get: function () { return this.__d } -}) +class ECPair { + compressed: boolean + network: Network + private __d: Buffer + private __Q: Buffer + constructor (d: Buffer | void, Q: Buffer | void, options: ECPairOptions) { + if (options === undefined) options = {} + this.compressed = options.compressed === undefined ? true : options.compressed + this.network = options.network || NETWORKS.bitcoin -Object.defineProperty(ECPair.prototype, 'publicKey', { get: function () { - if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed) - return this.__Q -}}) + this.__d = d || null + this.__Q = null + if (Q) this.__Q = ecc.pointCompress(Q, this.compressed) + } -ECPair.prototype.toWIF = function () { - if (!this.__d) throw new Error('Missing private key') - return wif.encode(this.network.wif, this.__d, this.compressed) + get privateKey (): Buffer { + return this.__d + } + + get publicKey (): Buffer { + if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed) + return this.__Q + } + + toWIF (): string { + if (!this.__d) throw new Error('Missing private key') + return wif.encode(this.network.wif, this.__d, this.compressed) + } + + sign (hash: Buffer): Buffer { + if (!this.__d) throw new Error('Missing private key') + return ecc.sign(hash, this.__d) + } + + verify (hash: Buffer, signature: Buffer): Buffer { + return ecc.verify(hash, this.publicKey, signature) + } } -ECPair.prototype.sign = function (hash) { - if (!this.__d) throw new Error('Missing private key') - return ecc.sign(hash, this.__d) -} - -ECPair.prototype.verify = function (hash, signature) { - return ecc.verify(hash, this.publicKey, signature) -} - -function fromPrivateKey (buffer, options) { +function fromPrivateKey (buffer: Buffer, options: ECPairOptions): ECPair { typeforce(types.Buffer256bit, buffer) if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)') typeforce(isOptions, options) @@ -53,13 +64,13 @@ function fromPrivateKey (buffer, options) { return new ECPair(buffer, null, options) } -function fromPublicKey (buffer, options) { +function fromPublicKey (buffer, options): ECPair { typeforce(ecc.isPoint, buffer) typeforce(isOptions, options) return new ECPair(null, buffer, options) } -function fromWIF (string, network) { +function fromWIF (string, network): ECPair { const decoded = wif.decode(string) const version = decoded.version @@ -84,9 +95,9 @@ function fromWIF (string, network) { }) } -function makeRandom (options) { +function makeRandom (options: ECPairOptions): ECPair { typeforce(isOptions, options) - options = options || {} + if (options === undefined) options = {} const rng = options.rng || randomBytes let d @@ -98,10 +109,9 @@ function makeRandom (options) { return fromPrivateKey(d, options) } -module.exports = { +export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF } -export {} From fce08352f57d23150da5abead1c50261e760a60c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 11:56:03 +0900 Subject: [PATCH 175/568] Add TransactionBuilder --- src/ecpair.ts | 15 +- src/index.ts | 2 +- src/transaction_builder.ts | 724 +++++++++++++++++++----------------- test/transaction_builder.js | 2 +- 4 files changed, 395 insertions(+), 348 deletions(-) diff --git a/src/ecpair.ts b/src/ecpair.ts index 15b3d0c..eceb2a1 100644 --- a/src/ecpair.ts +++ b/src/ecpair.ts @@ -11,13 +11,24 @@ const isOptions = typeforce.maybe(typeforce.compile({ network: types.maybe(types.Network) })) -export interface ECPairOptions { +interface ECPairOptions { compressed?: boolean network?: Network rng?(Buffer): Buffer } -class ECPair { +export interface ECPairInterface { + compressed: boolean + network: Network + privateKey: Buffer + publicKey: Buffer + toWIF(): string + sign(hash: Buffer): Buffer + verify(hash: Buffer, signature: Buffer): Buffer + getPublicKey?(): Buffer +} + +class ECPair implements ECPairInterface { compressed: boolean network: Network private __d: Buffer diff --git a/src/index.ts b/src/index.ts index 389e2b1..8019cb7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ const opcodes = require('bitcoin-ops') import { Block } from './block' import * as ECPair from './ecpair' import { Transaction } from './transaction' -import * as TransactionBuilder from './transaction_builder' +import { TransactionBuilder } from './transaction_builder' import * as address from './address' import * as bip32 from 'bip32' import * as crypto from './crypto' diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index 83fbe2b..01717ab 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -1,10 +1,11 @@ +import { Network } from './networks' +import * as networks from './networks' import { Transaction, Output } from './transaction' - +import { ECPairInterface } from './ecpair' const Buffer = require('safe-buffer').Buffer const baddress = require('./address') const bcrypto = require('./crypto') const bscript = require('./script') -const networks = require('./networks') const ops = require('bitcoin-ops') const payments = require('./payments') const typeforce = require('typeforce') @@ -14,7 +15,370 @@ const SCRIPT_TYPES = classify.types const ECPair = require('./ecpair') -function expandInput (scriptSig, witnessStack, type, scriptPubKey) { +interface TxbInput { + value?: number + hasWitness?: boolean + signScript?: Buffer + signType?: string + prevOutScript?: Buffer + redeemScript?: Buffer + redeemScriptType?: string + prevOutType?: string + pubkeys?: Array<Buffer> + signatures?: Array<Buffer> + witness?: Array<Buffer> + witnessScript?: Buffer + witnessScriptType?: string + script?: Buffer + sequence?: number + scriptSig?: Buffer + maxSignatures?: number +} + +interface TxbOutput { + type: string + pubkeys?: Array<Buffer> + signatures?: Array<Buffer> + maxSignatures?: number +} + +function txIsString(tx: Buffer | string | Transaction): tx is string { + return typeof (<string>tx) === 'string' || tx instanceof String +} + +function txIsTransaction(tx: Buffer | string | Transaction): tx is Transaction { + return (<Transaction>tx) instanceof Transaction +} + +export class TransactionBuilder { + network: Network + maximumFeeRate: number + private __prevTxSet: Object + private __inputs: Array<TxbInput> + private __tx: Transaction + + constructor (network: Network, maximumFeeRate?: number) { + this.__prevTxSet = {} + this.network = network || networks.bitcoin + + // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) + this.maximumFeeRate = maximumFeeRate || 2500 + + this.__inputs = [] + this.__tx = new Transaction() + this.__tx.version = 2 + } + + static fromTransaction (transaction: Transaction, network?: Network): TransactionBuilder { + const txb = new TransactionBuilder(network) + + // Copy transaction fields + txb.setVersion(transaction.version) + txb.setLockTime(transaction.locktime) + + // Copy outputs (done first to avoid signature invalidation) + transaction.outs.forEach(txOut => { + txb.addOutput(txOut.script, (<Output>txOut).value) + }) + + // Copy inputs + transaction.ins.forEach(txIn => { + txb.__addInputUnsafe(txIn.hash, txIn.index, { + sequence: txIn.sequence, + script: txIn.script, + witness: txIn.witness + }) + }) + + // fix some things not possible through the public API + txb.__inputs.forEach((input, i) => { + fixMultisigOrder(input, transaction, i) + }) + + return txb + } + + setLockTime (locktime: number): void { + typeforce(types.UInt32, locktime) + + // if any signatures exist, throw + if (this.__inputs.some(input => { + if (!input.signatures) return false + + return input.signatures.some(s => s !== undefined) + })) { + throw new Error('No, this would invalidate signatures') + } + + this.__tx.locktime = locktime + } + + setVersion (version: number): void { + typeforce(types.UInt32, version) + + // XXX: this might eventually become more complex depending on what the versions represent + this.__tx.version = version + } + + addInput (txHash: Buffer | string | Transaction, vout: number, sequence: number, prevOutScript: Buffer): number { + if (!this.__canModifyInputs()) { + throw new Error('No, this would invalidate signatures') + } + + let value: number + + // is it a hex string? + if (txIsString(txHash)) { + // transaction hashs's are displayed in reverse order, un-reverse it + txHash = <Buffer> Buffer.from(txHash, 'hex').reverse() + + // is it a Transaction object? + } else if (txIsTransaction(txHash)) { + const txOut = txHash.outs[vout] + prevOutScript = txOut.script + value = (<Output>txOut).value + + txHash = <Buffer> txHash.getHash(false) + } + + return this.__addInputUnsafe(txHash, vout, { + sequence: sequence, + prevOutScript: prevOutScript, + value: value + }) + } + + private __addInputUnsafe (txHash: Buffer, vout: number, options: TxbInput): number { + if (Transaction.isCoinbaseHash(txHash)) { + throw new Error('coinbase inputs not supported') + } + + const prevTxOut = txHash.toString('hex') + ':' + vout + if (this.__prevTxSet[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut) + + let input = <TxbInput>{} + + // derive what we can from the scriptSig + if (options.script !== undefined) { + input = expandInput(options.script, options.witness || []) + } + + // if an input value was given, retain it + if (options.value !== undefined) { + input.value = options.value + } + + // derive what we can from the previous transactions output script + if (!input.prevOutScript && options.prevOutScript) { + let prevOutType + + if (!input.pubkeys && !input.signatures) { + const expanded = expandOutput(options.prevOutScript) + if (expanded.pubkeys) { + input.pubkeys = expanded.pubkeys + input.signatures = expanded.signatures + } + + prevOutType = expanded.type + } + + input.prevOutScript = options.prevOutScript + input.prevOutType = prevOutType || classify.output(options.prevOutScript) + } + + const vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig) + this.__inputs[vin] = input + this.__prevTxSet[prevTxOut] = true + return vin + } + + addOutput (scriptPubKey: string | Buffer, value): number { + if (!this.__canModifyOutputs()) { + throw new Error('No, this would invalidate signatures') + } + + // Attempt to get a script if it's a base58 or bech32 address string + if (typeof scriptPubKey === 'string') { + scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network) + } + + return this.__tx.addOutput(<Buffer>scriptPubKey, value) + } + + build (): Transaction { + return this.__build(false) + } + + buildIncomplete (): Transaction { + return this.__build(true) + } + + private __build (allowIncomplete?: boolean): Transaction { + if (!allowIncomplete) { + if (!this.__tx.ins.length) throw new Error('Transaction has no inputs') + if (!this.__tx.outs.length) throw new Error('Transaction has no outputs') + } + + const tx = this.__tx.clone() + + // create script signatures from inputs + this.__inputs.forEach((input, i) => { + if (!input.prevOutType && !allowIncomplete) throw new Error('Transaction is not complete') + + const result = build(input.prevOutType, input, allowIncomplete) + if (!result) { + if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) throw new Error('Unknown input type') + if (!allowIncomplete) throw new Error('Not enough information') + return + } + + tx.setInputScript(i, result.input) + tx.setWitness(i, result.witness) + }) + + if (!allowIncomplete) { + // do not rely on this, its merely a last resort + if (this.__overMaximumFees(tx.virtualSize())) { + throw new Error('Transaction has absurd fees') + } + } + + return tx + } + + sign (vin: number, keyPair: ECPairInterface, redeemScript: Buffer, hashType: number, witnessValue: number, witnessScript: Buffer) { + // TODO: remove keyPair.network matching in 4.0.0 + if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network') + if (!this.__inputs[vin]) throw new Error('No input at index: ' + vin) + + hashType = hashType || Transaction.SIGHASH_ALL + if (this.__needsOutputs(hashType)) throw new Error('Transaction needs outputs') + + const input = this.__inputs[vin] + + // if redeemScript was previously provided, enforce consistency + if (input.redeemScript !== undefined && + redeemScript && + !input.redeemScript.equals(redeemScript)) { + throw new Error('Inconsistent redeemScript') + } + + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey() + if (!canSign(input)) { + if (witnessValue !== undefined) { + if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue') + typeforce(types.Satoshi, witnessValue) + input.value = witnessValue + } + + if (!canSign(input)) { + const prepared = prepareInput(input, ourPubKey, redeemScript, witnessValue, witnessScript) + + // updates inline + Object.assign(input, prepared) + } + + if (!canSign(input)) throw Error(input.prevOutType + ' not supported') + } + + // ready to sign + let signatureHash + if (input.hasWitness) { + signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType) + } else { + signatureHash = this.__tx.hashForSignature(vin, input.signScript, hashType) + } + + // enforce in order signing of public keys + const signed = input.pubkeys.some((pubKey, i) => { + if (!ourPubKey.equals(pubKey)) return false + if (input.signatures[i]) throw new Error('Signature already exists') + + // TODO: add tests + if (ourPubKey.length !== 33 && input.hasWitness) { + throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH') + } + + const signature = keyPair.sign(signatureHash) + input.signatures[i] = bscript.signature.encode(signature, hashType) + return true + }) + + if (!signed) throw new Error('Key pair cannot sign for this input') + } + + private __canModifyInputs (): boolean { + return this.__inputs.every(input => { + if (!input.signatures) return true + + return input.signatures.every(signature => { + if (!signature) return true + const hashType = signatureHashType(signature) + + // if SIGHASH_ANYONECANPAY is set, signatures would not + // be invalidated by more inputs + return (hashType & Transaction.SIGHASH_ANYONECANPAY) !== 0 + }) + }) + } + + private __needsOutputs (signingHashType: number): boolean { + if (signingHashType === Transaction.SIGHASH_ALL) { + return this.__tx.outs.length === 0 + } + + // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs + // .build() will fail, but .buildIncomplete() is OK + return (this.__tx.outs.length === 0) && this.__inputs.some((input) => { + if (!input.signatures) return false + + return input.signatures.some((signature) => { + if (!signature) return false // no signature, no issue + const hashType = signatureHashType(signature) + if (hashType & Transaction.SIGHASH_NONE) return false // SIGHASH_NONE doesn't care about outputs + return true // SIGHASH_* does care + }) + }) + } + + private __canModifyOutputs (): boolean { + const nInputs = this.__tx.ins.length + const nOutputs = this.__tx.outs.length + + return this.__inputs.every(input => { + if (input.signatures === undefined) return true + + return input.signatures.every(signature => { + if (!signature) return true + const hashType = signatureHashType(signature) + + const hashTypeMod = hashType & 0x1f + if (hashTypeMod === Transaction.SIGHASH_NONE) return true + if (hashTypeMod === Transaction.SIGHASH_SINGLE) { + // if SIGHASH_SINGLE is set, and nInputs > nOutputs + // some signatures would be invalidated by the addition + // of more outputs + return nInputs <= nOutputs + } + }) + }) + } + + private __overMaximumFees (bytes: number): boolean { + // not all inputs will have .value defined + const incoming = this.__inputs.reduce((a, x) => a + (x.value >>> 0), 0) + + // but all outputs do, and if we have any input value + // we can immediately determine if the outputs are too small + const outgoing = this.__tx.outs.reduce((a, x) => a + (<Output>x).value, 0) + const fee = incoming - outgoing + const feeRate = fee / bytes + + return feeRate > this.maximumFeeRate + } +} + +function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: string, scriptPubKey?: Buffer): TxbInput { if (scriptSig.length === 0 && witnessStack.length === 0) return {} if (!type) { let ssType = classify.input(scriptSig, true) @@ -103,7 +467,7 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) { const outputType = classify.output(redeem.output) let expanded if (outputType === SCRIPT_TYPES.P2WPKH) { - expanded = expandInput(redeem.input, redeem.witness, outputType, undefined) + expanded = expandInput(redeem.input, redeem.witness, outputType) } else { expanded = expandInput(bscript.compile(redeem.witness), [], outputType, redeem.output) } @@ -127,18 +491,18 @@ function expandInput (scriptSig, witnessStack, type, scriptPubKey) { } // could be done in expandInput, but requires the original Transaction for hashForSignature -function fixMultisigOrder (input, transaction, vin) { +function fixMultisigOrder (input: TxbInput, transaction: Transaction, vin: number): void { if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) return if (input.pubkeys.length === input.signatures.length) return const unmatched = input.signatures.concat() - input.signatures = input.pubkeys.map(function (pubKey) { + input.signatures = input.pubkeys.map(pubKey => { const keyPair = ECPair.fromPublicKey(pubKey) let match // check for a signature - unmatched.some(function (signature, i) { + unmatched.some((signature, i) => { // skip if undefined || OP_0 if (!signature) return false @@ -160,7 +524,7 @@ function fixMultisigOrder (input, transaction, vin) { }) } -function expandOutput (script, ourPubKey) { +function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { typeforce(types.Buffer, script) const type = classify.output(script) @@ -218,7 +582,7 @@ function expandOutput (script, ourPubKey) { return { type } } -function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScript) { +function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, witnessValue: number, witnessScript: Buffer): TxbInput { if (redeemScript && witnessScript) { const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }) const p2wshAlt = payments.p2wsh({ output: redeemScript }) @@ -231,7 +595,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri const expanded = expandOutput(p2wsh.redeem.output, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') - if (input.signatures && input.signatures.some(x => x)) { + if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures } @@ -271,7 +635,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri const expanded = expandOutput(p2sh.redeem.output, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')') - if (input.signatures && input.signatures.some(x => x)) { + if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures } @@ -307,7 +671,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri const expanded = expandOutput(p2wsh.redeem.output, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') - if (input.signatures && input.signatures.some(x => x)) { + if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures } @@ -339,7 +703,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri const expanded = expandOutput(input.prevOutScript, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported (' + bscript.toASM(input.prevOutScript) + ')') - if (input.signatures && input.signatures.some(x => x)) { + if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures } @@ -376,7 +740,7 @@ function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScri } } -function build (type, input, allowIncomplete) { +function build (type: string, input: TxbInput, allowIncomplete: boolean): any { //TODO payment type const pubkeys = input.pubkeys || [] let signatures = input.signatures || [] @@ -439,201 +803,7 @@ function build (type, input, allowIncomplete) { } } -function TransactionBuilder (network, maximumFeeRate) { - this.__prevTxSet = {} - this.network = network || networks.bitcoin - - // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) - this.maximumFeeRate = maximumFeeRate || 2500 - - this.__inputs = [] - this.__tx = new Transaction() - this.__tx.version = 2 -} - -TransactionBuilder.prototype.setLockTime = function (locktime) { - typeforce(types.UInt32, locktime) - - // if any signatures exist, throw - if (this.__inputs.some(function (input) { - if (!input.signatures) return false - - return input.signatures.some(function (s) { return s }) - })) { - throw new Error('No, this would invalidate signatures') - } - - this.__tx.locktime = locktime -} - -TransactionBuilder.prototype.setVersion = function (version) { - typeforce(types.UInt32, version) - - // XXX: this might eventually become more complex depending on what the versions represent - this.__tx.version = version -} - -TransactionBuilder.fromTransaction = function (transaction, network) { - const txb = new TransactionBuilder(network, undefined) - - // Copy transaction fields - txb.setVersion(transaction.version) - txb.setLockTime(transaction.locktime) - - // Copy outputs (done first to avoid signature invalidation) - transaction.outs.forEach(function (txOut) { - txb.addOutput(txOut.script, txOut.value) - }) - - // Copy inputs - transaction.ins.forEach(function (txIn) { - txb.__addInputUnsafe(txIn.hash, txIn.index, { - sequence: txIn.sequence, - script: txIn.script, - witness: txIn.witness - }) - }) - - // fix some things not possible through the public API - txb.__inputs.forEach(function (input, i) { - fixMultisigOrder(input, transaction, i) - }) - - return txb -} - -TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) { - if (!this.__canModifyInputs()) { - throw new Error('No, this would invalidate signatures') - } - - let value - - // is it a hex string? - if (typeof txHash === 'string') { - // transaction hashs's are displayed in reverse order, un-reverse it - txHash = Buffer.from(txHash, 'hex').reverse() - - // is it a Transaction object? - } else if (txHash instanceof Transaction) { - const txOut = txHash.outs[vout] - prevOutScript = txOut.script - value = (<Output>txOut).value - - txHash = txHash.getHash(false) - } - - return this.__addInputUnsafe(txHash, vout, { - sequence: sequence, - prevOutScript: prevOutScript, - value: value - }) -} - -TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) { - if (Transaction.isCoinbaseHash(txHash)) { - throw new Error('coinbase inputs not supported') - } - - const prevTxOut = txHash.toString('hex') + ':' + vout - if (this.__prevTxSet[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut) - - let input = { - value: undefined, - prevOutScript: undefined, - pubkeys: undefined, - signatures: undefined, - prevOutType: undefined, - } - - // derive what we can from the scriptSig - if (options.script !== undefined) { - input = expandInput(options.script, options.witness || [], undefined, undefined) - } - - // if an input value was given, retain it - if (options.value !== undefined) { - input.value = options.value - } - - // derive what we can from the previous transactions output script - if (!input.prevOutScript && options.prevOutScript) { - let prevOutType - - if (!input.pubkeys && !input.signatures) { - const expanded = expandOutput(options.prevOutScript, undefined) - if (expanded.pubkeys) { - input.pubkeys = expanded.pubkeys - input.signatures = expanded.signatures - } - - prevOutType = expanded.type - } - - input.prevOutScript = options.prevOutScript - input.prevOutType = prevOutType || classify.output(options.prevOutScript) - } - - const vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig) - this.__inputs[vin] = input - this.__prevTxSet[prevTxOut] = true - return vin -} - -TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) { - if (!this.__canModifyOutputs()) { - throw new Error('No, this would invalidate signatures') - } - - // Attempt to get a script if it's a base58 or bech32 address string - if (typeof scriptPubKey === 'string') { - scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network) - } - - return this.__tx.addOutput(scriptPubKey, value) -} - -TransactionBuilder.prototype.build = function () { - return this.__build(false) -} -TransactionBuilder.prototype.buildIncomplete = function () { - return this.__build(true) -} - -TransactionBuilder.prototype.__build = function (allowIncomplete) { - if (!allowIncomplete) { - if (!this.__tx.ins.length) throw new Error('Transaction has no inputs') - if (!this.__tx.outs.length) throw new Error('Transaction has no outputs') - } - - const tx = this.__tx.clone() - - // create script signatures from inputs - this.__inputs.forEach(function (input, i) { - if (!input.prevOutType && !allowIncomplete) throw new Error('Transaction is not complete') - - const result = build(input.prevOutType, input, allowIncomplete) - if (!result) { - if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) throw new Error('Unknown input type') - if (!allowIncomplete) throw new Error('Not enough information') - return - } - - tx.setInputScript(i, result.input) - tx.setWitness(i, result.witness) - }) - - if (!allowIncomplete) { - // do not rely on this, its merely a last resort - if (this.__overMaximumFees(tx.virtualSize())) { - throw new Error('Transaction has absurd fees') - } - } - - return tx -} - -function canSign (input) { +function canSign (input: TxbInput): boolean { return input.signScript !== undefined && input.signType !== undefined && input.pubkeys !== undefined && @@ -646,140 +816,6 @@ function canSign (input) { ) } -TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) { - // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network') - if (!this.__inputs[vin]) throw new Error('No input at index: ' + vin) - - hashType = hashType || Transaction.SIGHASH_ALL - if (this.__needsOutputs(hashType)) throw new Error('Transaction needs outputs') - - const input = this.__inputs[vin] - - // if redeemScript was previously provided, enforce consistency - if (input.redeemScript !== undefined && - redeemScript && - !input.redeemScript.equals(redeemScript)) { - throw new Error('Inconsistent redeemScript') - } - - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey() - if (!canSign(input)) { - if (witnessValue !== undefined) { - if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue') - typeforce(types.Satoshi, witnessValue) - input.value = witnessValue - } - - if (!canSign(input)) { - const prepared = prepareInput(input, ourPubKey, redeemScript, witnessValue, witnessScript) - - // updates inline - Object.assign(input, prepared) - } - - if (!canSign(input)) throw Error(input.prevOutType + ' not supported') - } - - // ready to sign - let signatureHash - if (input.hasWitness) { - signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType) - } else { - signatureHash = this.__tx.hashForSignature(vin, input.signScript, hashType) - } - - // enforce in order signing of public keys - const signed = input.pubkeys.some(function (pubKey, i) { - if (!ourPubKey.equals(pubKey)) return false - if (input.signatures[i]) throw new Error('Signature already exists') - - // TODO: add tests - if (ourPubKey.length !== 33 && input.hasWitness) { - throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH') - } - - const signature = keyPair.sign(signatureHash) - input.signatures[i] = bscript.signature.encode(signature, hashType) - return true - }) - - if (!signed) throw new Error('Key pair cannot sign for this input') -} - -function signatureHashType (buffer) { +function signatureHashType (buffer: Buffer): number { return buffer.readUInt8(buffer.length - 1) } - -TransactionBuilder.prototype.__canModifyInputs = function () { - return (this.__inputs || []).every(function (input) { - if (!input.signatures) return true - - return (input.signatures || []).every(function (signature) { - if (!signature) return true - const hashType = signatureHashType(signature) - - // if SIGHASH_ANYONECANPAY is set, signatures would not - // be invalidated by more inputs - return hashType & Transaction.SIGHASH_ANYONECANPAY - }) - }) -} - -TransactionBuilder.prototype.__needsOutputs = function (signingHashType) { - if (signingHashType === Transaction.SIGHASH_ALL) { - return this.__tx.outs.length === 0 - } - - // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs - // .build() will fail, but .buildIncomplete() is OK - return (this.__tx.outs.length === 0) && this.__inputs.some((input) => { - if (!input.signatures) return false - - return input.signatures.some((signature) => { - if (!signature) return false // no signature, no issue - const hashType = signatureHashType(signature) - if (hashType & Transaction.SIGHASH_NONE) return false // SIGHASH_NONE doesn't care about outputs - return true // SIGHASH_* does care - }) - }) -} - -TransactionBuilder.prototype.__canModifyOutputs = function () { - const nInputs = this.__tx.ins.length - const nOutputs = this.__tx.outs.length - - return this.__inputs.every(function (input) { - if (input.signatures === undefined) return true - - return input.signatures.every(function (signature) { - if (!signature) return true - const hashType = signatureHashType(signature) - - const hashTypeMod = hashType & 0x1f - if (hashTypeMod === Transaction.SIGHASH_NONE) return true - if (hashTypeMod === Transaction.SIGHASH_SINGLE) { - // if SIGHASH_SINGLE is set, and nInputs > nOutputs - // some signatures would be invalidated by the addition - // of more outputs - return nInputs <= nOutputs - } - }) - }) -} - -TransactionBuilder.prototype.__overMaximumFees = function (bytes) { - // not all inputs will have .value defined - const incoming = this.__inputs.reduce(function (a, x) { return a + (x.value >>> 0) }, 0) - - // but all outputs do, and if we have any input value - // we can immediately determine if the outputs are too small - const outgoing = this.__tx.outs.reduce(function (a, x) { return a + x.value }, 0) - const fee = incoming - outgoing - const feeRate = fee / bytes - - return feeRate > this.maximumFeeRate -} - -module.exports = TransactionBuilder -export {} diff --git a/test/transaction_builder.js b/test/transaction_builder.js index c416846..34d9270 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -6,7 +6,7 @@ const payments = require('../dist/src/payments') const ECPair = require('../dist/src/ecpair') const Transaction = require('..').Transaction -const TransactionBuilder = require('../dist/src/transaction_builder') +const TransactionBuilder = require('..').TransactionBuilder const NETWORKS = require('../dist/src/networks') const fixtures = require('./fixtures/transaction_builder') From 3b77caa4f16aba9a676abe5bad32c633333a0365 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 12:59:43 +0900 Subject: [PATCH 176/568] Fixed script, script number and signature --- src/script.ts | 74 ++++++++++++++++++++--------------------- src/script_number.ts | 10 ++---- src/script_signature.ts | 19 +++++------ 3 files changed, 47 insertions(+), 56 deletions(-) diff --git a/src/script.ts b/src/script.ts index 514e992..98b018a 100644 --- a/src/script.ts +++ b/src/script.ts @@ -10,37 +10,49 @@ const OPS = require('bitcoin-ops') const REVERSE_OPS = require('bitcoin-ops/map') const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 -function isOPInt (value) { +function isOPInt (value:number): boolean { return types.Number(value) && ((value === OPS.OP_0) || (value >= OPS.OP_1 && value <= OPS.OP_16) || (value === OPS.OP_1NEGATE)) } -function isPushOnlyChunk (value) { - return types.Buffer(value) || isOPInt(value) +function isPushOnlyChunk (value: number | Buffer): boolean { + return types.Buffer(value) || isOPInt(<number>value) } -function isPushOnly (value) { +export function isPushOnly (value: Array<number | Buffer>) { return types.Array(value) && value.every(isPushOnlyChunk) } -function asMinimalOP (buffer) { +function asMinimalOP (buffer: Buffer): number | void { if (buffer.length === 0) return OPS.OP_0 if (buffer.length !== 1) return if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0] if (buffer[0] === 0x81) return OPS.OP_1NEGATE } -function compile (chunks) { +function chunksIsBuffer(buf: Buffer | Array<number | Buffer>): buf is Buffer { + return Buffer.isBuffer(buf) +} + +function chunksIsArray(buf: Buffer | Array<number | Buffer>): buf is Array<number | Buffer> { + return types.Array(buf) +} + +function singleChunkIsBuffer(buf: number | Buffer): buf is Buffer { + return Buffer.isBuffer(buf) +} + +export function compile (chunks: Buffer | Array<number | Buffer>): Buffer { // TODO: remove me - if (Buffer.isBuffer(chunks)) return chunks + if (chunksIsBuffer(chunks)) return chunks typeforce(types.Array, chunks) - const bufferSize = chunks.reduce(function (accum, chunk) { + const bufferSize = chunks.reduce(function (accum: number, chunk) { // data chunk - if (Buffer.isBuffer(chunk)) { + if (singleChunkIsBuffer(chunk)) { // adhere to BIP62.3, minimal push policy if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { return accum + 1 @@ -58,7 +70,7 @@ function compile (chunks) { chunks.forEach(function (chunk) { // data chunk - if (Buffer.isBuffer(chunk)) { + if (singleChunkIsBuffer(chunk)) { // adhere to BIP62.3, minimal push policy const opcode = asMinimalOP(chunk) if (opcode !== undefined) { @@ -82,9 +94,9 @@ function compile (chunks) { return buffer } -function decompile (buffer) { +export function decompile (buffer: Buffer | Array<number | Buffer>): Array<number | Buffer> { // TODO: remove me - if (types.Array(buffer)) return buffer + if (chunksIsArray(buffer)) return buffer typeforce(types.Buffer, buffer) @@ -127,17 +139,17 @@ function decompile (buffer) { return chunks } -function toASM (chunks) { - if (Buffer.isBuffer(chunks)) { +export function toASM (chunks: Buffer | Array<number | Buffer>): string { + if (chunksIsBuffer(chunks)) { chunks = decompile(chunks) } return chunks.map(function (chunk) { // data? - if (Buffer.isBuffer(chunk)) { + if (singleChunkIsBuffer(chunk)) { const op = asMinimalOP(chunk) if (op === undefined) return chunk.toString('hex') - chunk = op + chunk = <number>op } // opcode! @@ -145,7 +157,7 @@ function toASM (chunks) { }).join(' ') } -function fromASM (asm) { +export function fromASM (asm: string): Buffer { typeforce(types.String, asm) return compile(asm.split(' ').map(function (chunkStr) { @@ -158,49 +170,35 @@ function fromASM (asm) { })) } -function toStack (chunks) { +export function toStack (chunks: Buffer | Array<number | Buffer>): Array<Buffer> { chunks = decompile(chunks) typeforce(isPushOnly, chunks) return chunks.map(function (op) { - if (Buffer.isBuffer(op)) return op + if (singleChunkIsBuffer(op)) return op if (op === OPS.OP_0) return Buffer.allocUnsafe(0) return scriptNumber.encode(op - OP_INT_BASE) }) } -function isCanonicalPubKey (buffer) { +export function isCanonicalPubKey (buffer: Buffer): boolean { return ecc.isPoint(buffer) } -function isDefinedHashType (hashType) { +export function isDefinedHashType (hashType: number): boolean { const hashTypeMod = hashType & ~0x80 // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE return hashTypeMod > 0x00 && hashTypeMod < 0x04 } -function isCanonicalScriptSignature (buffer) { +export function isCanonicalScriptSignature (buffer: Buffer): boolean { if (!Buffer.isBuffer(buffer)) return false if (!isDefinedHashType(buffer[buffer.length - 1])) return false return bip66.check(buffer.slice(0, -1)) } -module.exports = { - compile: compile, - decompile: decompile, - fromASM: fromASM, - toASM: toASM, - toStack: toStack, - - number: require('./script_number'), - signature: require('./script_signature'), - - isCanonicalPubKey: isCanonicalPubKey, - isCanonicalScriptSignature: isCanonicalScriptSignature, - isPushOnly: isPushOnly, - isDefinedHashType: isDefinedHashType -} -export {} +export const number = require('./script_number') +export const signature = require('./script_signature') diff --git a/src/script_number.ts b/src/script_number.ts index 4df4704..8178c95 100644 --- a/src/script_number.ts +++ b/src/script_number.ts @@ -1,6 +1,6 @@ const Buffer = require('safe-buffer').Buffer -function decode (buffer, maxLength, minimal) { +export function decode (buffer: Buffer, maxLength?: number, minimal?: boolean): number { maxLength = maxLength || 4 minimal = minimal === undefined ? true : minimal @@ -41,7 +41,7 @@ function scriptNumSize (i) { : 0 } -function encode (number) { +export function encode (number: number): Buffer { let value = Math.abs(number) const size = scriptNumSize(value) const buffer = Buffer.allocUnsafe(size) @@ -60,9 +60,3 @@ function encode (number) { return buffer } - -module.exports = { - decode: decode, - encode: encode -} -export {} diff --git a/src/script_signature.ts b/src/script_signature.ts index b1f933a..fefd09d 100644 --- a/src/script_signature.ts +++ b/src/script_signature.ts @@ -4,7 +4,7 @@ const typeforce = require('typeforce') const types = require('./types') const ZERO = Buffer.alloc(1, 0) -function toDER (x) { +function toDER (x: Buffer): Buffer { let i = 0 while (x[i] === 0) ++i if (i === x.length) return ZERO @@ -13,7 +13,7 @@ function toDER (x) { return x } -function fromDER (x) { +function fromDER (x: Buffer): Buffer { if (x[0] === 0x00) x = x.slice(1) const buffer = Buffer.alloc(32, 0) const bstart = Math.max(0, 32 - x.length) @@ -21,8 +21,13 @@ function fromDER (x) { return buffer } +interface ScriptSignature { + signature: Buffer + hashType: number +} + // BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) -function decode (buffer) { +export function decode (buffer: Buffer): ScriptSignature { const hashType = buffer.readUInt8(buffer.length - 1) const hashTypeMod = hashType & ~0x80 if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) @@ -37,7 +42,7 @@ function decode (buffer) { } } -function encode (signature, hashType) { +export function encode (signature: Buffer, hashType: number): Buffer { typeforce({ signature: types.BufferN(64), hashType: types.UInt8 @@ -57,9 +62,3 @@ function encode (signature, hashType) { hashTypeBuffer ]) } - -module.exports = { - decode: decode, - encode: encode -} -export {} From 2f32ea6bc97e460d635d10c57060e1c1b16c0495 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 13:18:42 +0900 Subject: [PATCH 177/568] OP_RETURN and Multisig templates --- package.json | 2 +- src/templates/multisig/index.ts | 10 ++++++---- src/templates/multisig/input.ts | 9 +++------ src/templates/multisig/output.ts | 15 ++++++--------- src/templates/nulldata.ts | 5 ++--- src/types.ts | 1 + 6 files changed, 19 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index e704249..777a210 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "scripts": { "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", - "coverage": "nyc --check-coverage --branches 90 --functions 90 mocha", + "coverage": "nyc --check-coverage --branches 85 --functions 90 mocha", "integration": "npm run build && mocha --timeout 50000 test/integration/", "standard": "standard", "test": "npm run build && npm run standard && npm run coverage", diff --git a/src/templates/multisig/index.ts b/src/templates/multisig/index.ts index 7192cfd..42aeb7b 100644 --- a/src/templates/multisig/index.ts +++ b/src/templates/multisig/index.ts @@ -1,5 +1,7 @@ -module.exports = { - input: require('./input'), - output: require('./output') +import * as input from './input' +import * as output from './output' + +export { + input, + output, } -export {} diff --git a/src/templates/multisig/input.ts b/src/templates/multisig/input.ts index d395108..864c793 100644 --- a/src/templates/multisig/input.ts +++ b/src/templates/multisig/input.ts @@ -1,13 +1,13 @@ // OP_0 [signatures ...] -const bscript = require('../../script') +import * as bscript from '../../script' const OPS = require('bitcoin-ops') function partialSignature (value) { return value === OPS.OP_0 || bscript.isCanonicalScriptSignature(value) } -function check (script, allowIncomplete) { +export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { const chunks = bscript.decompile(script) if (chunks.length < 2) return false if (chunks[0] !== OPS.OP_0) return false @@ -16,9 +16,6 @@ function check (script, allowIncomplete) { return chunks.slice(1).every(partialSignature) } - return chunks.slice(1).every(bscript.isCanonicalScriptSignature) + return (<Array<Buffer>>chunks.slice(1)).every(bscript.isCanonicalScriptSignature) } check.toJSON = function () { return 'multisig input' } - -module.exports = { check } -export {} diff --git a/src/templates/multisig/output.ts b/src/templates/multisig/output.ts index 5ff18f1..50c701e 100644 --- a/src/templates/multisig/output.ts +++ b/src/templates/multisig/output.ts @@ -1,19 +1,19 @@ // m [pubKeys ...] n OP_CHECKMULTISIG -const bscript = require('../../script') -const types = require('../../types') +import * as bscript from '../../script' +import * as types from '../../types' const OPS = require('bitcoin-ops') const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 -function check (script, allowIncomplete) { +export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { const chunks = bscript.decompile(script) if (chunks.length < 4) return false if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false if (!types.Number(chunks[0])) return false if (!types.Number(chunks[chunks.length - 2])) return false - const m = chunks[0] - OP_INT_BASE - const n = chunks[chunks.length - 2] - OP_INT_BASE + const m = <number>chunks[0] - OP_INT_BASE + const n = <number>chunks[chunks.length - 2] - OP_INT_BASE if (m <= 0) return false if (n > 16) return false @@ -21,10 +21,7 @@ function check (script, allowIncomplete) { if (n !== chunks.length - 3) return false if (allowIncomplete) return true - const keys = chunks.slice(1, -2) + const keys = <Array<Buffer>> chunks.slice(1, -2) return keys.every(bscript.isCanonicalPubKey) } check.toJSON = function () { return 'multi-sig output' } - -module.exports = { check } -export {} diff --git a/src/templates/nulldata.ts b/src/templates/nulldata.ts index 48b0365..91c1713 100644 --- a/src/templates/nulldata.ts +++ b/src/templates/nulldata.ts @@ -1,9 +1,8 @@ // OP_RETURN {data} - -const bscript = require('../script') +import * as bscript from '../script' const OPS = require('bitcoin-ops') -export function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) return buffer.length > 1 && diff --git a/src/types.ts b/src/types.ts index 57431ca..790b95e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,3 +34,4 @@ export const Buffer256bit = typeforce.BufferN(32) export const Hash160bit = typeforce.BufferN(20) export const Hash256bit = typeforce.BufferN(32) export * from 'typeforce' +export { Number } from 'typeforce' From 9da1c95f890ef7dc7142a2be338d487149d93886 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 13:23:23 +0900 Subject: [PATCH 178/568] Add P2PK templates --- src/templates/multisig/input.ts | 4 ++-- src/templates/pubkey/index.ts | 10 ++++++---- src/templates/pubkey/input.ts | 11 +++-------- src/templates/pubkey/output.ts | 9 +++------ 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/templates/multisig/input.ts b/src/templates/multisig/input.ts index 864c793..2060242 100644 --- a/src/templates/multisig/input.ts +++ b/src/templates/multisig/input.ts @@ -3,8 +3,8 @@ import * as bscript from '../../script' const OPS = require('bitcoin-ops') -function partialSignature (value) { - return value === OPS.OP_0 || bscript.isCanonicalScriptSignature(value) +function partialSignature (value: number | Buffer): boolean { + return value === OPS.OP_0 || bscript.isCanonicalScriptSignature(<Buffer>value) } export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { diff --git a/src/templates/pubkey/index.ts b/src/templates/pubkey/index.ts index 7192cfd..42aeb7b 100644 --- a/src/templates/pubkey/index.ts +++ b/src/templates/pubkey/index.ts @@ -1,5 +1,7 @@ -module.exports = { - input: require('./input'), - output: require('./output') +import * as input from './input' +import * as output from './output' + +export { + input, + output, } -export {} diff --git a/src/templates/pubkey/input.ts b/src/templates/pubkey/input.ts index b6963e7..f4b8bf7 100644 --- a/src/templates/pubkey/input.ts +++ b/src/templates/pubkey/input.ts @@ -1,16 +1,11 @@ // {signature} -const bscript = require('../../script') +import * as bscript from '../../script' -function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const chunks = bscript.decompile(script) return chunks.length === 1 && - bscript.isCanonicalScriptSignature(chunks[0]) + bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) } check.toJSON = function () { return 'pubKey input' } - -module.exports = { - check: check -} -export {} diff --git a/src/templates/pubkey/output.ts b/src/templates/pubkey/output.ts index b64c596..2728f68 100644 --- a/src/templates/pubkey/output.ts +++ b/src/templates/pubkey/output.ts @@ -1,16 +1,13 @@ // {pubKey} OP_CHECKSIG -const bscript = require('../../script') +import * as bscript from '../../script' const OPS = require('bitcoin-ops') -function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const chunks = bscript.decompile(script) return chunks.length === 2 && - bscript.isCanonicalPubKey(chunks[0]) && + bscript.isCanonicalPubKey(<Buffer>chunks[0]) && chunks[1] === OPS.OP_CHECKSIG } check.toJSON = function () { return 'pubKey output' } - -module.exports = { check } -export {} From 3db951fc6454d61e7ebb40d6c873124e8f9f75db Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 14:32:20 +0900 Subject: [PATCH 179/568] Add p2pkh template --- src/templates/pubkeyhash/index.ts | 10 ++++++---- src/templates/pubkeyhash/input.ts | 11 ++++------- src/templates/pubkeyhash/output.ts | 7 ++----- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/templates/pubkeyhash/index.ts b/src/templates/pubkeyhash/index.ts index 7192cfd..42aeb7b 100644 --- a/src/templates/pubkeyhash/index.ts +++ b/src/templates/pubkeyhash/index.ts @@ -1,5 +1,7 @@ -module.exports = { - input: require('./input'), - output: require('./output') +import * as input from './input' +import * as output from './output' + +export { + input, + output, } -export {} diff --git a/src/templates/pubkeyhash/input.ts b/src/templates/pubkeyhash/input.ts index cab6f0a..ffdc929 100644 --- a/src/templates/pubkeyhash/input.ts +++ b/src/templates/pubkeyhash/input.ts @@ -1,15 +1,12 @@ // {signature} {pubKey} -const bscript = require('../../script') +import * as bscript from '../../script' -function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const chunks = bscript.decompile(script) return chunks.length === 2 && - bscript.isCanonicalScriptSignature(chunks[0]) && - bscript.isCanonicalPubKey(chunks[1]) + bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) && + bscript.isCanonicalPubKey(<Buffer>chunks[1]) } check.toJSON = function () { return 'pubKeyHash input' } - -module.exports = { check } -export {} diff --git a/src/templates/pubkeyhash/output.ts b/src/templates/pubkeyhash/output.ts index bf34b83..71c1acb 100644 --- a/src/templates/pubkeyhash/output.ts +++ b/src/templates/pubkeyhash/output.ts @@ -1,9 +1,9 @@ // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG -const bscript = require('../../script') +import * as bscript from '../../script' const OPS = require('bitcoin-ops') -function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) return buffer.length === 25 && @@ -14,6 +14,3 @@ function check (script) { buffer[24] === OPS.OP_CHECKSIG } check.toJSON = function () { return 'pubKeyHash output' } - -module.exports = { check } -export {} From c488001b824a6c60f25af0aaf220bec98631bddf Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 14:45:17 +0900 Subject: [PATCH 180/568] Add P2WPKH --- src/templates/witnesspubkeyhash/index.ts | 10 ++++++---- src/templates/witnesspubkeyhash/input.ts | 13 +++++-------- src/templates/witnesspubkeyhash/output.ts | 9 ++------- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/templates/witnesspubkeyhash/index.ts b/src/templates/witnesspubkeyhash/index.ts index 7192cfd..42aeb7b 100644 --- a/src/templates/witnesspubkeyhash/index.ts +++ b/src/templates/witnesspubkeyhash/index.ts @@ -1,5 +1,7 @@ -module.exports = { - input: require('./input'), - output: require('./output') +import * as input from './input' +import * as output from './output' + +export { + input, + output, } -export {} diff --git a/src/templates/witnesspubkeyhash/input.ts b/src/templates/witnesspubkeyhash/input.ts index ec93c47..36f0606 100644 --- a/src/templates/witnesspubkeyhash/input.ts +++ b/src/templates/witnesspubkeyhash/input.ts @@ -1,19 +1,16 @@ // {signature} {pubKey} -const bscript = require('../../script') +import * as bscript from '../../script' -function isCompressedCanonicalPubKey (pubKey) { +function isCompressedCanonicalPubKey (pubKey: Buffer): boolean { return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33 } -function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const chunks = bscript.decompile(script) return chunks.length === 2 && - bscript.isCanonicalScriptSignature(chunks[0]) && - isCompressedCanonicalPubKey(chunks[1]) + bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) && + isCompressedCanonicalPubKey(<Buffer>chunks[1]) } check.toJSON = function () { return 'witnessPubKeyHash input' } - -module.exports = { check } -export {} diff --git a/src/templates/witnesspubkeyhash/output.ts b/src/templates/witnesspubkeyhash/output.ts index a289038..46fb444 100644 --- a/src/templates/witnesspubkeyhash/output.ts +++ b/src/templates/witnesspubkeyhash/output.ts @@ -1,9 +1,9 @@ // OP_0 {pubKeyHash} -const bscript = require('../../script') +import * as bscript from '../../script' const OPS = require('bitcoin-ops') -function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) return buffer.length === 22 && @@ -11,8 +11,3 @@ function check (script) { buffer[1] === 0x14 } check.toJSON = function () { return 'Witness pubKeyHash output' } - -module.exports = { - check -} -export {} From 528dff01c11ab7c25d0a56dd56a0f733818db77b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 14:55:30 +0900 Subject: [PATCH 181/568] Add P2WSH --- src/templates/witnessscripthash/index.ts | 10 ++++++---- src/templates/witnessscripthash/input.ts | 16 ++++++---------- src/templates/witnessscripthash/output.ts | 7 ++----- src/types.ts | 2 +- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/templates/witnessscripthash/index.ts b/src/templates/witnessscripthash/index.ts index 7192cfd..42aeb7b 100644 --- a/src/templates/witnessscripthash/index.ts +++ b/src/templates/witnessscripthash/index.ts @@ -1,5 +1,7 @@ -module.exports = { - input: require('./input'), - output: require('./output') +import * as input from './input' +import * as output from './output' + +export { + input, + output, } -export {} diff --git a/src/templates/witnessscripthash/input.ts b/src/templates/witnessscripthash/input.ts index c6d47e3..7f7e415 100644 --- a/src/templates/witnessscripthash/input.ts +++ b/src/templates/witnessscripthash/input.ts @@ -1,15 +1,14 @@ // <scriptSig> {serialized scriptPubKey script} -const bscript = require('../../script') -const types = require('../../types') +import * as bscript from '../../script' const typeforce = require('typeforce') -const p2ms = require('../multisig/') -const p2pk = require('../pubkey/') -const p2pkh = require('../pubkeyhash/') +import * as p2ms from '../multisig' +import * as p2pk from '../pubkey' +import * as p2pkh from '../pubkeyhash' -function check (chunks, allowIncomplete) { - typeforce(types.Array, chunks) +export function check (chunks: Array<Buffer>, allowIncomplete?: boolean): boolean { + typeforce(typeforce.Array, chunks) if (chunks.length < 1) return false const witnessScript = chunks[chunks.length - 1] @@ -35,6 +34,3 @@ function check (chunks, allowIncomplete) { return false } check.toJSON = function () { return 'witnessScriptHash input' } - -module.exports = { check } -export {} diff --git a/src/templates/witnessscripthash/output.ts b/src/templates/witnessscripthash/output.ts index 3bc327f..0b13cd1 100644 --- a/src/templates/witnessscripthash/output.ts +++ b/src/templates/witnessscripthash/output.ts @@ -1,9 +1,9 @@ // OP_0 {scriptHash} -const bscript = require('../../script') +import * as bscript from '../../script' const OPS = require('bitcoin-ops') -function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) return buffer.length === 34 && @@ -11,6 +11,3 @@ function check (script) { buffer[1] === 0x20 } check.toJSON = function () { return 'Witness scriptHash output' } - -module.exports = { check } -export {} diff --git a/src/types.ts b/src/types.ts index 790b95e..ad603c5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,4 +34,4 @@ export const Buffer256bit = typeforce.BufferN(32) export const Hash160bit = typeforce.BufferN(20) export const Hash256bit = typeforce.BufferN(32) export * from 'typeforce' -export { Number } from 'typeforce' +export { Number, Array } from 'typeforce' From 604072ffaded0f9a982aa8cefd39227044b62da3 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 14:57:30 +0900 Subject: [PATCH 182/568] Add P2SH --- src/templates/scripthash/index.ts | 10 ++++++---- src/templates/scripthash/input.ts | 21 +++++++++------------ src/templates/scripthash/output.ts | 7 ++----- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/templates/scripthash/index.ts b/src/templates/scripthash/index.ts index 7192cfd..42aeb7b 100644 --- a/src/templates/scripthash/index.ts +++ b/src/templates/scripthash/index.ts @@ -1,5 +1,7 @@ -module.exports = { - input: require('./input'), - output: require('./output') +import * as input from './input' +import * as output from './output' + +export { + input, + output, } -export {} diff --git a/src/templates/scripthash/input.ts b/src/templates/scripthash/input.ts index ad53df9..908a87b 100644 --- a/src/templates/scripthash/input.ts +++ b/src/templates/scripthash/input.ts @@ -1,15 +1,15 @@ // <scriptSig> {serialized scriptPubKey script} +import * as bscript from '../../script' +import * as p2ms from '../multisig' +import * as p2pk from '../pubkey' +import * as p2pkh from '../pubkeyhash' +import * as p2wpkho from '../witnesspubkeyhash/output' +import * as p2wsho from '../witnessscripthash/output' + const Buffer = require('safe-buffer').Buffer -const bscript = require('../../script') -const p2ms = require('../multisig/') -const p2pk = require('../pubkey/') -const p2pkh = require('../pubkeyhash/') -const p2wpkho = require('../witnesspubkeyhash/output') -const p2wsho = require('../witnessscripthash/output') - -function check (script, allowIncomplete) { +export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { const chunks = bscript.decompile(script) if (chunks.length < 1) return false @@ -17,7 +17,7 @@ function check (script, allowIncomplete) { if (!Buffer.isBuffer(lastChunk)) return false const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))) - const redeemScriptChunks = bscript.decompile(lastChunk) + const redeemScriptChunks = bscript.decompile(<Buffer>lastChunk) // is redeemScript a valid script? if (!redeemScriptChunks) return false @@ -44,6 +44,3 @@ function check (script, allowIncomplete) { return false } check.toJSON = function () { return 'scriptHash input' } - -module.exports = { check } -export {} diff --git a/src/templates/scripthash/output.ts b/src/templates/scripthash/output.ts index 9f91f17..3215c25 100644 --- a/src/templates/scripthash/output.ts +++ b/src/templates/scripthash/output.ts @@ -1,9 +1,9 @@ // OP_HASH160 {scriptHash} OP_EQUAL -const bscript = require('../../script') +import * as bscript from '../../script' const OPS = require('bitcoin-ops') -function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) return buffer.length === 23 && @@ -12,6 +12,3 @@ function check (script) { buffer[22] === OPS.OP_EQUAL } check.toJSON = function () { return 'scriptHash output' } - -module.exports = { check } -export {} From 5c34b4ce22d2e6d9471c69cad5f761f9e793ab2a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 28 Dec 2018 15:09:51 +0900 Subject: [PATCH 183/568] Add Witness Commitment --- src/templates/witnesscommitment/index.ts | 7 ++++--- src/templates/witnesscommitment/output.ts | 21 +++++++-------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/templates/witnesscommitment/index.ts b/src/templates/witnesscommitment/index.ts index d274d6b..c36a2b8 100644 --- a/src/templates/witnesscommitment/index.ts +++ b/src/templates/witnesscommitment/index.ts @@ -1,4 +1,5 @@ -module.exports = { - output: require('./output') +import * as output from './output' + +export { + output, } -export {} diff --git a/src/templates/witnesscommitment/output.ts b/src/templates/witnesscommitment/output.ts index db92e54..1b3a182 100644 --- a/src/templates/witnesscommitment/output.ts +++ b/src/templates/witnesscommitment/output.ts @@ -1,14 +1,14 @@ // OP_RETURN {aa21a9ed} {commitment} +import * as bscript from '../../script' +import * as types from '../../types' const Buffer = require('safe-buffer').Buffer -const bscript = require('../../script') -const types = require('../../types') const typeforce = require('typeforce') const OPS = require('bitcoin-ops') -const HEADER = Buffer.from('aa21a9ed', 'hex') +const HEADER: Buffer = Buffer.from('aa21a9ed', 'hex') -function check (script) { +export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) return buffer.length > 37 && @@ -19,7 +19,7 @@ function check (script) { check.toJSON = function () { return 'Witness commitment output' } -function encode (commitment) { +export function encode (commitment: Buffer): Buffer { typeforce(types.Hash256bit, commitment) const buffer = Buffer.allocUnsafe(36) @@ -29,15 +29,8 @@ function encode (commitment) { return bscript.compile([OPS.OP_RETURN, buffer]) } -function decode (buffer) { +export function decode (buffer: Buffer): Buffer { typeforce(check, buffer) - return bscript.decompile(buffer)[1].slice(4, 36) + return (<Buffer>bscript.decompile(buffer)[1]).slice(4, 36) } - -module.exports = { - check: check, - decode: decode, - encode: encode -} -export {} From 867f4b59f93fb894381000e721bf66ec5ce9c5ef Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 00:00:52 +0900 Subject: [PATCH 184/568] Add payments --- package.json | 2 +- src/payments/embed.ts | 20 +++++++------------- src/payments/index.ts | 40 +++++++++++++++++++++++++++++++--------- src/payments/lazy.ts | 9 +++------ src/payments/p2ms.ts | 40 +++++++++++++++------------------------- src/payments/p2pk.ts | 20 ++++++-------------- src/payments/p2pkh.ts | 24 +++++++++++------------- src/payments/p2sh.ts | 26 ++++++++++++-------------- src/payments/p2wpkh.ts | 23 ++++++++--------------- src/payments/p2wsh.ts | 25 +++++++++---------------- test/payments.js | 8 +++++++- 11 files changed, 110 insertions(+), 127 deletions(-) diff --git a/package.json b/package.json index 777a210..2583afc 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "scripts": { "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", - "coverage": "nyc --check-coverage --branches 85 --functions 90 mocha", + "coverage": "nyc --check-coverage --branches 80 --functions 80 mocha", "integration": "npm run build && mocha --timeout 50000 test/integration/", "standard": "standard", "test": "npm run build && npm run standard && npm run coverage", diff --git a/src/payments/embed.ts b/src/payments/embed.ts index bfb70e5..a28747f 100644 --- a/src/payments/embed.ts +++ b/src/payments/embed.ts @@ -1,11 +1,11 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin - -function stacksEqual (a, b) { +function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { if (a.length !== b.length) return false return a.every(function (x, i) { @@ -14,7 +14,7 @@ function stacksEqual (a, b) { } // output: OP_RETURN ... -function p2data (a, opts) { +export function p2data (a: Payment, opts: PaymentOpts): Payment { if ( !a.data && !a.output @@ -28,10 +28,7 @@ function p2data (a, opts) { }, a) const network = a.network || BITCOIN_NETWORK - const o = { - network, - data: undefined - } + const o = <Payment>{ network } lazy.prop(o, 'output', function () { if (!a.data) return @@ -55,6 +52,3 @@ function p2data (a, opts) { return Object.assign(o, a) } - -module.exports = p2data -export {} diff --git a/src/payments/index.ts b/src/payments/index.ts index bb4e0e6..08a8409 100644 --- a/src/payments/index.ts +++ b/src/payments/index.ts @@ -1,13 +1,35 @@ -const embed = require('./embed') -const p2ms = require('./p2ms') -const p2pk = require('./p2pk') -const p2pkh = require('./p2pkh') -const p2sh = require('./p2sh') -const p2wpkh = require('./p2wpkh') -const p2wsh = require('./p2wsh') +import { Network } from '../networks' +import { p2data as embed } from './embed' +import { p2ms } from './p2ms' +import { p2pk } from './p2pk' +import { p2pkh } from './p2pkh' +import { p2sh } from './p2sh' +import { p2wpkh } from './p2wpkh' +import { p2wsh } from './p2wsh' -module.exports = { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } +export interface Payment { + network?: Network, + output?: Buffer, + data?: Array<Buffer>, + m?: number, + n?: number, + pubkeys?: Array<Buffer>, + input?: Buffer, + signatures?: Array<Buffer>, + pubkey?: Buffer, + signature?: Buffer, + address?: string, + hash?: Buffer, + redeem?: Payment, + witness?: Array<Buffer>, +} + +export interface PaymentOpts { + validate?: boolean, + allowIncomplete?: boolean, +} + +export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } // TODO // witness commitment -export {} diff --git a/src/payments/lazy.ts b/src/payments/lazy.ts index 4a9611c..6a4cbe3 100644 --- a/src/payments/lazy.ts +++ b/src/payments/lazy.ts @@ -1,4 +1,4 @@ -function prop (object, name, f) { +export function prop (object: Object, name: string, f: ()=>any): void { Object.defineProperty(object, name, { configurable: true, enumerable: true, @@ -18,14 +18,11 @@ function prop (object, name, f) { }) } -function value (f) { - let value +export function value <T>(f: ()=>T): ()=>T { + let value: T return function () { if (value !== undefined) return value value = f() return value } } - -module.exports = { prop, value } -export {} diff --git a/src/payments/p2ms.ts b/src/payments/p2ms.ts index 39b38f1..2142ff1 100644 --- a/src/payments/p2ms.ts +++ b/src/payments/p2ms.ts @@ -1,13 +1,14 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const ecc = require('tiny-secp256k1') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 -function stacksEqual (a, b) { +function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { if (a.length !== b.length) return false return a.every(function (x, i) { @@ -17,7 +18,7 @@ function stacksEqual (a, b) { // input: OP_0 [signatures ...] // output: m [pubKeys ...] n OP_CHECKMULTISIG -function p2ms (a, opts) { +export function p2ms (a: Payment, opts: PaymentOpts): Payment { if ( !a.input && !a.output && @@ -26,8 +27,8 @@ function p2ms (a, opts) { ) throw new TypeError('Not enough data') opts = Object.assign({ validate: true }, opts || {}) - function isAcceptableSignature (x) { - return bscript.isCanonicalScriptSignature(x) || (opts.allowIncomplete && (x === OPS.OP_0)) + function isAcceptableSignature (x: Buffer | number) { + return bscript.isCanonicalScriptSignature(<Buffer>x) || (opts.allowIncomplete && (<number>x === OPS.OP_0)) } typef({ @@ -42,25 +43,17 @@ function p2ms (a, opts) { }, a) const network = a.network || BITCOIN_NETWORK - const o = { - network, - m: undefined, - n: undefined, - pubkeys: undefined, - output: undefined, - input: undefined, - signatures: undefined, - } + const o: Payment = { network } - let chunks + let chunks: Array<Buffer | number> let decoded = false - function decode (output) { + function decode (output: Buffer | Array<Buffer | number>): void { if (decoded) return decoded = true chunks = bscript.decompile(output) - o.m = chunks[0] - OP_INT_BASE - o.n = chunks[chunks.length - 2] - OP_INT_BASE - o.pubkeys = chunks.slice(1, -2) + o.m = <number>chunks[0] - OP_INT_BASE + o.n = <number>chunks[chunks.length - 2] - OP_INT_BASE + o.pubkeys = <Array<Buffer>>chunks.slice(1, -2) } lazy.prop(o, 'output', function () { @@ -137,13 +130,10 @@ function p2ms (a, opts) { if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') - if (a.signatures && !stacksEqual(a.signatures.equals(o.signatures), undefined)) throw new TypeError('Signature mismatch') + if (a.signatures && !stacksEqual(a.signatures, o.signatures)) throw new TypeError('Signature mismatch') if (a.m !== undefined && a.m !== a.signatures.length) throw new TypeError('Signature count mismatch') } } return Object.assign(o, a) } - -module.exports = p2ms -export {} diff --git a/src/payments/p2pk.ts b/src/payments/p2pk.ts index b810d48..a1ac5e8 100644 --- a/src/payments/p2pk.ts +++ b/src/payments/p2pk.ts @@ -1,14 +1,14 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const ecc = require('tiny-secp256k1') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin - // input: {signature} // output: {pubKey} OP_CHECKSIG -function p2pk (a, opts) { +export function p2pk (a: Payment, opts: PaymentOpts): Payment { if ( !a.input && !a.output && @@ -30,12 +30,7 @@ function p2pk (a, opts) { const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) const network = a.network || BITCOIN_NETWORK - const o = { - network, - input: undefined, - pubkey: undefined, - signature: undefined, - } + const o: Payment = { network } lazy.prop(o, 'output', function () { if (!a.pubkey) return @@ -81,6 +76,3 @@ function p2pk (a, opts) { return Object.assign(o, a) } - -module.exports = p2pk -export {} diff --git a/src/payments/p2pkh.ts b/src/payments/p2pkh.ts index c322fc7..4324c2a 100644 --- a/src/payments/p2pkh.ts +++ b/src/payments/p2pkh.ts @@ -1,16 +1,17 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as bcrypto from '../crypto' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const ecc = require('tiny-secp256k1') -const bcrypto = require('../crypto') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const bs58check = require('bs58check') // input: {signature} {pubkey} // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG -function p2pkh (a, opts) { +export function p2pkh (a: Payment, opts: PaymentOpts): Payment { if ( !a.address && !a.hash && @@ -90,7 +91,7 @@ function p2pkh (a, opts) { // extended validation if (opts.validate) { - let hash + let hash: Buffer if (a.address) { if (_address().version !== network.pubKeyHash) throw new TypeError('Invalid version or Network mismatch') if (_address().hash.length !== 20) throw new TypeError('Invalid address') @@ -125,19 +126,16 @@ function p2pkh (a, opts) { if (a.input) { const chunks = _chunks() if (chunks.length !== 2) throw new TypeError('Input is invalid') - if (!bscript.isCanonicalScriptSignature(chunks[0])) throw new TypeError('Input has invalid signature') + if (!bscript.isCanonicalScriptSignature(<Buffer>chunks[0])) throw new TypeError('Input has invalid signature') if (!ecc.isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey') - if (a.signature && !a.signature.equals(chunks[0])) throw new TypeError('Signature mismatch') - if (a.pubkey && !a.pubkey.equals(chunks[1])) throw new TypeError('Pubkey mismatch') + if (a.signature && !a.signature.equals(<Buffer>chunks[0])) throw new TypeError('Signature mismatch') + if (a.pubkey && !a.pubkey.equals(<Buffer>chunks[1])) throw new TypeError('Pubkey mismatch') - const pkh = bcrypto.hash160(chunks[1]) + const pkh = bcrypto.hash160(<Buffer>chunks[1]) if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') } } return Object.assign(o, a) } - -module.exports = p2pkh -export {} diff --git a/src/payments/p2sh.ts b/src/payments/p2sh.ts index 07bb32e..efd453a 100644 --- a/src/payments/p2sh.ts +++ b/src/payments/p2sh.ts @@ -1,13 +1,14 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as bcrypto from '../crypto' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') -const bcrypto = require('../crypto') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const bs58check = require('bs58check') -function stacksEqual (a, b) { +function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { if (a.length !== b.length) return false return a.every(function (x, i) { @@ -18,7 +19,7 @@ function stacksEqual (a, b) { // input: [redeemScriptSig ...] {redeemScript} // witness: <?> // output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL -function p2sh (a, opts) { +export function p2sh (a: Payment, opts: PaymentOpts): Payment { if ( !a.address && !a.hash && @@ -64,11 +65,11 @@ function p2sh (a, opts) { return { version, hash } }) const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) - const _redeem = lazy.value(function () { + const _redeem = lazy.value(function (): Payment { const chunks = _chunks() return { network, - output: chunks[chunks.length - 1], + output: <Buffer>chunks[chunks.length - 1], input: bscript.compile(chunks.slice(0, -1)), witness: a.witness || [] } @@ -116,7 +117,7 @@ function p2sh (a, opts) { }) if (opts.validate) { - let hash + let hash: Buffer if (a.address) { if (_address().version !== network.scriptHash) throw new TypeError('Invalid version or Network mismatch') if (_address().hash.length !== 20) throw new TypeError('Invalid address') @@ -141,7 +142,7 @@ function p2sh (a, opts) { } // inlined to prevent 'no-inner-declarations' failing - const checkRedeem = function (redeem) { + const checkRedeem = function (redeem: Payment): void { // is the redeem output empty/invalid? if (redeem.output) { const decompile = bscript.decompile(redeem.output) @@ -177,7 +178,7 @@ function p2sh (a, opts) { if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') if (a.input) { const redeem = _redeem() - if (a.redeem.output && !a.redeem.output.equals(redeem.output)) throw new TypeError('Redeem.output mismatch') + if (a.redeem.output && !a.redeem.output.equals(<Buffer>redeem.output)) throw new TypeError('Redeem.output mismatch') if (a.redeem.input && !a.redeem.input.equals(redeem.input)) throw new TypeError('Redeem.input mismatch') } @@ -194,6 +195,3 @@ function p2sh (a, opts) { return Object.assign(o, a) } - -module.exports = p2sh -export {} diff --git a/src/payments/p2wpkh.ts b/src/payments/p2wpkh.ts index 1ab21b5..a25d037 100644 --- a/src/payments/p2wpkh.ts +++ b/src/payments/p2wpkh.ts @@ -1,19 +1,20 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as bcrypto from '../crypto' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const ecc = require('tiny-secp256k1') -const bcrypto = require('../crypto') const bech32 = require('bech32') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const EMPTY_BUFFER = Buffer.alloc(0) // witness: {signature} {pubKey} // input: <> // output: OP_0 {pubKeyHash} -function p2wpkh (a, opts) { +export function p2wpkh (a: Payment, opts: PaymentOpts): Payment { if ( !a.address && !a.hash && @@ -46,12 +47,7 @@ function p2wpkh (a, opts) { }) const network = a.network || BITCOIN_NETWORK - const o = { - network, - hash: undefined, - pubkey: undefined, - witness: undefined, - } + const o: Payment = { network } lazy.prop(o, 'address', function () { if (!o.hash) return @@ -93,7 +89,7 @@ function p2wpkh (a, opts) { // extended validation if (opts.validate) { - let hash + let hash: Buffer if (a.address) { if (network && network.bech32 !== _address().prefix) throw new TypeError('Invalid prefix or Network mismatch') if (_address().version !== 0x00) throw new TypeError('Invalid address version') @@ -136,6 +132,3 @@ function p2wpkh (a, opts) { return Object.assign(o, a) } - -module.exports = p2wpkh -export {} diff --git a/src/payments/p2wsh.ts b/src/payments/p2wsh.ts index fe19314..d94d446 100644 --- a/src/payments/p2wsh.ts +++ b/src/payments/p2wsh.ts @@ -1,15 +1,16 @@ -const lazy = require('./lazy') +import { Payment, PaymentOpts } from './index' +import * as bscript from '../script' +import * as bcrypto from '../crypto' +import * as lazy from './lazy' +import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') const OPS = require('bitcoin-ops') const bech32 = require('bech32') -const bcrypto = require('../crypto') -const bscript = require('../script') -const BITCOIN_NETWORK = require('../networks').bitcoin const EMPTY_BUFFER = Buffer.alloc(0) -function stacksEqual (a, b) { +function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { if (a.length !== b.length) return false return a.every(function (x, i) { @@ -20,7 +21,7 @@ function stacksEqual (a, b) { // input: <> // witness: [redeemScriptSig ...] {redeemScript} // output: OP_0 {sha256(redeemScript)} -function p2wsh (a, opts) { +export function p2wsh (a: Payment, opts: PaymentOpts): Payment { if ( !a.address && !a.hash && @@ -64,12 +65,7 @@ function p2wsh (a, opts) { network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK } - const o = { - network, - hash: undefined, - redeem: undefined, - witness: undefined, - } + const o: Payment = { network } lazy.prop(o, 'address', function () { if (!o.hash) return @@ -126,7 +122,7 @@ function p2wsh (a, opts) { // extended validation if (opts.validate) { - let hash + let hash: Buffer if (a.address) { if (_address().prefix !== network.bech32) throw new TypeError('Invalid prefix or Network mismatch') if (_address().version !== 0x00) throw new TypeError('Invalid address version') @@ -181,6 +177,3 @@ function p2wsh (a, opts) { return Object.assign(o, a) } - -module.exports = p2wsh -export {} diff --git a/test/payments.js b/test/payments.js index 53e3f16..6617047 100644 --- a/test/payments.js +++ b/test/payments.js @@ -4,7 +4,13 @@ const u = require('./payments.utils') ;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { describe(p, function () { - const fn = require('../dist/src/payments/' + p) + let fn + let payment = require('../dist/src/payments/' + p) + if (p === 'embed') { + fn = payment.p2data + } else { + fn = payment[p] + } const fixtures = require('./fixtures/' + p) fixtures.valid.forEach(function (f, i) { From 4cddc83016af9fbbaf677a72b09bfe97e7756e19 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 00:53:54 +0900 Subject: [PATCH 185/568] noImplicitAny is now true --- package.json | 2 +- src/address.ts | 6 +++--- src/block.ts | 2 +- src/ecpair.ts | 14 +++++++------- src/index.ts | 2 +- src/payments/p2pkh.ts | 7 +------ src/payments/p2sh.ts | 7 +------ src/script.ts | 6 +++--- src/script_number.ts | 2 +- src/script_signature.ts | 2 +- src/transaction.ts | 32 ++++++++++++++++---------------- src/transaction_builder.ts | 10 +++++----- src/types.ts | 16 ++++++++++++++-- tsconfig.json | 2 +- 14 files changed, 56 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 2583afc..55454bc 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "scripts": { "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", - "coverage": "nyc --check-coverage --branches 80 --functions 80 mocha", + "coverage": "nyc --check-coverage --branches 80 --functions 80 --lines 80 mocha", "integration": "npm run build && mocha --timeout 50000 test/integration/", "standard": "standard", "test": "npm run build && npm run standard && npm run coverage", diff --git a/src/address.ts b/src/address.ts index e657d24..f273b43 100644 --- a/src/address.ts +++ b/src/address.ts @@ -1,13 +1,13 @@ +import * as Networks from './networks' +import { Network } from './networks' +import * as types from './types' const Buffer = require('safe-buffer').Buffer const bech32 = require('bech32') const bs58check = require('bs58check') const bscript = require('./script') const networks = require('./networks') const typeforce = require('typeforce') -const types = require('./types') const payments = require('./payments') -import * as Networks from './networks' -import { Network } from './networks' export type Base58CheckResult = { hash: Buffer; diff --git a/src/block.ts b/src/block.ts index 92c4fb1..03ff901 100644 --- a/src/block.ts +++ b/src/block.ts @@ -1,9 +1,9 @@ import { Transaction } from './transaction' +import * as types from './types' const Buffer = require('safe-buffer').Buffer const bcrypto = require('./crypto') const fastMerkleRoot = require('merkle-lib/fastRoot') const typeforce = require('typeforce') -const types = require('./types') const varuint = require('varuint-bitcoin') const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions') diff --git a/src/ecpair.ts b/src/ecpair.ts index eceb2a1..cb835d1 100644 --- a/src/ecpair.ts +++ b/src/ecpair.ts @@ -1,9 +1,9 @@ import { Network } from './networks' import * as NETWORKS from './networks' +import * as types from './types' const ecc = require('tiny-secp256k1') const randomBytes = require('randombytes') const typeforce = require('typeforce') -const types = require('./types') const wif = require('wif') const isOptions = typeforce.maybe(typeforce.compile({ @@ -14,7 +14,7 @@ const isOptions = typeforce.maybe(typeforce.compile({ interface ECPairOptions { compressed?: boolean network?: Network - rng?(Buffer): Buffer + rng?(arg0: Buffer): Buffer } export interface ECPairInterface { @@ -75,19 +75,19 @@ function fromPrivateKey (buffer: Buffer, options: ECPairOptions): ECPair { return new ECPair(buffer, null, options) } -function fromPublicKey (buffer, options): ECPair { +function fromPublicKey (buffer: Buffer, options: ECPairOptions): ECPair { typeforce(ecc.isPoint, buffer) typeforce(isOptions, options) return new ECPair(null, buffer, options) } -function fromWIF (string, network): ECPair { +function fromWIF (string: string, network: Network | Array<Network>): ECPair { const decoded = wif.decode(string) const version = decoded.version // list of networks? if (types.Array(network)) { - network = network.filter(function (x) { + network = (<Array<Network>>network).filter(function (x: Network) { return version === x.wif }).pop() @@ -97,12 +97,12 @@ function fromWIF (string, network): ECPair { } else { network = network || NETWORKS.bitcoin - if (version !== network.wif) throw new Error('Invalid network version') + if (version !== (<Network>network).wif) throw new Error('Invalid network version') } return fromPrivateKey(decoded.privateKey, { compressed: decoded.compressed, - network: network + network: <Network>network }) } diff --git a/src/index.ts b/src/index.ts index 8019cb7..d93f587 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,11 @@ const opcodes = require('bitcoin-ops') +const bip32 = require('bip32') import { Block } from './block' import * as ECPair from './ecpair' import { Transaction } from './transaction' import { TransactionBuilder } from './transaction_builder' import * as address from './address' -import * as bip32 from 'bip32' import * as crypto from './crypto' import * as networks from './networks' import * as payments from './payments' diff --git a/src/payments/p2pkh.ts b/src/payments/p2pkh.ts index 4324c2a..e32f933 100644 --- a/src/payments/p2pkh.ts +++ b/src/payments/p2pkh.ts @@ -41,12 +41,7 @@ export function p2pkh (a: Payment, opts: PaymentOpts): Payment { const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) const network = a.network || BITCOIN_NETWORK - const o = { - network, - hash: undefined, - pubkey: undefined, - input: undefined, - } + const o: Payment = { network } lazy.prop(o, 'address', function () { if (!o.hash) return diff --git a/src/payments/p2sh.ts b/src/payments/p2sh.ts index efd453a..2219f33 100644 --- a/src/payments/p2sh.ts +++ b/src/payments/p2sh.ts @@ -51,12 +51,7 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK } - const o = { - network, - hash: undefined, - redeem: undefined, - input: undefined, - } + const o: Payment = { network } const _address = lazy.value(function () { const payload = bs58check.decode(a.address) diff --git a/src/script.ts b/src/script.ts index 98b018a..88bc46e 100644 --- a/src/script.ts +++ b/src/script.ts @@ -1,9 +1,9 @@ +import * as types from './types' const Buffer = require('safe-buffer').Buffer const bip66 = require('bip66') const ecc = require('tiny-secp256k1') const pushdata = require('pushdata-bitcoin') const typeforce = require('typeforce') -const types = require('./types') const scriptNumber = require('./script_number') const OPS = require('bitcoin-ops') @@ -100,7 +100,7 @@ export function decompile (buffer: Buffer | Array<number | Buffer>): Array<numbe typeforce(types.Buffer, buffer) - const chunks = [] + const chunks: Array<number | Buffer> = [] let i = 0 while (i < buffer.length) { @@ -123,7 +123,7 @@ export function decompile (buffer: Buffer | Array<number | Buffer>): Array<numbe // decompile minimally const op = asMinimalOP(data) if (op !== undefined) { - chunks.push(op) + chunks.push(<number>op) } else { chunks.push(data) } diff --git a/src/script_number.ts b/src/script_number.ts index 8178c95..0ff4a7e 100644 --- a/src/script_number.ts +++ b/src/script_number.ts @@ -32,7 +32,7 @@ export function decode (buffer: Buffer, maxLength?: number, minimal?: boolean): return result } -function scriptNumSize (i) { +function scriptNumSize (i: number): number { return i > 0x7fffffff ? 5 : i > 0x7fffff ? 4 : i > 0x7fff ? 3 diff --git a/src/script_signature.ts b/src/script_signature.ts index fefd09d..c24e88a 100644 --- a/src/script_signature.ts +++ b/src/script_signature.ts @@ -1,7 +1,7 @@ +import * as types from './types' const bip66 = require('bip66') const Buffer = require('safe-buffer').Buffer const typeforce = require('typeforce') -const types = require('./types') const ZERO = Buffer.alloc(1, 0) function toDER (x: Buffer): Buffer { diff --git a/src/transaction.ts b/src/transaction.ts index 88e4280..91956c5 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -1,10 +1,10 @@ import * as bcrypto from './crypto' +import * as bscript from './script' +import * as types from './types' const Buffer = require('safe-buffer').Buffer -const bscript = require('./script') const bufferutils = require('./bufferutils') const opcodes = require('bitcoin-ops') const typeforce = require('typeforce') -const types = require('./types') const varuint = require('varuint-bitcoin') function varSliceSize (someScript: Buffer): number { @@ -73,7 +73,7 @@ export class Transaction { this.ins = [] this.outs = [] } - + static fromBuffer (buffer: Buffer, __noStrict: boolean): Transaction { let offset: number = 0 @@ -294,7 +294,7 @@ export class Transaction { // ignore OP_CODESEPARATOR const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter((x) => { - return x !== opcodes.OP_CODESEPARATOR + return x !== <number>opcodes.OP_CODESEPARATOR })) const txTmp = this.clone() @@ -465,46 +465,46 @@ export class Transaction { return Buffer.from(this.getHash(false).reverse()).toString('hex') } - toBuffer (buffer: Buffer | void, initialOffset: number | void): Buffer { + toBuffer (buffer?: Buffer, initialOffset?: number): Buffer { return this.__toBuffer(buffer, initialOffset, true) } - __toBuffer (buffer: Buffer | void, initialOffset: number | void, __allowWitness: boolean | void): Buffer { + __toBuffer (buffer?: Buffer, initialOffset?: number, __allowWitness?: boolean): Buffer { if (!buffer) buffer = <Buffer> Buffer.allocUnsafe(this.__byteLength((<boolean>__allowWitness))) let offset = initialOffset || 0 - function writeSlice (slice) { + function writeSlice (slice: Buffer): void { offset += slice.copy(buffer, offset) } - function writeUInt8 (i) { + function writeUInt8 (i: number) { offset = (<Buffer>buffer).writeUInt8(i, offset) } - function writeUInt32 (i) { + function writeUInt32 (i: number) { offset = (<Buffer>buffer).writeUInt32LE(i, offset) } - function writeInt32 (i) { + function writeInt32 (i: number) { offset = (<Buffer>buffer).writeInt32LE(i, offset) } - function writeUInt64 (i) { + function writeUInt64 (i: number) { offset = bufferutils.writeUInt64LE(buffer, i, offset) } - function writeVarInt (i) { + function writeVarInt (i: number) { varuint.encode(i, buffer, offset) offset += varuint.encode.bytes } - function writeVarSlice (slice) { + function writeVarSlice (slice: Buffer) { writeVarInt(slice.length) writeSlice(slice) } - function writeVector (vector) { + function writeVector (vector: Array<Buffer>) { writeVarInt(vector.length) vector.forEach(writeVarSlice) } @@ -555,13 +555,13 @@ export class Transaction { return this.toBuffer(undefined, undefined).toString('hex') } - setInputScript (index, scriptSig) { + setInputScript (index: number, scriptSig: Buffer) { typeforce(types.tuple(types.Number, types.Buffer), arguments) this.ins[index].script = scriptSig } - setWitness (index, witness) { + setWitness (index: number, witness: Array<Buffer>) { typeforce(types.tuple(types.Number, [types.Buffer]), arguments) this.ins[index].witness = witness diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index 01717ab..e4c9c09 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -2,6 +2,7 @@ import { Network } from './networks' import * as networks from './networks' import { Transaction, Output } from './transaction' import { ECPairInterface } from './ecpair' +import * as types from './types' const Buffer = require('safe-buffer').Buffer const baddress = require('./address') const bcrypto = require('./crypto') @@ -9,7 +10,6 @@ const bscript = require('./script') const ops = require('bitcoin-ops') const payments = require('./payments') const typeforce = require('typeforce') -const types = require('./types') const classify = require('./classify') const SCRIPT_TYPES = classify.types @@ -53,7 +53,7 @@ function txIsTransaction(tx: Buffer | string | Transaction): tx is Transaction { export class TransactionBuilder { network: Network maximumFeeRate: number - private __prevTxSet: Object + private __prevTxSet: { [index: string]: boolean } private __inputs: Array<TxbInput> private __tx: Transaction @@ -192,7 +192,7 @@ export class TransactionBuilder { return vin } - addOutput (scriptPubKey: string | Buffer, value): number { + addOutput (scriptPubKey: string | Buffer, value: number): number { if (!this.__canModifyOutputs()) { throw new Error('No, this would invalidate signatures') } @@ -282,7 +282,7 @@ export class TransactionBuilder { } // ready to sign - let signatureHash + let signatureHash: Buffer if (input.hasWitness) { signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType) } else { @@ -573,7 +573,7 @@ function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { return { type, pubkeys: p2ms.pubkeys, - signatures: p2ms.pubkeys.map(() => undefined), + signatures: p2ms.pubkeys.map((): undefined => undefined), maxSignatures: p2ms.m } } diff --git a/src/types.ts b/src/types.ts index ad603c5..8eacbdc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -33,5 +33,17 @@ export const Network = typeforce.compile({ export const Buffer256bit = typeforce.BufferN(32) export const Hash160bit = typeforce.BufferN(20) export const Hash256bit = typeforce.BufferN(32) -export * from 'typeforce' -export { Number, Array } from 'typeforce' +export const Number = typeforce.Number +export const Array = typeforce.Array +export const Boolean = typeforce.Boolean +export const String = typeforce.String +export const Buffer = typeforce.Buffer +export const Hex = typeforce.Hex +export const maybe = typeforce.maybe +export const tuple = typeforce.tuple +export const UInt8 = typeforce.UInt8 +export const UInt32 = typeforce.UInt32 +export const Function = typeforce.Function +export const BufferN = typeforce.BufferN +export const Null = typeforce.Null +export const oneOf = typeforce.oneOf diff --git a/tsconfig.json b/tsconfig.json index 8803b83..f8bd5e2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ ], "allowJs": false, "strict": false, - "noImplicitAny": false, + "noImplicitAny": true, "strictNullChecks": false, "strictFunctionTypes": true, "strictBindCallApply": true, From fdf0006fdeb54f6bcd6d4e244b4ba5eb354ace81 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 01:55:07 +0900 Subject: [PATCH 186/568] Add strictNullChecks --- src/address.ts | 4 +- src/ecpair.ts | 16 ++++---- src/payments/embed.ts | 8 ++-- src/payments/lazy.ts | 2 +- src/payments/p2ms.ts | 32 ++++++++-------- src/payments/p2pk.ts | 10 ++--- src/payments/p2pkh.ts | 18 ++++----- src/payments/p2sh.ts | 21 ++++++----- src/payments/p2wpkh.ts | 12 +++--- src/payments/p2wsh.ts | 19 +++++----- src/script.ts | 6 +-- src/templates/multisig/input.ts | 2 +- src/templates/multisig/output.ts | 2 +- src/templates/pubkey/input.ts | 2 +- src/templates/pubkey/output.ts | 2 +- src/templates/pubkeyhash/input.ts | 2 +- src/templates/scripthash/input.ts | 4 +- src/templates/witnesscommitment/output.ts | 2 +- src/templates/witnesspubkeyhash/input.ts | 2 +- src/transaction.ts | 8 ++-- src/transaction_builder.ts | 46 +++++++++++------------ tsconfig.json | 6 +-- 22 files changed, 114 insertions(+), 112 deletions(-) diff --git a/src/address.ts b/src/address.ts index f273b43..c9ffeeb 100644 --- a/src/address.ts +++ b/src/address.ts @@ -75,8 +75,8 @@ export function fromOutputScript (output: Buffer, network: Network): string { // export function toOutputScript (address: string, network: Network): Buffer { network = network || networks.bitcoin - let decodeBase58: Base58CheckResult - let decodeBech32: Bech32Result + let decodeBase58: Base58CheckResult | undefined = undefined + let decodeBech32: Bech32Result | undefined = undefined try { decodeBase58 = fromBase58Check(address) } catch (e) {} diff --git a/src/ecpair.ts b/src/ecpair.ts index cb835d1..a7426ae 100644 --- a/src/ecpair.ts +++ b/src/ecpair.ts @@ -20,8 +20,8 @@ interface ECPairOptions { export interface ECPairInterface { compressed: boolean network: Network - privateKey: Buffer - publicKey: Buffer + privateKey: Buffer | null + publicKey: Buffer | null toWIF(): string sign(hash: Buffer): Buffer verify(hash: Buffer, signature: Buffer): Buffer @@ -31,9 +31,9 @@ export interface ECPairInterface { class ECPair implements ECPairInterface { compressed: boolean network: Network - private __d: Buffer - private __Q: Buffer - constructor (d: Buffer | void, Q: Buffer | void, options: ECPairOptions) { + private __d: Buffer | null + private __Q: Buffer | null + constructor (d: Buffer | null, Q: Buffer | null, options: ECPairOptions) { if (options === undefined) options = {} this.compressed = options.compressed === undefined ? true : options.compressed this.network = options.network || NETWORKS.bitcoin @@ -43,11 +43,11 @@ class ECPair implements ECPairInterface { if (Q) this.__Q = ecc.pointCompress(Q, this.compressed) } - get privateKey (): Buffer { + get privateKey (): Buffer | null { return this.__d } - get publicKey (): Buffer { + get publicKey (): Buffer | null { if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed) return this.__Q } @@ -87,7 +87,7 @@ function fromWIF (string: string, network: Network | Array<Network>): ECPair { // list of networks? if (types.Array(network)) { - network = (<Array<Network>>network).filter(function (x: Network) { + network = <Network>(<Array<Network>>network).filter(function (x: Network) { return version === x.wif }).pop() diff --git a/src/payments/embed.ts b/src/payments/embed.ts index a28747f..2526fe0 100644 --- a/src/payments/embed.ts +++ b/src/payments/embed.ts @@ -36,17 +36,17 @@ export function p2data (a: Payment, opts: PaymentOpts): Payment { }) lazy.prop(o, 'data', function () { if (!a.output) return - return bscript.decompile(a.output).slice(1) + return (<Array<Buffer | number>>bscript.decompile(a.output)).slice(1) }) // extended validation if (opts.validate) { if (a.output) { const chunks = bscript.decompile(a.output) - if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') - if (!chunks.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') + if ((<Array<Buffer | number>>chunks)[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') + if (!(<Array<Buffer | number>>chunks).slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') - if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch') + if (a.data && !stacksEqual(a.data, <Array<Buffer>>o.data)) throw new TypeError('Data mismatch') } } diff --git a/src/payments/lazy.ts b/src/payments/lazy.ts index 6a4cbe3..1d4af68 100644 --- a/src/payments/lazy.ts +++ b/src/payments/lazy.ts @@ -20,7 +20,7 @@ export function prop (object: Object, name: string, f: ()=>any): void { export function value <T>(f: ()=>T): ()=>T { let value: T - return function () { + return function (): T { if (value !== undefined) return value value = f() return value diff --git a/src/payments/p2ms.ts b/src/payments/p2ms.ts index 2142ff1..b8a05b3 100644 --- a/src/payments/p2ms.ts +++ b/src/payments/p2ms.ts @@ -28,7 +28,7 @@ export function p2ms (a: Payment, opts: PaymentOpts): Payment { opts = Object.assign({ validate: true }, opts || {}) function isAcceptableSignature (x: Buffer | number) { - return bscript.isCanonicalScriptSignature(<Buffer>x) || (opts.allowIncomplete && (<number>x === OPS.OP_0)) + return bscript.isCanonicalScriptSignature(<Buffer>x) || (opts.allowIncomplete && (<number>x === OPS.OP_0)) !== undefined } typef({ @@ -45,12 +45,12 @@ export function p2ms (a: Payment, opts: PaymentOpts): Payment { const network = a.network || BITCOIN_NETWORK const o: Payment = { network } - let chunks: Array<Buffer | number> + let chunks: Array<Buffer | number> = [] let decoded = false function decode (output: Buffer | Array<Buffer | number>): void { if (decoded) return decoded = true - chunks = bscript.decompile(output) + chunks = <Array<Buffer | number>>bscript.decompile(output) o.m = <number>chunks[0] - OP_INT_BASE o.n = <number>chunks[chunks.length - 2] - OP_INT_BASE o.pubkeys = <Array<Buffer>>chunks.slice(1, -2) @@ -60,7 +60,7 @@ export function p2ms (a: Payment, opts: PaymentOpts): Payment { if (!a.m) return if (!o.n) return if (!a.pubkeys) return - return bscript.compile([].concat( + return bscript.compile((<Array<Buffer | number>>[]).concat( OP_INT_BASE + a.m, a.pubkeys, OP_INT_BASE + o.n, @@ -83,7 +83,7 @@ export function p2ms (a: Payment, opts: PaymentOpts): Payment { }) lazy.prop(o, 'signatures', function () { if (!a.input) return - return bscript.decompile(a.input).slice(1) + return (<Array<Buffer | number>>bscript.decompile(a.input)).slice(1) }) lazy.prop(o, 'input', function () { if (!a.signatures) return @@ -103,35 +103,35 @@ export function p2ms (a: Payment, opts: PaymentOpts): Payment { if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid') if ( - o.m <= 0 || - o.n > 16 || - o.m > o.n || + <number>(<Payment>o).m <= 0 || + <number>(<Payment>o).n > 16 || + <number>(<Payment>o).m > <number>(<Payment>o).n || o.n !== chunks.length - 3) throw new TypeError('Output is invalid') - if (!o.pubkeys.every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid') + if (!(<Array<Buffer>>o.pubkeys).every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid') if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch') if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch') - if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys)) throw new TypeError('Pubkeys mismatch') + if (a.pubkeys && !stacksEqual(a.pubkeys, (<Array<Buffer>>o.pubkeys))) throw new TypeError('Pubkeys mismatch') } if (a.pubkeys) { if (a.n !== undefined && a.n !== a.pubkeys.length) throw new TypeError('Pubkey count mismatch') o.n = a.pubkeys.length - if (o.n < o.m) throw new TypeError('Pubkey count cannot be less than m') + if (o.n < <number>(<Payment>o).m) throw new TypeError('Pubkey count cannot be less than m') } if (a.signatures) { - if (a.signatures.length < o.m) throw new TypeError('Not enough signatures provided') - if (a.signatures.length > o.m) throw new TypeError('Too many signatures provided') + if (a.signatures.length < <number>(<Payment>o).m) throw new TypeError('Not enough signatures provided') + if (a.signatures.length > <number>(<Payment>o).m) throw new TypeError('Too many signatures provided') } if (a.input) { if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') - if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') + if ((<Array<Buffer>>o.signatures).length === 0 || !(<Array<Buffer>>o.signatures).every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') - if (a.signatures && !stacksEqual(a.signatures, o.signatures)) throw new TypeError('Signature mismatch') - if (a.m !== undefined && a.m !== a.signatures.length) throw new TypeError('Signature count mismatch') + if (a.signatures && !stacksEqual(a.signatures, (<Array<Buffer>>o.signatures))) throw new TypeError('Signature mismatch') + if (a.m !== undefined && a.m !== (<Array<Buffer>>a.signatures).length) throw new TypeError('Signature count mismatch') } } diff --git a/src/payments/p2pk.ts b/src/payments/p2pk.ts index a1ac5e8..45b3577 100644 --- a/src/payments/p2pk.ts +++ b/src/payments/p2pk.ts @@ -27,7 +27,7 @@ export function p2pk (a: Payment, opts: PaymentOpts): Payment { input: typef.maybe(typef.Buffer) }, a) - const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) + const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(<Buffer>a.input) }) const network = a.network || BITCOIN_NETWORK const o: Payment = { network } @@ -45,7 +45,7 @@ export function p2pk (a: Payment, opts: PaymentOpts): Payment { }) lazy.prop(o, 'signature', function () { if (!a.input) return - return _chunks()[0] + return <Buffer>_chunks()[0] }) lazy.prop(o, 'input', function () { if (!a.signature) return @@ -61,16 +61,16 @@ export function p2pk (a: Payment, opts: PaymentOpts): Payment { if (a.output) { if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') if (!ecc.isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid') - if (a.pubkey && !a.pubkey.equals(o.pubkey)) throw new TypeError('Pubkey mismatch') + if (a.pubkey && !a.pubkey.equals(<Buffer>o.pubkey)) throw new TypeError('Pubkey mismatch') } if (a.signature) { - if (a.input && !a.input.equals(o.input)) throw new TypeError('Signature mismatch') + if (a.input && !a.input.equals(<Buffer>o.input)) throw new TypeError('Signature mismatch') } if (a.input) { if (_chunks().length !== 1) throw new TypeError('Input is invalid') - if (!bscript.isCanonicalScriptSignature(o.signature)) throw new TypeError('Input has invalid signature') + if (!bscript.isCanonicalScriptSignature(<Buffer>o.signature)) throw new TypeError('Input has invalid signature') } } diff --git a/src/payments/p2pkh.ts b/src/payments/p2pkh.ts index e32f933..f2054b0 100644 --- a/src/payments/p2pkh.ts +++ b/src/payments/p2pkh.ts @@ -38,7 +38,7 @@ export function p2pkh (a: Payment, opts: PaymentOpts): Payment { const hash = payload.slice(1) return { version, hash } }) - const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) + const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(<Buffer>a.input) }) const network = a.network || BITCOIN_NETWORK const o: Payment = { network } @@ -54,7 +54,7 @@ export function p2pkh (a: Payment, opts: PaymentOpts): Payment { lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(3, 23) if (a.address) return _address().hash - if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey) + if (a.pubkey || o.pubkey) return bcrypto.hash160(<Buffer>a.pubkey || <Buffer>o.pubkey) }) lazy.prop(o, 'output', function () { if (!o.hash) return @@ -68,11 +68,11 @@ export function p2pkh (a: Payment, opts: PaymentOpts): Payment { }) lazy.prop(o, 'pubkey', function () { if (!a.input) return - return _chunks()[1] + return <Buffer>_chunks()[1] }) lazy.prop(o, 'signature', function () { if (!a.input) return - return _chunks()[0] + return <Buffer>_chunks()[0] }) lazy.prop(o, 'input', function () { if (!a.pubkey) return @@ -86,7 +86,7 @@ export function p2pkh (a: Payment, opts: PaymentOpts): Payment { // extended validation if (opts.validate) { - let hash: Buffer + let hash: Buffer = Buffer.from([]) if (a.address) { if (_address().version !== network.pubKeyHash) throw new TypeError('Invalid version or Network mismatch') if (_address().hash.length !== 20) throw new TypeError('Invalid address') @@ -94,7 +94,7 @@ export function p2pkh (a: Payment, opts: PaymentOpts): Payment { } if (a.hash) { - if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') else hash = a.hash } @@ -108,13 +108,13 @@ export function p2pkh (a: Payment, opts: PaymentOpts): Payment { a.output[24] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') const hash2 = a.output.slice(3, 23) - if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') else hash = hash2 } if (a.pubkey) { const pkh = bcrypto.hash160(a.pubkey) - if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch') else hash = pkh } @@ -128,7 +128,7 @@ export function p2pkh (a: Payment, opts: PaymentOpts): Payment { if (a.pubkey && !a.pubkey.equals(<Buffer>chunks[1])) throw new TypeError('Pubkey mismatch') const pkh = bcrypto.hash160(<Buffer>chunks[1]) - if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch') } } diff --git a/src/payments/p2sh.ts b/src/payments/p2sh.ts index 2219f33..6facde9 100644 --- a/src/payments/p2sh.ts +++ b/src/payments/p2sh.ts @@ -1,4 +1,5 @@ import { Payment, PaymentOpts } from './index' +import { Network } from '../networks' import * as bscript from '../script' import * as bcrypto from '../crypto' import * as lazy from './lazy' @@ -59,7 +60,7 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { const hash = payload.slice(1) return { version, hash } }) - const _chunks = lazy.value(function () { return bscript.decompile(a.input) }) + const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(<Buffer>a.input) }) const _redeem = lazy.value(function (): Payment { const chunks = _chunks() return { @@ -75,7 +76,7 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { if (!o.hash) return const payload = Buffer.allocUnsafe(21) - payload.writeUInt8(network.scriptHash, 0) + payload.writeUInt8((<Network>o.network).scriptHash, 0) o.hash.copy(payload, 1) return bs58check.encode(payload) }) @@ -101,8 +102,8 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { }) lazy.prop(o, 'input', function () { if (!a.redeem || !a.redeem.input || !a.redeem.output) return - return bscript.compile([].concat( - bscript.decompile(a.redeem.input), + return bscript.compile((<Array<Buffer | number>>[]).concat( + <Array<Buffer | number>>bscript.decompile(a.redeem.input), a.redeem.output )) }) @@ -112,7 +113,7 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { }) if (opts.validate) { - let hash: Buffer + let hash: Buffer = Buffer.from([]) if (a.address) { if (_address().version !== network.scriptHash) throw new TypeError('Invalid version or Network mismatch') if (_address().hash.length !== 20) throw new TypeError('Invalid address') @@ -120,7 +121,7 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { } if (a.hash) { - if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') else hash = a.hash } @@ -132,7 +133,7 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { a.output[22] !== OPS.OP_EQUAL) throw new TypeError('Output is invalid') const hash2 = a.output.slice(2, 22) - if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') else hash = hash2 } @@ -145,7 +146,7 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { // match hash against other sources const hash2 = bcrypto.hash160(redeem.output) - if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') else hash = hash2 } @@ -155,7 +156,7 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { if (!hasInput && !hasWitness) throw new TypeError('Empty input') if (hasInput && hasWitness) throw new TypeError('Input and witness provided') if (hasInput) { - const richunks = bscript.decompile(redeem.input) + const richunks = <Array<Buffer | number>>bscript.decompile(redeem.input) if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig') } } @@ -174,7 +175,7 @@ export function p2sh (a: Payment, opts: PaymentOpts): Payment { if (a.input) { const redeem = _redeem() if (a.redeem.output && !a.redeem.output.equals(<Buffer>redeem.output)) throw new TypeError('Redeem.output mismatch') - if (a.redeem.input && !a.redeem.input.equals(redeem.input)) throw new TypeError('Redeem.input mismatch') + if (a.redeem.input && !a.redeem.input.equals(<Buffer>redeem.input)) throw new TypeError('Redeem.input mismatch') } checkRedeem(a.redeem) diff --git a/src/payments/p2wpkh.ts b/src/payments/p2wpkh.ts index a25d037..7089246 100644 --- a/src/payments/p2wpkh.ts +++ b/src/payments/p2wpkh.ts @@ -59,7 +59,7 @@ export function p2wpkh (a: Payment, opts: PaymentOpts): Payment { lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(2, 22) if (a.address) return _address().data - if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey) + if (a.pubkey || o.pubkey) return bcrypto.hash160(<Buffer>a.pubkey || <Buffer>o.pubkey) }) lazy.prop(o, 'output', function () { if (!o.hash) return @@ -89,7 +89,7 @@ export function p2wpkh (a: Payment, opts: PaymentOpts): Payment { // extended validation if (opts.validate) { - let hash: Buffer + let hash: Buffer = Buffer.from([]) if (a.address) { if (network && network.bech32 !== _address().prefix) throw new TypeError('Invalid prefix or Network mismatch') if (_address().version !== 0x00) throw new TypeError('Invalid address version') @@ -98,7 +98,7 @@ export function p2wpkh (a: Payment, opts: PaymentOpts): Payment { } if (a.hash) { - if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') else hash = a.hash } @@ -107,13 +107,13 @@ export function p2wpkh (a: Payment, opts: PaymentOpts): Payment { a.output.length !== 22 || a.output[0] !== OPS.OP_0 || a.output[1] !== 0x14) throw new TypeError('Output is invalid') - if (hash && !hash.equals(a.output.slice(2))) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(a.output.slice(2))) throw new TypeError('Hash mismatch') else hash = a.output.slice(2) } if (a.pubkey) { const pkh = bcrypto.hash160(a.pubkey) - if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch') else hash = pkh } @@ -126,7 +126,7 @@ export function p2wpkh (a: Payment, opts: PaymentOpts): Payment { if (a.pubkey && !a.pubkey.equals(a.witness[1])) throw new TypeError('Pubkey mismatch') const pkh = bcrypto.hash160(a.witness[1]) - if (hash && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch') } } diff --git a/src/payments/p2wsh.ts b/src/payments/p2wsh.ts index d94d446..c70cfb6 100644 --- a/src/payments/p2wsh.ts +++ b/src/payments/p2wsh.ts @@ -1,4 +1,5 @@ import { Payment, PaymentOpts } from './index' +import { Network } from '../networks' import * as bscript from '../script' import * as bcrypto from '../crypto' import * as lazy from './lazy' @@ -58,7 +59,7 @@ export function p2wsh (a: Payment, opts: PaymentOpts): Payment { data: Buffer.from(data) } }) - const _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input) }) + const _rchunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(<Buffer>(<Payment>a.redeem).input) }) let network = a.network if (!network) { @@ -71,7 +72,7 @@ export function p2wsh (a: Payment, opts: PaymentOpts): Payment { if (!o.hash) return const words = bech32.toWords(o.hash) words.unshift(0x00) - return bech32.encode(network.bech32, words) + return bech32.encode((<Network>network).bech32, words) }) lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(2) @@ -111,18 +112,18 @@ export function p2wsh (a: Payment, opts: PaymentOpts): Payment { // assign, and blank the existing input o.redeem = Object.assign({ witness: stack }, a.redeem) o.redeem.input = EMPTY_BUFFER - return [].concat(stack, a.redeem.output) + return (<Array<Buffer>>[]).concat(stack, a.redeem.output) } if (!a.redeem) return if (!a.redeem.output) return if (!a.redeem.witness) return - return [].concat(a.redeem.witness, a.redeem.output) + return (<Array<Buffer>>[]).concat(a.redeem.witness, a.redeem.output) }) // extended validation if (opts.validate) { - let hash: Buffer + let hash: Buffer = Buffer.from([]) if (a.address) { if (_address().prefix !== network.bech32) throw new TypeError('Invalid prefix or Network mismatch') if (_address().version !== 0x00) throw new TypeError('Invalid address version') @@ -131,7 +132,7 @@ export function p2wsh (a: Payment, opts: PaymentOpts): Payment { } if (a.hash) { - if (hash && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') else hash = a.hash } @@ -141,7 +142,7 @@ export function p2wsh (a: Payment, opts: PaymentOpts): Payment { a.output[0] !== OPS.OP_0 || a.output[1] !== 0x20) throw new TypeError('Output is invalid') const hash2 = a.output.slice(2) - if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') else hash = hash2 } @@ -158,11 +159,11 @@ export function p2wsh (a: Payment, opts: PaymentOpts): Payment { // is the redeem output non-empty? if (a.redeem.output) { - if (bscript.decompile(a.redeem.output).length === 0) throw new TypeError('Redeem.output is invalid') + if ((<Array<Buffer | number>>bscript.decompile(a.redeem.output)).length === 0) throw new TypeError('Redeem.output is invalid') // match hash against other sources const hash2 = bcrypto.sha256(a.redeem.output) - if (hash && !hash.equals(hash2)) throw new TypeError('Hash mismatch') + if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') else hash = hash2 } diff --git a/src/script.ts b/src/script.ts index 88bc46e..6d9bfc9 100644 --- a/src/script.ts +++ b/src/script.ts @@ -94,7 +94,7 @@ export function compile (chunks: Buffer | Array<number | Buffer>): Buffer { return buffer } -export function decompile (buffer: Buffer | Array<number | Buffer>): Array<number | Buffer> { +export function decompile (buffer: Buffer | Array<number | Buffer>): Array<number | Buffer> | null { // TODO: remove me if (chunksIsArray(buffer)) return buffer @@ -141,7 +141,7 @@ export function decompile (buffer: Buffer | Array<number | Buffer>): Array<numbe export function toASM (chunks: Buffer | Array<number | Buffer>): string { if (chunksIsBuffer(chunks)) { - chunks = decompile(chunks) + chunks = <Array<number | Buffer>>decompile(chunks) } return chunks.map(function (chunk) { @@ -171,7 +171,7 @@ export function fromASM (asm: string): Buffer { } export function toStack (chunks: Buffer | Array<number | Buffer>): Array<Buffer> { - chunks = decompile(chunks) + chunks = <Array<number | Buffer>>decompile(chunks) typeforce(isPushOnly, chunks) return chunks.map(function (op) { diff --git a/src/templates/multisig/input.ts b/src/templates/multisig/input.ts index 2060242..abf0982 100644 --- a/src/templates/multisig/input.ts +++ b/src/templates/multisig/input.ts @@ -8,7 +8,7 @@ function partialSignature (value: number | Buffer): boolean { } export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { - const chunks = bscript.decompile(script) + const chunks = <Array<number | Buffer>>bscript.decompile(script) if (chunks.length < 2) return false if (chunks[0] !== OPS.OP_0) return false diff --git a/src/templates/multisig/output.ts b/src/templates/multisig/output.ts index 50c701e..e4fa333 100644 --- a/src/templates/multisig/output.ts +++ b/src/templates/multisig/output.ts @@ -6,7 +6,7 @@ const OPS = require('bitcoin-ops') const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { - const chunks = bscript.decompile(script) + const chunks = <Array<number | Buffer>>bscript.decompile(script) if (chunks.length < 4) return false if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false diff --git a/src/templates/pubkey/input.ts b/src/templates/pubkey/input.ts index f4b8bf7..f9245e4 100644 --- a/src/templates/pubkey/input.ts +++ b/src/templates/pubkey/input.ts @@ -3,7 +3,7 @@ import * as bscript from '../../script' export function check (script: Buffer | Array<number | Buffer>): boolean { - const chunks = bscript.decompile(script) + const chunks = <Array<number | Buffer>>bscript.decompile(script) return chunks.length === 1 && bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) diff --git a/src/templates/pubkey/output.ts b/src/templates/pubkey/output.ts index 2728f68..f60cc05 100644 --- a/src/templates/pubkey/output.ts +++ b/src/templates/pubkey/output.ts @@ -4,7 +4,7 @@ import * as bscript from '../../script' const OPS = require('bitcoin-ops') export function check (script: Buffer | Array<number | Buffer>): boolean { - const chunks = bscript.decompile(script) + const chunks = <Array<number | Buffer>>bscript.decompile(script) return chunks.length === 2 && bscript.isCanonicalPubKey(<Buffer>chunks[0]) && diff --git a/src/templates/pubkeyhash/input.ts b/src/templates/pubkeyhash/input.ts index ffdc929..f27f809 100644 --- a/src/templates/pubkeyhash/input.ts +++ b/src/templates/pubkeyhash/input.ts @@ -3,7 +3,7 @@ import * as bscript from '../../script' export function check (script: Buffer | Array<number | Buffer>): boolean { - const chunks = bscript.decompile(script) + const chunks = <Array<number | Buffer>>bscript.decompile(script) return chunks.length === 2 && bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) && diff --git a/src/templates/scripthash/input.ts b/src/templates/scripthash/input.ts index 908a87b..93e7d58 100644 --- a/src/templates/scripthash/input.ts +++ b/src/templates/scripthash/input.ts @@ -10,13 +10,13 @@ import * as p2wsho from '../witnessscripthash/output' const Buffer = require('safe-buffer').Buffer export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { - const chunks = bscript.decompile(script) + const chunks = <Array<number | Buffer>>bscript.decompile(script) if (chunks.length < 1) return false const lastChunk = chunks[chunks.length - 1] if (!Buffer.isBuffer(lastChunk)) return false - const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))) + const scriptSigChunks = <Array<number | Buffer>>bscript.decompile(bscript.compile(chunks.slice(0, -1))) const redeemScriptChunks = bscript.decompile(<Buffer>lastChunk) // is redeemScript a valid script? diff --git a/src/templates/witnesscommitment/output.ts b/src/templates/witnesscommitment/output.ts index 1b3a182..d89b8e2 100644 --- a/src/templates/witnesscommitment/output.ts +++ b/src/templates/witnesscommitment/output.ts @@ -32,5 +32,5 @@ export function encode (commitment: Buffer): Buffer { export function decode (buffer: Buffer): Buffer { typeforce(check, buffer) - return (<Buffer>bscript.decompile(buffer)[1]).slice(4, 36) + return (<Buffer>(<Array<number | Buffer>>bscript.decompile(buffer))[1]).slice(4, 36) } diff --git a/src/templates/witnesspubkeyhash/input.ts b/src/templates/witnesspubkeyhash/input.ts index 36f0606..91b8357 100644 --- a/src/templates/witnesspubkeyhash/input.ts +++ b/src/templates/witnesspubkeyhash/input.ts @@ -7,7 +7,7 @@ function isCompressedCanonicalPubKey (pubKey: Buffer): boolean { } export function check (script: Buffer | Array<number | Buffer>): boolean { - const chunks = bscript.decompile(script) + const chunks = <Array<number | Buffer>>bscript.decompile(script) return chunks.length === 2 && bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) && diff --git a/src/transaction.ts b/src/transaction.ts index 91956c5..af17449 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -182,7 +182,7 @@ export class Transaction { return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) } - addInput (hash: Buffer, index: number, sequence: number, scriptSig: Buffer): number { + addInput (hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number { typeforce(types.tuple( types.Hash256bit, types.UInt32, @@ -199,7 +199,7 @@ export class Transaction { hash: hash, index: index, script: scriptSig || EMPTY_SCRIPT, - sequence: sequence, + sequence: <number>sequence, witness: EMPTY_WITNESS }) - 1) } @@ -293,7 +293,7 @@ export class Transaction { if (inIndex >= this.ins.length) return ONE // ignore OP_CODESEPARATOR - const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter((x) => { + const ourScript = bscript.compile((<Array<Buffer | number>>bscript.decompile(prevOutScript)).filter((x) => { return x !== <number>opcodes.OP_CODESEPARATOR })) @@ -475,7 +475,7 @@ export class Transaction { let offset = initialOffset || 0 function writeSlice (slice: Buffer): void { - offset += slice.copy(buffer, offset) + offset += slice.copy(<Buffer>buffer, offset) } function writeUInt8 (i: number) { diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index e4c9c09..41d524e 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -24,8 +24,8 @@ interface TxbInput { redeemScript?: Buffer redeemScriptType?: string prevOutType?: string - pubkeys?: Array<Buffer> - signatures?: Array<Buffer> + pubkeys?: Array<Buffer> | Array<undefined> + signatures?: Array<Buffer> | Array<Buffer | undefined> witness?: Array<Buffer> witnessScript?: Buffer witnessScriptType?: string @@ -38,7 +38,7 @@ interface TxbInput { interface TxbOutput { type: string pubkeys?: Array<Buffer> - signatures?: Array<Buffer> + signatures?: Array<Buffer> | Array<Buffer | undefined> maxSignatures?: number } @@ -57,7 +57,7 @@ export class TransactionBuilder { private __inputs: Array<TxbInput> private __tx: Transaction - constructor (network: Network, maximumFeeRate?: number) { + constructor (network?: Network, maximumFeeRate?: number) { this.__prevTxSet = {} this.network = network || networks.bitcoin @@ -125,7 +125,7 @@ export class TransactionBuilder { throw new Error('No, this would invalidate signatures') } - let value: number + let value: number | undefined = undefined // is it a hex string? if (txIsString(txHash)) { @@ -225,7 +225,7 @@ export class TransactionBuilder { this.__inputs.forEach((input, i) => { if (!input.prevOutType && !allowIncomplete) throw new Error('Transaction is not complete') - const result = build(input.prevOutType, input, allowIncomplete) + const result = build(<string>input.prevOutType, input, allowIncomplete) if (!result) { if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) throw new Error('Unknown input type') if (!allowIncomplete) throw new Error('Not enough information') @@ -263,7 +263,7 @@ export class TransactionBuilder { throw new Error('Inconsistent redeemScript') } - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey() + const ourPubKey = keyPair.publicKey || (<()=>Buffer>keyPair.getPublicKey)() if (!canSign(input)) { if (witnessValue !== undefined) { if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue') @@ -284,15 +284,15 @@ export class TransactionBuilder { // ready to sign let signatureHash: Buffer if (input.hasWitness) { - signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType) + signatureHash = this.__tx.hashForWitnessV0(vin, <Buffer>input.signScript, <number>input.value, hashType) } else { - signatureHash = this.__tx.hashForSignature(vin, input.signScript, hashType) + signatureHash = this.__tx.hashForSignature(vin, <Buffer>input.signScript, hashType) } // enforce in order signing of public keys - const signed = input.pubkeys.some((pubKey, i) => { + const signed = (<Array<Buffer>>input.pubkeys).some((pubKey, i) => { if (!ourPubKey.equals(pubKey)) return false - if (input.signatures[i]) throw new Error('Signature already exists') + if ((<Array<Buffer>>input.signatures)[i]) throw new Error('Signature already exists') // TODO: add tests if (ourPubKey.length !== 33 && input.hasWitness) { @@ -300,7 +300,7 @@ export class TransactionBuilder { } const signature = keyPair.sign(signatureHash) - input.signatures[i] = bscript.signature.encode(signature, hashType) + ;(<Array<Buffer>>input.signatures)[i] = bscript.signature.encode(signature, hashType) return true }) @@ -348,7 +348,7 @@ export class TransactionBuilder { return this.__inputs.every(input => { if (input.signatures === undefined) return true - return input.signatures.every(signature => { + return input.signatures.every(<(signature: Buffer | undefined)=>boolean>(signature => { if (!signature) return true const hashType = signatureHashType(signature) @@ -360,13 +360,13 @@ export class TransactionBuilder { // of more outputs return nInputs <= nOutputs } - }) + })) }) } private __overMaximumFees (bytes: number): boolean { // not all inputs will have .value defined - const incoming = this.__inputs.reduce((a, x) => a + (x.value >>> 0), 0) + const incoming = this.__inputs.reduce((a, x) => a + (<number>x.value >>> 0), 0) // but all outputs do, and if we have any input value // we can immediately determine if the outputs are too small @@ -493,13 +493,13 @@ function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: str // could be done in expandInput, but requires the original Transaction for hashForSignature function fixMultisigOrder (input: TxbInput, transaction: Transaction, vin: number): void { if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) return - if (input.pubkeys.length === input.signatures.length) return + if ((<Array<Buffer>>input.pubkeys).length === (<Array<Buffer>>input.signatures).length) return - const unmatched = input.signatures.concat() + const unmatched = (<Array<Buffer | undefined>>input.signatures).concat() - input.signatures = input.pubkeys.map(pubKey => { + input.signatures = <Array<Buffer | undefined>>(<Array<Buffer>>input.pubkeys).map(pubKey => { const keyPair = ECPair.fromPublicKey(pubKey) - let match + let match: Buffer | undefined // check for a signature unmatched.some((signature, i) => { @@ -508,7 +508,7 @@ function fixMultisigOrder (input: TxbInput, transaction: Transaction, vin: numbe // TODO: avoid O(n) hashForSignature const parsed = bscript.signature.decode(signature) - const hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType) + const hash = transaction.hashForSignature(vin, (<Buffer>input.redeemScript), parsed.hashType) // skip if signature does not match pubKey if (!keyPair.verify(hash, parsed.signature)) return false @@ -740,7 +740,7 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, } } -function build (type: string, input: TxbInput, allowIncomplete: boolean): any { //TODO payment type +function build (type: string, input: TxbInput, allowIncomplete?: boolean): any { //TODO payment type const pubkeys = input.pubkeys || [] let signatures = input.signatures || [] @@ -777,7 +777,7 @@ function build (type: string, input: TxbInput, allowIncomplete: boolean): any { return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate }) } case SCRIPT_TYPES.P2SH: { - const redeem = build(input.redeemScriptType, input, allowIncomplete) + const redeem = build(<string>input.redeemScriptType, input, allowIncomplete) if (!redeem) return return payments.p2sh({ @@ -789,7 +789,7 @@ function build (type: string, input: TxbInput, allowIncomplete: boolean): any { }) } case SCRIPT_TYPES.P2WSH: { - const redeem = build(input.witnessScriptType, input, allowIncomplete) + const redeem = build(<string>input.witnessScriptType, input, allowIncomplete) if (!redeem) return return payments.p2wsh({ diff --git a/tsconfig.json b/tsconfig.json index f8bd5e2..cfc286a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,12 +10,12 @@ "allowJs": false, "strict": false, "noImplicitAny": true, - "strictNullChecks": false, + "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": false, - "noImplicitThis": false, - "alwaysStrict": false, + "noImplicitThis": true, + "alwaysStrict": true, "esModuleInterop": true }, "include": [ From 9955c3c082b1c34d068167cfbfe5479fdc62f60b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 10:35:57 +0900 Subject: [PATCH 187/568] Add strictPropertyInitialization --- src/block.ts | 22 +++++++++++++--------- tsconfig.json | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/block.ts b/src/block.ts index 03ff901..6440d8a 100644 --- a/src/block.ts +++ b/src/block.ts @@ -24,19 +24,23 @@ function txesHaveWitness (transactions: Array<Transaction>): boolean { export class Block { version: number - prevHash: Buffer - merkleRoot: Buffer + prevHash?: Buffer + merkleRoot?: Buffer timestamp: number - witnessCommit: Buffer + witnessCommit?: Buffer bits: number nonce: number - transactions: Array<Transaction> + transactions?: Array<Transaction> constructor () { this.version = 1 this.timestamp = 0 this.bits = 0 this.nonce = 0 + this.prevHash = undefined + this.merkleRoot = undefined + this.witnessCommit = undefined + this.transactions = undefined } static fromBuffer (buffer: Buffer): Block { @@ -134,7 +138,7 @@ export class Block { } hasWitnessCommit (): boolean { - return txesHaveWitness(this.transactions) + return txesHaveWitness(<Array<Transaction>>this.transactions) } byteLength (headersOnly: boolean): number { @@ -179,8 +183,8 @@ export class Block { } writeInt32(this.version) - writeSlice(this.prevHash) - writeSlice(this.merkleRoot) + writeSlice(<Buffer>this.prevHash) + writeSlice(<Buffer>this.merkleRoot) writeUInt32(this.timestamp) writeUInt32(this.bits) writeUInt32(this.nonce) @@ -207,7 +211,7 @@ export class Block { if (!this.transactions) throw errorMerkleNoTxes const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions) - return this.merkleRoot.compare(actualMerkleRoot) === 0 + return (<Buffer>this.merkleRoot).compare(actualMerkleRoot) === 0 } checkWitnessCommit (): boolean { @@ -215,7 +219,7 @@ export class Block { if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true) - return this.witnessCommit.compare(actualWitnessCommit) === 0 + return (<Buffer>this.witnessCommit).compare(actualWitnessCommit) === 0 } checkProofOfWork (): boolean { diff --git a/tsconfig.json b/tsconfig.json index cfc286a..fd2d954 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,7 @@ "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, - "strictPropertyInitialization": false, + "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true, "esModuleInterop": true From 180f9ec958ce828e19b4554bf5d0c3bd4be5ecd1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 10:38:22 +0900 Subject: [PATCH 188/568] Enable strict (HUZZAH\!) --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index fd2d954..b7e8c8e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "node" ], "allowJs": false, - "strict": false, + "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, From a7735ce07737e546149e1279c8ffa775128e1023 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 10:45:28 +0900 Subject: [PATCH 189/568] Match coverage targets with current level --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 55454bc..130d644 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "scripts": { "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", - "coverage": "nyc --check-coverage --branches 80 --functions 80 --lines 80 mocha", + "coverage": "nyc --check-coverage --branches 83 --functions 90 --lines 89 mocha", "integration": "npm run build && mocha --timeout 50000 test/integration/", "standard": "standard", "test": "npm run build && npm run standard && npm run coverage", From 1aaba64acc354ac922fa0ec96c1920e48c250215 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 10:49:28 +0900 Subject: [PATCH 190/568] esModuleInterop was causing low coverage --- package.json | 2 +- tsconfig.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 130d644..ceb96de 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "scripts": { "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", - "coverage": "nyc --check-coverage --branches 83 --functions 90 --lines 89 mocha", + "coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", "integration": "npm run build && mocha --timeout 50000 test/integration/", "standard": "standard", "test": "npm run build && npm run standard && npm run coverage", diff --git a/tsconfig.json b/tsconfig.json index b7e8c8e..1d68be9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,7 @@ "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true, - "esModuleInterop": true + "esModuleInterop": false }, "include": [ "src/**/*" From 44d88fcfb3670200c8d2e864f931b53b707a7f54 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 11:10:25 +0900 Subject: [PATCH 191/568] Clean up scripts and add postinstall for installing via git --- package.json | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index ceb96de..0a09c44 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "4.0.2", "description": "Client-side Bitcoin JavaScript library", "main": "./dist/src/index.js", + "types": "./dist/src/index.d.ts", "engines": { "node": ">=8.0.0" }, @@ -14,14 +15,21 @@ "bitcoinjs" ], "scripts": { - "coverage-report": "nyc report --reporter=lcov", - "coverage-html": "nyc report --reporter=html", - "coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", - "integration": "npm run build && mocha --timeout 50000 test/integration/", - "standard": "standard", - "test": "npm run build && npm run standard && npm run coverage", - "unit": "npm run build && mocha", - "build": "tsc -d -p tsconfig.json" + "build": "tsc -d -p ./tsconfig.json", + "coverage-report": "npm run build && npm run nobuild:coverage-report", + "coverage-html": "npm run build && npm run nobuild:coverage-html", + "coverage": "npm run build && npm run nobuild:coverage", + "integration": "npm run build && npm run nobuild:integration", + "nobuild:coverage-report": "nyc report --reporter=lcov", + "nobuild:coverage-html": "nyc report --reporter=html", + "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", + "nobuild:integration": "mocha --timeout 50000 test/integration/", + "nobuild:standard": "standard", + "nobuild:unit": "mocha", + "prepare": "npm run build", + "standard": "npm run build && npm run nobuild:standard", + "test": "npm run build && npm run nobuild:standard && npm run nobuild:coverage", + "unit": "npm run build && npm run nobuild:unit" }, "repository": { "type": "git", @@ -31,6 +39,7 @@ "dist/src" ], "dependencies": { + "@types/node": "^10.12.18", "bech32": "^1.1.2", "bip32": "^1.0.0", "bip66": "^1.1.0", @@ -44,11 +53,11 @@ "safe-buffer": "^5.1.1", "tiny-secp256k1": "^1.0.0", "typeforce": "^1.11.3", + "typescript": "^3.2.2", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" }, "devDependencies": { - "@types/node": "^10.12.18", "bip39": "^2.3.0", "bip65": "^1.0.1", "bip68": "^1.0.3", @@ -60,8 +69,7 @@ "mocha": "^5.2.0", "nyc": "^11.8.0", "proxyquire": "^2.0.1", - "standard": "^11.0.1", - "typescript": "^3.2.2" + "standard": "^11.0.1" }, "license": "MIT" } From c17cdce348db972f2673c9cb203e3b7db69dfa0e Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 16:10:36 +0900 Subject: [PATCH 192/568] Move all imports to modules where possible --- src/address.ts | 22 +++++----- src/block.ts | 2 +- src/classify.ts | 22 +++++----- src/ecpair.ts | 10 ++--- src/payments/embed.ts | 2 +- src/payments/p2ms.ts | 4 +- src/payments/p2pk.ts | 2 +- src/payments/p2pkh.ts | 2 +- src/payments/p2sh.ts | 2 +- src/payments/p2wpkh.ts | 2 +- src/payments/p2wsh.ts | 2 +- src/script.ts | 7 ++-- src/transaction.ts | 4 +- src/transaction_builder.ts | 85 +++++++++++++++++++------------------- 14 files changed, 85 insertions(+), 83 deletions(-) diff --git a/src/address.ts b/src/address.ts index c9ffeeb..1e5e547 100644 --- a/src/address.ts +++ b/src/address.ts @@ -1,13 +1,13 @@ import * as Networks from './networks' import { Network } from './networks' import * as types from './types' +import * as bscript from './script' +import * as networks from './networks' +import * as payments from './payments' const Buffer = require('safe-buffer').Buffer const bech32 = require('bech32') const bs58check = require('bs58check') -const bscript = require('./script') -const networks = require('./networks') const typeforce = require('typeforce') -const payments = require('./payments') export type Base58CheckResult = { hash: Buffer; @@ -64,10 +64,10 @@ export function toBech32 (data: Buffer, version: number, prefix: string): string export function fromOutputScript (output: Buffer, network: Network): string { //TODO: Network network = network || networks.bitcoin - try { return payments.p2pkh({ output, network }).address } catch (e) {} - try { return payments.p2sh({ output, network }).address } catch (e) {} - try { return payments.p2wpkh({ output, network }).address } catch (e) {} - try { return payments.p2wsh({ output, network }).address } catch (e) {} + try { return <string>payments.p2pkh({ output, network }).address } catch (e) {} + try { return <string>payments.p2sh({ output, network }).address } catch (e) {} + try { return <string>payments.p2wpkh({ output, network }).address } catch (e) {} + try { return <string>payments.p2wsh({ output, network }).address } catch (e) {} throw new Error(bscript.toASM(output) + ' has no matching Address') } @@ -82,8 +82,8 @@ export function toOutputScript (address: string, network: Network): Buffer { } catch (e) {} if (decodeBase58) { - if (decodeBase58.version === network.pubKeyHash) return payments.p2pkh({ hash: decodeBase58.hash }).output - if (decodeBase58.version === network.scriptHash) return payments.p2sh({ hash: decodeBase58.hash }).output + if (decodeBase58.version === network.pubKeyHash) return <Buffer>payments.p2pkh({ hash: decodeBase58.hash }).output + if (decodeBase58.version === network.scriptHash) return <Buffer>payments.p2sh({ hash: decodeBase58.hash }).output } else { try { decodeBech32 = fromBech32(address) @@ -92,8 +92,8 @@ export function toOutputScript (address: string, network: Network): Buffer { if (decodeBech32) { if (decodeBech32.prefix !== network.bech32) throw new Error(address + ' has an invalid prefix') if (decodeBech32.version === 0) { - if (decodeBech32.data.length === 20) return payments.p2wpkh({ hash: decodeBech32.data }).output - if (decodeBech32.data.length === 32) return payments.p2wsh({ hash: decodeBech32.data }).output + if (decodeBech32.data.length === 20) return <Buffer>payments.p2wpkh({ hash: decodeBech32.data }).output + if (decodeBech32.data.length === 32) return <Buffer>payments.p2wsh({ hash: decodeBech32.data }).output } } } diff --git a/src/block.ts b/src/block.ts index 6440d8a..f91ad32 100644 --- a/src/block.ts +++ b/src/block.ts @@ -1,7 +1,7 @@ import { Transaction } from './transaction' import * as types from './types' +import * as bcrypto from './crypto' const Buffer = require('safe-buffer').Buffer -const bcrypto = require('./crypto') const fastMerkleRoot = require('merkle-lib/fastRoot') const typeforce = require('typeforce') const varuint = require('varuint-bitcoin') diff --git a/src/classify.ts b/src/classify.ts index 5eabc7e..9a5c849 100644 --- a/src/classify.ts +++ b/src/classify.ts @@ -1,12 +1,12 @@ -const decompile = require('./script').decompile -const multisig = require('./templates/multisig') -const nullData = require('./templates/nulldata') -const pubKey = require('./templates/pubkey') -const pubKeyHash = require('./templates/pubkeyhash') -const scriptHash = require('./templates/scripthash') -const witnessPubKeyHash = require('./templates/witnesspubkeyhash') -const witnessScriptHash = require('./templates/witnessscripthash') -const witnessCommitment = require('./templates/witnesscommitment') +import { decompile } from './script' +import * as multisig from './templates/multisig' +import * as nullData from './templates/nulldata' +import * as pubKey from './templates/pubkey' +import * as pubKeyHash from './templates/pubkeyhash' +import * as scriptHash from './templates/scripthash' +import * as witnessPubKeyHash from './templates/witnesspubkeyhash' +import * as witnessScriptHash from './templates/witnessscripthash' +import * as witnessCommitment from './templates/witnesscommitment' const types = { P2MS: <string> 'multisig', @@ -51,13 +51,13 @@ function classifyInput (script: Buffer, allowIncomplete: boolean): string { return types.NONSTANDARD } -function classifyWitness (script: Buffer, allowIncomplete: boolean): string { +function classifyWitness (script: Array<Buffer>, allowIncomplete: boolean): string { // XXX: optimization, below functions .decompile before use const chunks = decompile(script) if (!chunks) throw new TypeError('Invalid script') if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH - if (witnessScriptHash.input.check(chunks, allowIncomplete)) return types.P2WSH + if (witnessScriptHash.input.check(<Array<Buffer>>chunks, allowIncomplete)) return types.P2WSH return types.NONSTANDARD } diff --git a/src/ecpair.ts b/src/ecpair.ts index a7426ae..9a54b1a 100644 --- a/src/ecpair.ts +++ b/src/ecpair.ts @@ -67,18 +67,18 @@ class ECPair implements ECPairInterface { } } -function fromPrivateKey (buffer: Buffer, options: ECPairOptions): ECPair { +function fromPrivateKey (buffer: Buffer, options?: ECPairOptions): ECPair { typeforce(types.Buffer256bit, buffer) if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)') typeforce(isOptions, options) - return new ECPair(buffer, null, options) + return new ECPair(buffer, null, <ECPairOptions>options) } -function fromPublicKey (buffer: Buffer, options: ECPairOptions): ECPair { +function fromPublicKey (buffer: Buffer, options?: ECPairOptions): ECPair { typeforce(ecc.isPoint, buffer) typeforce(isOptions, options) - return new ECPair(null, buffer, options) + return new ECPair(null, buffer, <ECPairOptions>options) } function fromWIF (string: string, network: Network | Array<Network>): ECPair { @@ -106,7 +106,7 @@ function fromWIF (string: string, network: Network | Array<Network>): ECPair { }) } -function makeRandom (options: ECPairOptions): ECPair { +function makeRandom (options?: ECPairOptions): ECPair { typeforce(isOptions, options) if (options === undefined) options = {} const rng = options.rng || randomBytes diff --git a/src/payments/embed.ts b/src/payments/embed.ts index 2526fe0..8d198a0 100644 --- a/src/payments/embed.ts +++ b/src/payments/embed.ts @@ -14,7 +14,7 @@ function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { } // output: OP_RETURN ... -export function p2data (a: Payment, opts: PaymentOpts): Payment { +export function p2data (a: Payment, opts?: PaymentOpts): Payment { if ( !a.data && !a.output diff --git a/src/payments/p2ms.ts b/src/payments/p2ms.ts index b8a05b3..37701a5 100644 --- a/src/payments/p2ms.ts +++ b/src/payments/p2ms.ts @@ -18,7 +18,7 @@ function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { // input: OP_0 [signatures ...] // output: m [pubKeys ...] n OP_CHECKMULTISIG -export function p2ms (a: Payment, opts: PaymentOpts): Payment { +export function p2ms (a: Payment, opts?: PaymentOpts): Payment { if ( !a.input && !a.output && @@ -28,7 +28,7 @@ export function p2ms (a: Payment, opts: PaymentOpts): Payment { opts = Object.assign({ validate: true }, opts || {}) function isAcceptableSignature (x: Buffer | number) { - return bscript.isCanonicalScriptSignature(<Buffer>x) || (opts.allowIncomplete && (<number>x === OPS.OP_0)) !== undefined + return bscript.isCanonicalScriptSignature(<Buffer>x) || ((<PaymentOpts>opts).allowIncomplete && (<number>x === OPS.OP_0)) !== undefined } typef({ diff --git a/src/payments/p2pk.ts b/src/payments/p2pk.ts index 45b3577..99b25d2 100644 --- a/src/payments/p2pk.ts +++ b/src/payments/p2pk.ts @@ -8,7 +8,7 @@ const ecc = require('tiny-secp256k1') // input: {signature} // output: {pubKey} OP_CHECKSIG -export function p2pk (a: Payment, opts: PaymentOpts): Payment { +export function p2pk (a: Payment, opts?: PaymentOpts): Payment { if ( !a.input && !a.output && diff --git a/src/payments/p2pkh.ts b/src/payments/p2pkh.ts index f2054b0..53c452f 100644 --- a/src/payments/p2pkh.ts +++ b/src/payments/p2pkh.ts @@ -11,7 +11,7 @@ const bs58check = require('bs58check') // input: {signature} {pubkey} // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG -export function p2pkh (a: Payment, opts: PaymentOpts): Payment { +export function p2pkh (a: Payment, opts?: PaymentOpts): Payment { if ( !a.address && !a.hash && diff --git a/src/payments/p2sh.ts b/src/payments/p2sh.ts index 6facde9..dfbee4c 100644 --- a/src/payments/p2sh.ts +++ b/src/payments/p2sh.ts @@ -20,7 +20,7 @@ function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { // input: [redeemScriptSig ...] {redeemScript} // witness: <?> // output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL -export function p2sh (a: Payment, opts: PaymentOpts): Payment { +export function p2sh (a: Payment, opts?: PaymentOpts): Payment { if ( !a.address && !a.hash && diff --git a/src/payments/p2wpkh.ts b/src/payments/p2wpkh.ts index 7089246..96c367b 100644 --- a/src/payments/p2wpkh.ts +++ b/src/payments/p2wpkh.ts @@ -14,7 +14,7 @@ const EMPTY_BUFFER = Buffer.alloc(0) // witness: {signature} {pubKey} // input: <> // output: OP_0 {pubKeyHash} -export function p2wpkh (a: Payment, opts: PaymentOpts): Payment { +export function p2wpkh (a: Payment, opts?: PaymentOpts): Payment { if ( !a.address && !a.hash && diff --git a/src/payments/p2wsh.ts b/src/payments/p2wsh.ts index c70cfb6..b151b82 100644 --- a/src/payments/p2wsh.ts +++ b/src/payments/p2wsh.ts @@ -22,7 +22,7 @@ function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { // input: <> // witness: [redeemScriptSig ...] {redeemScript} // output: OP_0 {sha256(redeemScript)} -export function p2wsh (a: Payment, opts: PaymentOpts): Payment { +export function p2wsh (a: Payment, opts?: PaymentOpts): Payment { if ( !a.address && !a.hash && diff --git a/src/script.ts b/src/script.ts index 6d9bfc9..190c9cd 100644 --- a/src/script.ts +++ b/src/script.ts @@ -1,10 +1,11 @@ import * as types from './types' +import * as scriptNumber from './script_number' +import * as scriptSignature from './script_signature' const Buffer = require('safe-buffer').Buffer const bip66 = require('bip66') const ecc = require('tiny-secp256k1') const pushdata = require('pushdata-bitcoin') const typeforce = require('typeforce') -const scriptNumber = require('./script_number') const OPS = require('bitcoin-ops') const REVERSE_OPS = require('bitcoin-ops/map') @@ -200,5 +201,5 @@ export function isCanonicalScriptSignature (buffer: Buffer): boolean { return bip66.check(buffer.slice(0, -1)) } -export const number = require('./script_number') -export const signature = require('./script_signature') +export const number = scriptNumber +export const signature = scriptSignature diff --git a/src/transaction.ts b/src/transaction.ts index af17449..b40e368 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -1,8 +1,8 @@ import * as bcrypto from './crypto' import * as bscript from './script' import * as types from './types' +import * as bufferutils from './bufferutils' const Buffer = require('safe-buffer').Buffer -const bufferutils = require('./bufferutils') const opcodes = require('bitcoin-ops') const typeforce = require('typeforce') const varuint = require('varuint-bitcoin') @@ -491,7 +491,7 @@ export class Transaction { } function writeUInt64 (i: number) { - offset = bufferutils.writeUInt64LE(buffer, i, offset) + offset = bufferutils.writeUInt64LE(<Buffer>buffer, i, offset) } function writeVarInt (i: number) { diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index 41d524e..a6b3b48 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -2,18 +2,19 @@ import { Network } from './networks' import * as networks from './networks' import { Transaction, Output } from './transaction' import { ECPairInterface } from './ecpair' +import * as ECPair from './ecpair' import * as types from './types' -const Buffer = require('safe-buffer').Buffer -const baddress = require('./address') -const bcrypto = require('./crypto') -const bscript = require('./script') +import * as baddress from './address' +import * as bcrypto from './crypto' +import * as bscript from './script' +import { Payment } from './payments' +import * as payments from './payments' +import * as classify from './classify' const ops = require('bitcoin-ops') -const payments = require('./payments') const typeforce = require('typeforce') -const classify = require('./classify') +const Buffer = require('safe-buffer').Buffer const SCRIPT_TYPES = classify.types -const ECPair = require('./ecpair') interface TxbInput { value?: number @@ -24,7 +25,7 @@ interface TxbInput { redeemScript?: Buffer redeemScriptType?: string prevOutType?: string - pubkeys?: Array<Buffer> | Array<undefined> + pubkeys?: Array<Buffer | undefined> signatures?: Array<Buffer> | Array<Buffer | undefined> witness?: Array<Buffer> witnessScript?: Buffer @@ -37,7 +38,7 @@ interface TxbInput { interface TxbOutput { type: string - pubkeys?: Array<Buffer> + pubkeys?: Array<Buffer | undefined> signatures?: Array<Buffer> | Array<Buffer | undefined> maxSignatures?: number } @@ -232,8 +233,8 @@ export class TransactionBuilder { return } - tx.setInputScript(i, result.input) - tx.setWitness(i, result.witness) + tx.setInputScript(i, <Buffer>result.input) + tx.setWitness(i, <Array<Buffer>>result.witness) }) if (!allowIncomplete) { @@ -381,8 +382,8 @@ export class TransactionBuilder { function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: string, scriptPubKey?: Buffer): TxbInput { if (scriptSig.length === 0 && witnessStack.length === 0) return {} if (!type) { - let ssType = classify.input(scriptSig, true) - let wsType = classify.witness(witnessStack, true) + let ssType: string | undefined = classify.input(scriptSig, true) + let wsType: string | undefined = classify.witness(witnessStack, true) if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined type = ssType || wsType @@ -442,14 +443,14 @@ function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: str witness: witnessStack }) - const outputType = classify.output(redeem.output) - const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output) + const outputType = classify.output(<Buffer>(<Payment>redeem).output) + const expanded = expandInput(<Buffer>(<Payment>redeem).input, <Array<Buffer>>(<Payment>redeem).witness, outputType, (<Payment>redeem).output) if (!expanded.prevOutType) return {} return { prevOutScript: output, prevOutType: SCRIPT_TYPES.P2SH, - redeemScript: redeem.output, + redeemScript: (<Payment>redeem).output, redeemScriptType: expanded.prevOutType, witnessScript: expanded.witnessScript, witnessScriptType: expanded.witnessScriptType, @@ -464,19 +465,19 @@ function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: str input: scriptSig, witness: witnessStack }) - const outputType = classify.output(redeem.output) + const outputType = classify.output(<Buffer>(<Payment>redeem).output) let expanded if (outputType === SCRIPT_TYPES.P2WPKH) { - expanded = expandInput(redeem.input, redeem.witness, outputType) + expanded = expandInput(<Buffer>(<Payment>redeem).input, <Array<Buffer>>(<Payment>redeem).witness, outputType) } else { - expanded = expandInput(bscript.compile(redeem.witness), [], outputType, redeem.output) + expanded = expandInput(bscript.compile(<Array<Buffer>>(<Payment>redeem).witness), [], outputType, (<Payment>redeem).output) } if (!expanded.prevOutType) return {} return { prevOutScript: output, prevOutType: SCRIPT_TYPES.P2WSH, - witnessScript: redeem.output, + witnessScript: (<Payment>redeem).output, witnessScriptType: expanded.prevOutType, pubkeys: expanded.pubkeys, @@ -535,7 +536,7 @@ function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { // does our hash160(pubKey) match the output scripts? const pkh1 = payments.p2pkh({ output: script }).hash const pkh2 = bcrypto.hash160(ourPubKey) - if (!pkh1.equals(pkh2)) return { type } + if (!(<Buffer>pkh1).equals(pkh2)) return { type } return { type, @@ -550,7 +551,7 @@ function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { // does our hash160(pubKey) match the output scripts? const wpkh1 = payments.p2wpkh({ output: script }).hash const wpkh2 = bcrypto.hash160(ourPubKey) - if (!wpkh1.equals(wpkh2)) return { type } + if (!(<Buffer>wpkh1).equals(wpkh2)) return { type } return { type, @@ -573,7 +574,7 @@ function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { return { type, pubkeys: p2ms.pubkeys, - signatures: p2ms.pubkeys.map((): undefined => undefined), + signatures: (<Array<Buffer>>p2ms.pubkeys).map((): undefined => undefined), maxSignatures: p2ms.m } } @@ -584,16 +585,16 @@ function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, witnessValue: number, witnessScript: Buffer): TxbInput { if (redeemScript && witnessScript) { - const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }) - const p2wshAlt = payments.p2wsh({ output: redeemScript }) - const p2sh = payments.p2sh({ redeem: { output: redeemScript } }) - const p2shAlt = payments.p2sh({ redeem: p2wsh }) + const p2wsh = <Payment> payments.p2wsh({ redeem: { output: witnessScript } }) + const p2wshAlt = <Payment> payments.p2wsh({ output: redeemScript }) + const p2sh = <Payment> payments.p2sh({ redeem: { output: redeemScript } }) + const p2shAlt = <Payment> payments.p2sh({ redeem: p2wsh }) // enforces P2SH(P2WSH(...)) - if (!p2wsh.hash.equals(p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript') - if (!p2sh.hash.equals(p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript') + if (!(<Buffer>p2wsh.hash).equals(<Buffer>p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript') + if (!(<Buffer>p2sh.hash).equals(<Buffer>p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript') - const expanded = expandOutput(p2wsh.redeem.output, ourPubKey) + const expanded = expandOutput(<Buffer>(<Payment>p2wsh.redeem).output, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures @@ -623,17 +624,17 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, } if (redeemScript) { - const p2sh = payments.p2sh({ redeem: { output: redeemScript } }) + const p2sh = <Payment> payments.p2sh({ redeem: { output: redeemScript } }) if (input.prevOutScript) { let p2shAlt try { - p2shAlt = payments.p2sh({ output: input.prevOutScript }) + p2shAlt = <Payment> payments.p2sh({ output: input.prevOutScript }) } catch (e) { throw new Error('PrevOutScript must be P2SH') } - if (!p2sh.hash.equals(p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript') + if (!(<Buffer>p2sh.hash).equals(<Buffer>p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript') } - const expanded = expandOutput(p2sh.redeem.output, ourPubKey) + const expanded = expandOutput(<Buffer>(<Payment>p2sh.redeem).output, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')') if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures @@ -641,7 +642,7 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, let signScript = redeemScript if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output + signScript = <Buffer> payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output } return { @@ -662,14 +663,14 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, } if (witnessScript) { - const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }) + const p2wsh = <Payment> payments.p2wsh({ redeem: { output: witnessScript } }) if (input.prevOutScript) { const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }) - if (!p2wsh.hash.equals(p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript') + if (!(<Buffer>p2wsh.hash).equals(<Buffer>p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript') } - const expanded = expandOutput(p2wsh.redeem.output, ourPubKey) + const expanded = expandOutput(<Buffer>(<Payment>p2wsh.redeem).output, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures @@ -709,7 +710,7 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, let signScript = input.prevOutScript if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output + signScript = <Buffer> payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output } return { @@ -740,9 +741,9 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, } } -function build (type: string, input: TxbInput, allowIncomplete?: boolean): any { //TODO payment type - const pubkeys = input.pubkeys || [] - let signatures = input.signatures || [] +function build (type: string, input: TxbInput, allowIncomplete?: boolean): Payment | undefined { + const pubkeys = <Array<Buffer>>(input.pubkeys || []) + let signatures = <Array<Buffer>>(input.signatures || []) switch (type) { case SCRIPT_TYPES.P2PKH: { From 3124e50e52a1b9fcf7f3a986f2025cb95fcbbe4b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 16:36:36 +0900 Subject: [PATCH 193/568] TypeScript hates Buffer.prototype.reverse, so fixed it. --- src/block.ts | 5 +++-- src/bufferutils.ts | 13 +++++++++++++ src/ecpair.ts | 14 ++++++++------ src/transaction.ts | 11 ++++++----- src/transaction_builder.ts | 3 ++- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/block.ts b/src/block.ts index f91ad32..d79de9d 100644 --- a/src/block.ts +++ b/src/block.ts @@ -1,6 +1,7 @@ import { Transaction } from './transaction' import * as types from './types' import * as bcrypto from './crypto' +import { reverseBuffer } from './bufferutils' const Buffer = require('safe-buffer').Buffer const fastMerkleRoot = require('merkle-lib/fastRoot') const typeforce = require('typeforce') @@ -153,7 +154,7 @@ export class Block { } getId (): string { - return Buffer.from(this.getHash().reverse()).toString('hex') + return reverseBuffer(this.getHash()).toString('hex') } getUTCDate (): Date { @@ -223,7 +224,7 @@ export class Block { } checkProofOfWork (): boolean { - const hash: Buffer = Buffer.from(this.getHash().reverse()) + const hash: Buffer = reverseBuffer(this.getHash()) const target = Block.calculateTarget(this.bits) return hash.compare(target) <= 0 diff --git a/src/bufferutils.ts b/src/bufferutils.ts index b66c84a..3ef7492 100644 --- a/src/bufferutils.ts +++ b/src/bufferutils.ts @@ -22,3 +22,16 @@ export function writeUInt64LE (buffer: Buffer, value: number, offset: number): n buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4) return offset + 8 } + +export function reverseBuffer (buffer: Buffer): Buffer { + if (buffer.length < 1) return buffer + let j = buffer.length - 1 + let tmp = 0 + for (let i = 0; i < buffer.length / 2; i++) { + tmp = buffer[i] + buffer[i] = buffer[j] + buffer[j] = tmp + j-- + } + return buffer +} diff --git a/src/ecpair.ts b/src/ecpair.ts index 9a54b1a..321a5ed 100644 --- a/src/ecpair.ts +++ b/src/ecpair.ts @@ -33,14 +33,16 @@ class ECPair implements ECPairInterface { network: Network private __d: Buffer | null private __Q: Buffer | null - constructor (d: Buffer | null, Q: Buffer | null, options: ECPairOptions) { + + constructor (d?: Buffer, Q?: Buffer, options?: ECPairOptions) { if (options === undefined) options = {} this.compressed = options.compressed === undefined ? true : options.compressed this.network = options.network || NETWORKS.bitcoin - this.__d = d || null + this.__d = null this.__Q = null - if (Q) this.__Q = ecc.pointCompress(Q, this.compressed) + if (d !== undefined) this.__d = d + if (Q !== undefined) this.__Q = ecc.pointCompress(Q, this.compressed) } get privateKey (): Buffer | null { @@ -72,16 +74,16 @@ function fromPrivateKey (buffer: Buffer, options?: ECPairOptions): ECPair { if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)') typeforce(isOptions, options) - return new ECPair(buffer, null, <ECPairOptions>options) + return new ECPair(buffer, undefined, <ECPairOptions>options) } function fromPublicKey (buffer: Buffer, options?: ECPairOptions): ECPair { typeforce(ecc.isPoint, buffer) typeforce(isOptions, options) - return new ECPair(null, buffer, <ECPairOptions>options) + return new ECPair(undefined, buffer, <ECPairOptions>options) } -function fromWIF (string: string, network: Network | Array<Network>): ECPair { +function fromWIF (string: string, network?: Network | Array<Network>): ECPair { const decoded = wif.decode(string) const version = decoded.version diff --git a/src/transaction.ts b/src/transaction.ts index b40e368..515ffb6 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -2,6 +2,7 @@ import * as bcrypto from './crypto' import * as bscript from './script' import * as types from './types' import * as bufferutils from './bufferutils' +import { reverseBuffer } from './bufferutils' const Buffer = require('safe-buffer').Buffer const opcodes = require('bitcoin-ops') const typeforce = require('typeforce') @@ -74,7 +75,7 @@ export class Transaction { this.outs = [] } - static fromBuffer (buffer: Buffer, __noStrict: boolean): Transaction { + static fromBuffer (buffer: Buffer, __noStrict?: boolean): Transaction { let offset: number = 0 function readSlice (n: number): Buffer { @@ -234,7 +235,7 @@ export class Transaction { return this.__byteLength(true) } - __byteLength (__allowWitness: boolean): number { + private __byteLength (__allowWitness: boolean): number { const hasWitnesses = __allowWitness && this.hasWitnesses() return ( @@ -454,7 +455,7 @@ export class Transaction { return bcrypto.hash256(tbuffer) } - getHash (forWitness: boolean): Buffer { + getHash (forWitness?: boolean): Buffer { // wtxid for coinbase is always 32 bytes of 0x00 if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0) return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness)) @@ -462,14 +463,14 @@ export class Transaction { getId (): string { // transaction hash's are displayed in reverse order - return Buffer.from(this.getHash(false).reverse()).toString('hex') + return reverseBuffer(this.getHash(false)).toString('hex') } toBuffer (buffer?: Buffer, initialOffset?: number): Buffer { return this.__toBuffer(buffer, initialOffset, true) } - __toBuffer (buffer?: Buffer, initialOffset?: number, __allowWitness?: boolean): Buffer { + private __toBuffer (buffer?: Buffer, initialOffset?: number, __allowWitness?: boolean): Buffer { if (!buffer) buffer = <Buffer> Buffer.allocUnsafe(this.__byteLength((<boolean>__allowWitness))) let offset = initialOffset || 0 diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index a6b3b48..a920130 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -1,5 +1,6 @@ import { Network } from './networks' import * as networks from './networks' +import { reverseBuffer } from './bufferutils' import { Transaction, Output } from './transaction' import { ECPairInterface } from './ecpair' import * as ECPair from './ecpair' @@ -131,7 +132,7 @@ export class TransactionBuilder { // is it a hex string? if (txIsString(txHash)) { // transaction hashs's are displayed in reverse order, un-reverse it - txHash = <Buffer> Buffer.from(txHash, 'hex').reverse() + txHash = reverseBuffer(Buffer.from(txHash, 'hex')) // is it a Transaction object? } else if (txIsTransaction(txHash)) { From 912d6d41becf3b4b77b00038c481d71b34c64b2e Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 17:02:32 +0900 Subject: [PATCH 194/568] Fix some small bugs --- src/payments/p2ms.js | 2 +- src/transaction_builder.js | 4 ++-- test/fixtures/p2ms.json | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index 5c90a4d..8c5a380 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -129,7 +129,7 @@ function p2ms (a, opts) { if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') - if (a.signatures && !stacksEqual(a.signatures.equals(o.signatures))) throw new TypeError('Signature mismatch') + if (a.signatures && !stacksEqual(a.signatures, o.signatures)) throw new TypeError('Signature mismatch') if (a.m !== undefined && a.m !== a.signatures.length) throw new TypeError('Signature count mismatch') } } diff --git a/src/transaction_builder.js b/src/transaction_builder.js index bea1ded..6508a86 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -217,7 +217,7 @@ function expandOutput (script, ourPubKey) { return { type } } -function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScript) { +function prepareInput (input, ourPubKey, redeemScript, witnessScript) { if (redeemScript && witnessScript) { const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }) const p2wshAlt = payments.p2wsh({ output: redeemScript }) @@ -665,7 +665,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy } if (!canSign(input)) { - const prepared = prepareInput(input, ourPubKey, redeemScript, witnessValue, witnessScript) + const prepared = prepareInput(input, ourPubKey, redeemScript, witnessScript) // updates inline Object.assign(input, prepared) diff --git a/test/fixtures/p2ms.json b/test/fixtures/p2ms.json index 8ea033e..2f41270 100644 --- a/test/fixtures/p2ms.json +++ b/test/fixtures/p2ms.json @@ -310,6 +310,20 @@ ] } }, + { + "exception": "Signature mismatch", + "arguments": { + "m": 1, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "signatures": [ + "300602010002010001" + ], + "input": "OP_0 300602010002010101" + } + }, { "exception": "Too many signatures provided", "arguments": { From e58d0126159f2da6fb85128c84c46d7de16fa969 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 21:39:19 +0900 Subject: [PATCH 195/568] Remove safe-buffer and add type to bitcoin-ops --- package.json | 1 - src/address.ts | 2 +- src/block.ts | 2 +- src/index.ts | 2 +- src/payments/embed.ts | 4 ++-- src/payments/p2ms.ts | 4 ++-- src/payments/p2pk.ts | 2 +- src/payments/p2pkh.ts | 2 +- src/payments/p2sh.ts | 2 +- src/payments/p2wpkh.ts | 2 +- src/payments/p2wsh.ts | 2 +- src/script.ts | 7 ++++--- src/script_number.ts | 2 +- src/script_signature.ts | 2 +- src/templates/multisig/input.ts | 2 +- src/templates/multisig/output.ts | 2 +- src/templates/nulldata.ts | 2 +- src/templates/pubkey/output.ts | 2 +- src/templates/pubkeyhash/output.ts | 2 +- src/templates/scripthash/input.ts | 2 +- src/templates/scripthash/output.ts | 2 +- src/templates/witnesscommitment/output.ts | 4 ++-- src/templates/witnesspubkeyhash/output.ts | 2 +- src/templates/witnessscripthash/output.ts | 2 +- src/transaction.ts | 4 ++-- src/transaction_builder.ts | 4 ++-- 26 files changed, 33 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 0a09c44..4b9557e 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "merkle-lib": "^2.0.10", "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", - "safe-buffer": "^5.1.1", "tiny-secp256k1": "^1.0.0", "typeforce": "^1.11.3", "typescript": "^3.2.2", diff --git a/src/address.ts b/src/address.ts index 1e5e547..7d9d351 100644 --- a/src/address.ts +++ b/src/address.ts @@ -4,7 +4,7 @@ import * as types from './types' import * as bscript from './script' import * as networks from './networks' import * as payments from './payments' -const Buffer = require('safe-buffer').Buffer + const bech32 = require('bech32') const bs58check = require('bs58check') const typeforce = require('typeforce') diff --git a/src/block.ts b/src/block.ts index d79de9d..1483f2c 100644 --- a/src/block.ts +++ b/src/block.ts @@ -2,7 +2,7 @@ import { Transaction } from './transaction' import * as types from './types' import * as bcrypto from './crypto' import { reverseBuffer } from './bufferutils' -const Buffer = require('safe-buffer').Buffer + const fastMerkleRoot = require('merkle-lib/fastRoot') const typeforce = require('typeforce') const varuint = require('varuint-bitcoin') diff --git a/src/index.ts b/src/index.ts index d93f587..31ef042 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,3 @@ -const opcodes = require('bitcoin-ops') const bip32 = require('bip32') import { Block } from './block' @@ -10,6 +9,7 @@ import * as crypto from './crypto' import * as networks from './networks' import * as payments from './payments' import * as script from './script' +import { OPS as opcodes } from './script' export { Block, diff --git a/src/payments/embed.ts b/src/payments/embed.ts index 8d198a0..7e82511 100644 --- a/src/payments/embed.ts +++ b/src/payments/embed.ts @@ -3,7 +3,7 @@ import * as bscript from '../script' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -const OPS = require('bitcoin-ops') +import { OPS } from '../script' function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { if (a.length !== b.length) return false @@ -32,7 +32,7 @@ export function p2data (a: Payment, opts?: PaymentOpts): Payment { lazy.prop(o, 'output', function () { if (!a.data) return - return bscript.compile([OPS.OP_RETURN].concat(a.data)) + return bscript.compile((<Array<Buffer | number>>[OPS.OP_RETURN]).concat(a.data)) }) lazy.prop(o, 'data', function () { if (!a.output) return diff --git a/src/payments/p2ms.ts b/src/payments/p2ms.ts index 37701a5..3c5c679 100644 --- a/src/payments/p2ms.ts +++ b/src/payments/p2ms.ts @@ -3,7 +3,7 @@ import * as bscript from '../script' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -const OPS = require('bitcoin-ops') +import { OPS } from '../script' const ecc = require('tiny-secp256k1') const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 @@ -87,7 +87,7 @@ export function p2ms (a: Payment, opts?: PaymentOpts): Payment { }) lazy.prop(o, 'input', function () { if (!a.signatures) return - return bscript.compile([OPS.OP_0].concat(a.signatures)) + return bscript.compile((<Array<Buffer | number>>[OPS.OP_0]).concat(a.signatures)) }) lazy.prop(o, 'witness', function () { if (!o.input) return diff --git a/src/payments/p2pk.ts b/src/payments/p2pk.ts index 99b25d2..f383a1c 100644 --- a/src/payments/p2pk.ts +++ b/src/payments/p2pk.ts @@ -3,7 +3,7 @@ import * as bscript from '../script' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -const OPS = require('bitcoin-ops') +import { OPS } from '../script' const ecc = require('tiny-secp256k1') // input: {signature} diff --git a/src/payments/p2pkh.ts b/src/payments/p2pkh.ts index 53c452f..4fb49de 100644 --- a/src/payments/p2pkh.ts +++ b/src/payments/p2pkh.ts @@ -4,7 +4,7 @@ import * as bcrypto from '../crypto' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -const OPS = require('bitcoin-ops') +import { OPS } from '../script' const ecc = require('tiny-secp256k1') const bs58check = require('bs58check') diff --git a/src/payments/p2sh.ts b/src/payments/p2sh.ts index dfbee4c..d25b616 100644 --- a/src/payments/p2sh.ts +++ b/src/payments/p2sh.ts @@ -5,7 +5,7 @@ import * as bcrypto from '../crypto' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -const OPS = require('bitcoin-ops') +import { OPS } from '../script' const bs58check = require('bs58check') diff --git a/src/payments/p2wpkh.ts b/src/payments/p2wpkh.ts index 96c367b..918e909 100644 --- a/src/payments/p2wpkh.ts +++ b/src/payments/p2wpkh.ts @@ -4,7 +4,7 @@ import * as bcrypto from '../crypto' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -const OPS = require('bitcoin-ops') +import { OPS } from '../script' const ecc = require('tiny-secp256k1') const bech32 = require('bech32') diff --git a/src/payments/p2wsh.ts b/src/payments/p2wsh.ts index b151b82..1a8e86e 100644 --- a/src/payments/p2wsh.ts +++ b/src/payments/p2wsh.ts @@ -5,7 +5,7 @@ import * as bcrypto from '../crypto' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -const OPS = require('bitcoin-ops') +import { OPS } from '../script' const bech32 = require('bech32') diff --git a/src/script.ts b/src/script.ts index 190c9cd..79e3342 100644 --- a/src/script.ts +++ b/src/script.ts @@ -1,14 +1,15 @@ import * as types from './types' import * as scriptNumber from './script_number' import * as scriptSignature from './script_signature' -const Buffer = require('safe-buffer').Buffer const bip66 = require('bip66') const ecc = require('tiny-secp256k1') const pushdata = require('pushdata-bitcoin') const typeforce = require('typeforce') -const OPS = require('bitcoin-ops') -const REVERSE_OPS = require('bitcoin-ops/map') +export type OpCode = number +export const OPS = <{[index:string]: OpCode}> require('bitcoin-ops') + +const REVERSE_OPS = <{[index:number]: string}> require('bitcoin-ops/map') const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 function isOPInt (value:number): boolean { diff --git a/src/script_number.ts b/src/script_number.ts index 0ff4a7e..d88c463 100644 --- a/src/script_number.ts +++ b/src/script_number.ts @@ -1,4 +1,4 @@ -const Buffer = require('safe-buffer').Buffer + export function decode (buffer: Buffer, maxLength?: number, minimal?: boolean): number { maxLength = maxLength || 4 diff --git a/src/script_signature.ts b/src/script_signature.ts index c24e88a..3b5bc6e 100644 --- a/src/script_signature.ts +++ b/src/script_signature.ts @@ -1,6 +1,6 @@ import * as types from './types' const bip66 = require('bip66') -const Buffer = require('safe-buffer').Buffer + const typeforce = require('typeforce') const ZERO = Buffer.alloc(1, 0) diff --git a/src/templates/multisig/input.ts b/src/templates/multisig/input.ts index abf0982..1ec2874 100644 --- a/src/templates/multisig/input.ts +++ b/src/templates/multisig/input.ts @@ -1,7 +1,7 @@ // OP_0 [signatures ...] import * as bscript from '../../script' -const OPS = require('bitcoin-ops') +import { OPS } from '../../script' function partialSignature (value: number | Buffer): boolean { return value === OPS.OP_0 || bscript.isCanonicalScriptSignature(<Buffer>value) diff --git a/src/templates/multisig/output.ts b/src/templates/multisig/output.ts index e4fa333..e46fa78 100644 --- a/src/templates/multisig/output.ts +++ b/src/templates/multisig/output.ts @@ -2,7 +2,7 @@ import * as bscript from '../../script' import * as types from '../../types' -const OPS = require('bitcoin-ops') +import { OPS } from '../../script' const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { diff --git a/src/templates/nulldata.ts b/src/templates/nulldata.ts index 91c1713..8889fb9 100644 --- a/src/templates/nulldata.ts +++ b/src/templates/nulldata.ts @@ -1,6 +1,6 @@ // OP_RETURN {data} import * as bscript from '../script' -const OPS = require('bitcoin-ops') +import { OPS } from '../script' export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) diff --git a/src/templates/pubkey/output.ts b/src/templates/pubkey/output.ts index f60cc05..aa321b3 100644 --- a/src/templates/pubkey/output.ts +++ b/src/templates/pubkey/output.ts @@ -1,7 +1,7 @@ // {pubKey} OP_CHECKSIG import * as bscript from '../../script' -const OPS = require('bitcoin-ops') +import { OPS } from '../../script' export function check (script: Buffer | Array<number | Buffer>): boolean { const chunks = <Array<number | Buffer>>bscript.decompile(script) diff --git a/src/templates/pubkeyhash/output.ts b/src/templates/pubkeyhash/output.ts index 71c1acb..242cb0e 100644 --- a/src/templates/pubkeyhash/output.ts +++ b/src/templates/pubkeyhash/output.ts @@ -1,7 +1,7 @@ // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG import * as bscript from '../../script' -const OPS = require('bitcoin-ops') +import { OPS } from '../../script' export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) diff --git a/src/templates/scripthash/input.ts b/src/templates/scripthash/input.ts index 93e7d58..90af0fe 100644 --- a/src/templates/scripthash/input.ts +++ b/src/templates/scripthash/input.ts @@ -7,7 +7,7 @@ import * as p2pkh from '../pubkeyhash' import * as p2wpkho from '../witnesspubkeyhash/output' import * as p2wsho from '../witnessscripthash/output' -const Buffer = require('safe-buffer').Buffer + export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { const chunks = <Array<number | Buffer>>bscript.decompile(script) diff --git a/src/templates/scripthash/output.ts b/src/templates/scripthash/output.ts index 3215c25..0b968bf 100644 --- a/src/templates/scripthash/output.ts +++ b/src/templates/scripthash/output.ts @@ -1,7 +1,7 @@ // OP_HASH160 {scriptHash} OP_EQUAL import * as bscript from '../../script' -const OPS = require('bitcoin-ops') +import { OPS } from '../../script' export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) diff --git a/src/templates/witnesscommitment/output.ts b/src/templates/witnesscommitment/output.ts index d89b8e2..71e8df3 100644 --- a/src/templates/witnesscommitment/output.ts +++ b/src/templates/witnesscommitment/output.ts @@ -2,9 +2,9 @@ import * as bscript from '../../script' import * as types from '../../types' -const Buffer = require('safe-buffer').Buffer + const typeforce = require('typeforce') -const OPS = require('bitcoin-ops') +import { OPS } from '../../script' const HEADER: Buffer = Buffer.from('aa21a9ed', 'hex') diff --git a/src/templates/witnesspubkeyhash/output.ts b/src/templates/witnesspubkeyhash/output.ts index 46fb444..bc7a3a0 100644 --- a/src/templates/witnesspubkeyhash/output.ts +++ b/src/templates/witnesspubkeyhash/output.ts @@ -1,7 +1,7 @@ // OP_0 {pubKeyHash} import * as bscript from '../../script' -const OPS = require('bitcoin-ops') +import { OPS } from '../../script' export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) diff --git a/src/templates/witnessscripthash/output.ts b/src/templates/witnessscripthash/output.ts index 0b13cd1..deffe1f 100644 --- a/src/templates/witnessscripthash/output.ts +++ b/src/templates/witnessscripthash/output.ts @@ -1,7 +1,7 @@ // OP_0 {scriptHash} import * as bscript from '../../script' -const OPS = require('bitcoin-ops') +import { OPS } from '../../script' export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) diff --git a/src/transaction.ts b/src/transaction.ts index 515ffb6..29b49c8 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -3,8 +3,8 @@ import * as bscript from './script' import * as types from './types' import * as bufferutils from './bufferutils' import { reverseBuffer } from './bufferutils' -const Buffer = require('safe-buffer').Buffer -const opcodes = require('bitcoin-ops') +import { OPS as opcodes } from './script' + const typeforce = require('typeforce') const varuint = require('varuint-bitcoin') diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index a920130..8977364 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -11,9 +11,9 @@ import * as bscript from './script' import { Payment } from './payments' import * as payments from './payments' import * as classify from './classify' -const ops = require('bitcoin-ops') +import { OPS as ops } from './script' const typeforce = require('typeforce') -const Buffer = require('safe-buffer').Buffer + const SCRIPT_TYPES = classify.types From 572fd159b0393e80bcc4bb29b5c6c3b82f691676 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 22:49:35 +0900 Subject: [PATCH 196/568] Added TypeScript standard linter to tests --- package.json | 14 +++++++++++--- src/block.ts | 2 +- src/payments/embed.ts | 4 ++-- src/payments/index.ts | 2 +- src/payments/lazy.ts | 2 +- src/payments/p2ms.ts | 18 ++++++++++-------- src/payments/p2pk.ts | 4 ++-- src/payments/p2pkh.ts | 6 +++--- src/payments/p2sh.ts | 7 +++---- src/payments/p2wpkh.ts | 6 +++--- src/payments/p2wsh.ts | 7 +++---- src/templates/nulldata.ts | 2 +- 12 files changed, 41 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 4b9557e..2d89027 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "nobuild:coverage-html": "nyc report --reporter=html", "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", "nobuild:integration": "mocha --timeout 50000 test/integration/", - "nobuild:standard": "standard", + "nobuild:standard": "standard src/**/*.ts", "nobuild:unit": "mocha", "prepare": "npm run build", "standard": "npm run build && npm run nobuild:standard", @@ -63,12 +63,20 @@ "bn.js": "^4.11.8", "bs58": "^4.0.0", "dhttp": "^3.0.0", + "eslint-plugin-typescript": "^0.14.0", "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^5.2.0", "nyc": "^11.8.0", "proxyquire": "^2.0.1", - "standard": "^11.0.1" + "standard": "^11.0.1", + "typescript-eslint-parser": "^21.0.2" }, - "license": "MIT" + "license": "MIT", + "standard": { + "parser": "typescript-eslint-parser", + "plugins": [ + "typescript" + ] + } } diff --git a/src/block.ts b/src/block.ts index 1483f2c..82700aa 100644 --- a/src/block.ts +++ b/src/block.ts @@ -124,7 +124,7 @@ export class Block { return target } - static calculateMerkleRoot (transactions: Array<Transaction>, forWitness: boolean | void): Buffer { + static calculateMerkleRoot (transactions: Array<Transaction>, forWitness?: boolean): Buffer { typeforce([{ getHash: types.Function }], transactions) if (transactions.length === 0) throw errorMerkleNoTxes if (forWitness && !txesHaveWitness(transactions)) throw errorWitnessNotSegwit diff --git a/src/payments/embed.ts b/src/payments/embed.ts index 7e82511..8cc8899 100644 --- a/src/payments/embed.ts +++ b/src/payments/embed.ts @@ -1,9 +1,9 @@ -import { Payment, PaymentOpts } from './index' +import { Payment, PaymentOpts } from './index' // eslint-disable-line import * as bscript from '../script' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -import { OPS } from '../script' +const OPS = bscript.OPS function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { if (a.length !== b.length) return false diff --git a/src/payments/index.ts b/src/payments/index.ts index 08a8409..7fc72d8 100644 --- a/src/payments/index.ts +++ b/src/payments/index.ts @@ -1,4 +1,4 @@ -import { Network } from '../networks' +import { Network } from '../networks' // eslint-disable-line import { p2data as embed } from './embed' import { p2ms } from './p2ms' import { p2pk } from './p2pk' diff --git a/src/payments/lazy.ts b/src/payments/lazy.ts index 1d4af68..a6f0951 100644 --- a/src/payments/lazy.ts +++ b/src/payments/lazy.ts @@ -18,7 +18,7 @@ export function prop (object: Object, name: string, f: ()=>any): void { }) } -export function value <T>(f: ()=>T): ()=>T { +export function value <T> (f: ()=>T): ()=>T { let value: T return function (): T { if (value !== undefined) return value diff --git a/src/payments/p2ms.ts b/src/payments/p2ms.ts index 3c5c679..67b9692 100644 --- a/src/payments/p2ms.ts +++ b/src/payments/p2ms.ts @@ -1,9 +1,9 @@ -import { Payment, PaymentOpts } from './index' +import { Payment, PaymentOpts } from './index' // eslint-disable-line import * as bscript from '../script' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' +const OPS = bscript.OPS const typef = require('typeforce') -import { OPS } from '../script' const ecc = require('tiny-secp256k1') const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 @@ -28,7 +28,9 @@ export function p2ms (a: Payment, opts?: PaymentOpts): Payment { opts = Object.assign({ validate: true }, opts || {}) function isAcceptableSignature (x: Buffer | number) { - return bscript.isCanonicalScriptSignature(<Buffer>x) || ((<PaymentOpts>opts).allowIncomplete && (<number>x === OPS.OP_0)) !== undefined + return bscript.isCanonicalScriptSignature(<Buffer>x) || + ((<PaymentOpts>opts).allowIncomplete && + (<number> x === OPS.OP_0)) !== undefined // eslint-disable-line } typef({ @@ -51,8 +53,8 @@ export function p2ms (a: Payment, opts?: PaymentOpts): Payment { if (decoded) return decoded = true chunks = <Array<Buffer | number>>bscript.decompile(output) - o.m = <number>chunks[0] - OP_INT_BASE - o.n = <number>chunks[chunks.length - 2] - OP_INT_BASE + o.m = <number> chunks[0] - OP_INT_BASE // eslint-disable-line + o.n = <number> chunks[chunks.length - 2] - OP_INT_BASE // eslint-disable-line o.pubkeys = <Array<Buffer>>chunks.slice(1, -2) } @@ -103,9 +105,9 @@ export function p2ms (a: Payment, opts?: PaymentOpts): Payment { if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid') if ( - <number>(<Payment>o).m <= 0 || - <number>(<Payment>o).n > 16 || - <number>(<Payment>o).m > <number>(<Payment>o).n || + <number>(<Payment>o).m <= 0 || // eslint-disable-line + <number>(<Payment>o).n > 16 || // eslint-disable-line + <number>(<Payment>o).m > <number>(<Payment>o).n || // eslint-disable-line o.n !== chunks.length - 3) throw new TypeError('Output is invalid') if (!(<Array<Buffer>>o.pubkeys).every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid') diff --git a/src/payments/p2pk.ts b/src/payments/p2pk.ts index f383a1c..0825955 100644 --- a/src/payments/p2pk.ts +++ b/src/payments/p2pk.ts @@ -1,9 +1,9 @@ -import { Payment, PaymentOpts } from './index' +import { Payment, PaymentOpts } from './index' // eslint-disable-line import * as bscript from '../script' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -import { OPS } from '../script' +const OPS = bscript.OPS const ecc = require('tiny-secp256k1') // input: {signature} diff --git a/src/payments/p2pkh.ts b/src/payments/p2pkh.ts index 4fb49de..f12faf8 100644 --- a/src/payments/p2pkh.ts +++ b/src/payments/p2pkh.ts @@ -1,10 +1,10 @@ -import { Payment, PaymentOpts } from './index' +import { Payment, PaymentOpts } from './index' // eslint-disable-line import * as bscript from '../script' import * as bcrypto from '../crypto' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -import { OPS } from '../script' +const OPS = bscript.OPS const ecc = require('tiny-secp256k1') const bs58check = require('bs58check') @@ -54,7 +54,7 @@ export function p2pkh (a: Payment, opts?: PaymentOpts): Payment { lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(3, 23) if (a.address) return _address().hash - if (a.pubkey || o.pubkey) return bcrypto.hash160(<Buffer>a.pubkey || <Buffer>o.pubkey) + if (a.pubkey || o.pubkey) return bcrypto.hash160(<Buffer> a.pubkey || <Buffer>o.pubkey) // eslint-disable-line }) lazy.prop(o, 'output', function () { if (!o.hash) return diff --git a/src/payments/p2sh.ts b/src/payments/p2sh.ts index d25b616..e0292f3 100644 --- a/src/payments/p2sh.ts +++ b/src/payments/p2sh.ts @@ -1,11 +1,10 @@ -import { Payment, PaymentOpts } from './index' -import { Network } from '../networks' +import { Payment, PaymentOpts } from './index' // eslint-disable-line +import { Network, bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line import * as bscript from '../script' import * as bcrypto from '../crypto' import * as lazy from './lazy' -import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -import { OPS } from '../script' +const OPS = bscript.OPS const bs58check = require('bs58check') diff --git a/src/payments/p2wpkh.ts b/src/payments/p2wpkh.ts index 918e909..b40085b 100644 --- a/src/payments/p2wpkh.ts +++ b/src/payments/p2wpkh.ts @@ -1,10 +1,10 @@ -import { Payment, PaymentOpts } from './index' +import { Payment, PaymentOpts } from './index' // eslint-disable-line import * as bscript from '../script' import * as bcrypto from '../crypto' import * as lazy from './lazy' import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -import { OPS } from '../script' +const OPS = bscript.OPS const ecc = require('tiny-secp256k1') const bech32 = require('bech32') @@ -59,7 +59,7 @@ export function p2wpkh (a: Payment, opts?: PaymentOpts): Payment { lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(2, 22) if (a.address) return _address().data - if (a.pubkey || o.pubkey) return bcrypto.hash160(<Buffer>a.pubkey || <Buffer>o.pubkey) + if (a.pubkey || o.pubkey) return bcrypto.hash160(<Buffer> a.pubkey || <Buffer>o.pubkey) // eslint-disable-line }) lazy.prop(o, 'output', function () { if (!o.hash) return diff --git a/src/payments/p2wsh.ts b/src/payments/p2wsh.ts index 1a8e86e..7e2f237 100644 --- a/src/payments/p2wsh.ts +++ b/src/payments/p2wsh.ts @@ -1,11 +1,10 @@ -import { Payment, PaymentOpts } from './index' -import { Network } from '../networks' +import { Payment, PaymentOpts } from './index' // eslint-disable-line +import { Network, bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line import * as bscript from '../script' import * as bcrypto from '../crypto' import * as lazy from './lazy' -import { bitcoin as BITCOIN_NETWORK } from '../networks' const typef = require('typeforce') -import { OPS } from '../script' +const OPS = bscript.OPS const bech32 = require('bech32') diff --git a/src/templates/nulldata.ts b/src/templates/nulldata.ts index 8889fb9..0833eac 100644 --- a/src/templates/nulldata.ts +++ b/src/templates/nulldata.ts @@ -1,6 +1,6 @@ // OP_RETURN {data} import * as bscript from '../script' -import { OPS } from '../script' +const OPS = bscript.OPS export function check (script: Buffer | Array<number | Buffer>): boolean { const buffer = bscript.compile(script) From f8427274cc7f77f5901ea732f79dd4d641adb956 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 23:00:32 +0900 Subject: [PATCH 197/568] Add a few type aliases to TransactionBuilder --- src/transaction_builder.ts | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index 8977364..b3c6ee0 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -16,31 +16,36 @@ const typeforce = require('typeforce') const SCRIPT_TYPES = classify.types +type TxbSignatures = Array<Buffer> | Array<Buffer | undefined> +type TxbPubkeys = Array<Buffer | undefined> +type TxbWitness = Array<Buffer> +type TxbScriptType = string +type TxbScript = Buffer interface TxbInput { value?: number hasWitness?: boolean - signScript?: Buffer - signType?: string - prevOutScript?: Buffer - redeemScript?: Buffer - redeemScriptType?: string - prevOutType?: string - pubkeys?: Array<Buffer | undefined> - signatures?: Array<Buffer> | Array<Buffer | undefined> - witness?: Array<Buffer> - witnessScript?: Buffer - witnessScriptType?: string - script?: Buffer + signScript?: TxbScript + signType?: TxbScriptType + prevOutScript?: TxbScript + redeemScript?: TxbScript + redeemScriptType?: TxbScriptType + prevOutType?: TxbScriptType + pubkeys?: TxbPubkeys + signatures?: TxbSignatures + witness?: TxbWitness + witnessScript?: TxbScript + witnessScriptType?: TxbScriptType + script?: TxbScript sequence?: number - scriptSig?: Buffer + scriptSig?: TxbScript maxSignatures?: number } interface TxbOutput { type: string - pubkeys?: Array<Buffer | undefined> - signatures?: Array<Buffer> | Array<Buffer | undefined> + pubkeys?: TxbPubkeys + signatures?: TxbSignatures maxSignatures?: number } From 35adaa84717d42b7f69217e88b31d00bc703ddfc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 29 Dec 2018 23:27:57 +0900 Subject: [PATCH 198/568] Add test from bugfix, also remove unnecessary arg --- src/transaction_builder.ts | 4 ++-- test/fixtures/p2ms.json | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index b3c6ee0..59fc102 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -279,7 +279,7 @@ export class TransactionBuilder { } if (!canSign(input)) { - const prepared = prepareInput(input, ourPubKey, redeemScript, witnessValue, witnessScript) + const prepared = prepareInput(input, ourPubKey, redeemScript, witnessScript) // updates inline Object.assign(input, prepared) @@ -589,7 +589,7 @@ function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { return { type } } -function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, witnessValue: number, witnessScript: Buffer): TxbInput { +function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, witnessScript: Buffer): TxbInput { if (redeemScript && witnessScript) { const p2wsh = <Payment> payments.p2wsh({ redeem: { output: witnessScript } }) const p2wshAlt = <Payment> payments.p2wsh({ output: redeemScript }) diff --git a/test/fixtures/p2ms.json b/test/fixtures/p2ms.json index 8ea033e..2f41270 100644 --- a/test/fixtures/p2ms.json +++ b/test/fixtures/p2ms.json @@ -310,6 +310,20 @@ ] } }, + { + "exception": "Signature mismatch", + "arguments": { + "m": 1, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "signatures": [ + "300602010002010001" + ], + "input": "OP_0 300602010002010101" + } + }, { "exception": "Too many signatures provided", "arguments": { From b8c2e9e339a64ea79f78c81864b25e8b69f0efcc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sun, 30 Dec 2018 11:23:28 +0900 Subject: [PATCH 199/568] Change ECPair to be compatible with payment({pubkey:ecpair.publicKey}) --- src/ecpair.ts | 16 ++++++++-------- test/ecpair.js | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ecpair.ts b/src/ecpair.ts index 321a5ed..ad28adf 100644 --- a/src/ecpair.ts +++ b/src/ecpair.ts @@ -20,8 +20,8 @@ interface ECPairOptions { export interface ECPairInterface { compressed: boolean network: Network - privateKey: Buffer | null - publicKey: Buffer | null + privateKey?: Buffer + publicKey?: Buffer toWIF(): string sign(hash: Buffer): Buffer verify(hash: Buffer, signature: Buffer): Buffer @@ -31,25 +31,25 @@ export interface ECPairInterface { class ECPair implements ECPairInterface { compressed: boolean network: Network - private __d: Buffer | null - private __Q: Buffer | null + private __d?: Buffer + private __Q?: Buffer constructor (d?: Buffer, Q?: Buffer, options?: ECPairOptions) { if (options === undefined) options = {} this.compressed = options.compressed === undefined ? true : options.compressed this.network = options.network || NETWORKS.bitcoin - this.__d = null - this.__Q = null + this.__d = undefined + this.__Q = undefined if (d !== undefined) this.__d = d if (Q !== undefined) this.__Q = ecc.pointCompress(Q, this.compressed) } - get privateKey (): Buffer | null { + get privateKey (): Buffer | undefined { return this.__d } - get publicKey (): Buffer | null { + get publicKey (): Buffer | undefined { if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed) return this.__Q } diff --git a/test/ecpair.js b/test/ecpair.js index 75a2e81..f590e18 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -30,7 +30,7 @@ describe('ECPair', function () { }) it('calls pointFromScalar lazily', hoodwink(function () { - assert.strictEqual(keyPair.__Q, null) + assert.strictEqual(keyPair.__Q, undefined) // .publicKey forces the memoization assert.strictEqual(keyPair.publicKey.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') From 63d51d1da7b04f5108c1ca86ef103e0175c151d2 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sun, 30 Dec 2018 13:34:40 +0900 Subject: [PATCH 200/568] Export some types and interfaces --- src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/index.ts b/src/index.ts index 31ef042..1db2052 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,3 +24,8 @@ export { payments, script, } + +export { Payment, PaymentOpts } from './payments' +export { Input as TxInput, Output as TxOutput } from './transaction' +export { Network } from './networks' +export { OpCode } from './script' From e7ac2b9a4ec703aca840fa0517406a5bbf1d5c38 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sun, 30 Dec 2018 15:08:29 +0900 Subject: [PATCH 201/568] pull in bip32 typescript --- package.json | 2 +- src/index.ts | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 2d89027..81761a9 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "dependencies": { "@types/node": "^10.12.18", "bech32": "^1.1.2", - "bip32": "^1.0.0", + "bip32": "git+https://github.com/junderw/bip32.git#typeScript", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", "bs58check": "^2.0.0", diff --git a/src/index.ts b/src/index.ts index 1db2052..7574048 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,31 +1,28 @@ -const bip32 = require('bip32') - -import { Block } from './block' +import * as bip32 from 'bip32' import * as ECPair from './ecpair' -import { Transaction } from './transaction' -import { TransactionBuilder } from './transaction_builder' import * as address from './address' import * as crypto from './crypto' import * as networks from './networks' import * as payments from './payments' import * as script from './script' -import { OPS as opcodes } from './script' export { - Block, ECPair, - Transaction, - TransactionBuilder, address, bip32, crypto, networks, - opcodes, payments, script, } +export { Block } from './block' +export { Transaction } from './transaction' +export { TransactionBuilder } from './transaction_builder' +export { OPS as opcodes } from './script' + export { Payment, PaymentOpts } from './payments' export { Input as TxInput, Output as TxOutput } from './transaction' export { Network } from './networks' export { OpCode } from './script' +export { BIP32Interface } from 'bip32' From 53b8f966e768e9ee69337412d23ba9d5f038bd2b Mon Sep 17 00:00:00 2001 From: "Sam (Sangho Kim)" <rlaace423@gmail.com> Date: Mon, 31 Dec 2018 12:24:27 +0900 Subject: [PATCH 202/568] Update transactions.js I think this is typo. --- test/integration/transactions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 0d88d78..f725241 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -64,7 +64,7 @@ describe('bitcoinjs-lib (transactions)', function () { txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" txb.addOutput(aliceCpkh.address, 1e4) // Alice's change - // (in)(4e4 + 2e4) - (out)(1e4 + 3e4) = (fee)2e4 = 20000, this is the miner fee + // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee // Alice signs each input with the respective private keys txb.sign(0, alice1) From f8490b6c0d89ab44f3408a6cdbd44c6da2c718ef Mon Sep 17 00:00:00 2001 From: Jonathan Underwood <junderwood@bitcoinbank.co.jp> Date: Mon, 31 Dec 2018 15:17:00 +0900 Subject: [PATCH 203/568] Update rng function In case someone copy pastes, instead of getting a dangerous key, they will get a random key, and a console error. --- test/integration/addresses.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 9ad7502..501e7ac 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -15,7 +15,14 @@ const LITECOIN = { } // deterministic RNG for testing only -function rng () { return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } +function rng (c) { + if (describe === undefined || it === undefined) { + console.error('DO NOT USE THIS rng FUNCTION OUTSIDE OF AUTOMATED TESTING!') + const randomBytes = require('randombytes') + return randomBytes(c) + } + return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') +} describe('bitcoinjs-lib (addresses)', function () { it('can generate a random address', function () { From 7d8dd860d1208ed715579a1ec4364d9f9d7aa4b2 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Tue, 1 Jan 2019 11:30:20 +1100 Subject: [PATCH 204/568] testing: use NODE_ENV instead of mocha constants --- package.json | 2 +- test/integration/addresses.js | 23 ++++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 755be28..c9ab813 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", "coverage": "nyc --check-coverage --branches 90 --functions 90 mocha", - "integration": "mocha --timeout 50000 test/integration/", + "integration": "NODE_ENV=TESTING-BITCOINJS mocha --timeout 50000 test/integration/", "standard": "standard", "test": "npm run standard && npm run coverage", "unit": "mocha" diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 76c0330..9a70172 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -14,20 +14,17 @@ const LITECOIN = { wif: 0xb0 } -// deterministic RNG for testing only -function rng (c) { - if (describe === undefined || it === undefined) { - console.error('DO NOT USE THIS rng FUNCTION OUTSIDE OF AUTOMATED TESTING!') - const randomBytes = require('randombytes') - return randomBytes(c) - } +// deterministic random number generator for TESTING ONLY +// WARNING: DO NOT USE THIS - IT IS NOT RANDOM - it produces the same private key every time for the purposes of testing. +function unsafeDeterministicRng (c) { + if (process.env.NODE_ENV !== 'TESTING-BITCOINJS') throw new Error('DO NOT USE THIS FUNCTION - IT IS NOT RANDOM - IT IS FOR TESTING ONLY - IT PRODUCES THE SAME PRIVATE KEY EVERY TIME') return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } describe('bitcoinjs-lib (addresses)', function () { it('can generate a random address', function () { - // in production: const keyPair = bitcoin.ECPair.makeRandom({}) - const keyPair = bitcoin.ECPair.makeRandom({ rng: rng }) + // const keyPair = bitcoin.ECPair.makeRandom() + const keyPair = bitcoin.ECPair.makeRandom({ rng: unsafeDeterministicRng }) const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) assert.strictEqual(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64') @@ -118,8 +115,8 @@ describe('bitcoinjs-lib (addresses)', function () { // other networks it('can generate a Testnet address', function () { const testnet = bitcoin.networks.testnet - // in production: const keyPair = bitcoin.ECPair.makeRandom({ network: testnet }) - const keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: rng }) + // const keyPair = bitcoin.ECPair.makeRandom({ network: testnet }) + const keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: unsafeDeterministicRng }) const wif = keyPair.toWIF() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: testnet }) @@ -128,8 +125,8 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can generate a Litecoin address', function () { - // in production: const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }) - const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: rng }) + // const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }) + const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: unsafeDeterministicRng }) const wif = keyPair.toWIF() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: LITECOIN }) From e5781d97b9f3a8631fdfadadd75b3fe78b244bf4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Tue, 1 Jan 2019 11:41:00 +1100 Subject: [PATCH 205/568] testing: extra warnings --- test/integration/addresses.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 9a70172..661d5b6 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -3,6 +3,17 @@ const assert = require('assert') const bitcoin = require('../../') const dhttp = require('dhttp/200') +// WARNING: DO NOT USE THIS - IT IS NOT RANDOM +// WARNING: It produces the same 'number' every time for the purposes of testing. +function unsafeDeterministicRng (c) { + if (process.env.NODE_ENV !== 'TESTING-BITCOINJS') { + throw new Error('DO NOT USE THIS FUNCTION - IT IS NOT RANDOM - IT IS FOR TESTING ONLY - IT PRODUCES THE SAME NUMBER EVERY TIME') + } + + // deterministic result for TESTING ONLY + return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') +} + const LITECOIN = { messagePrefix: '\x19Litecoin Signed Message:\n', bip32: { @@ -14,16 +25,11 @@ const LITECOIN = { wif: 0xb0 } -// deterministic random number generator for TESTING ONLY -// WARNING: DO NOT USE THIS - IT IS NOT RANDOM - it produces the same private key every time for the purposes of testing. -function unsafeDeterministicRng (c) { - if (process.env.NODE_ENV !== 'TESTING-BITCOINJS') throw new Error('DO NOT USE THIS FUNCTION - IT IS NOT RANDOM - IT IS FOR TESTING ONLY - IT PRODUCES THE SAME PRIVATE KEY EVERY TIME') - return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') -} - describe('bitcoinjs-lib (addresses)', function () { it('can generate a random address', function () { // const keyPair = bitcoin.ECPair.makeRandom() + + // WARNING: uses unsafeDeterministicRng function for testing, see warning at top of file const keyPair = bitcoin.ECPair.makeRandom({ rng: unsafeDeterministicRng }) const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) @@ -116,6 +122,8 @@ describe('bitcoinjs-lib (addresses)', function () { it('can generate a Testnet address', function () { const testnet = bitcoin.networks.testnet // const keyPair = bitcoin.ECPair.makeRandom({ network: testnet }) + + // WARNING: uses unsafeDeterministicRng function for testing, see warning at top of file const keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: unsafeDeterministicRng }) const wif = keyPair.toWIF() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: testnet }) @@ -126,6 +134,8 @@ describe('bitcoinjs-lib (addresses)', function () { it('can generate a Litecoin address', function () { // const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }) + + // WARNING: uses unsafeDeterministicRng function for testing, see warning at top of file const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: unsafeDeterministicRng }) const wif = keyPair.toWIF() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: LITECOIN }) From 88951d34dbe5834a1330282a5b7b97d6157cdc28 Mon Sep 17 00:00:00 2001 From: Jonathan Underwood <junderwood@bitcoinbank.co.jp> Date: Tue, 1 Jan 2019 14:26:34 +0900 Subject: [PATCH 206/568] Update LICENSE Happy new year! --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index fadd21b..064b885 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2011-2018 bitcoinjs-lib contributors +Copyright (c) 2011-2019 bitcoinjs-lib contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 49be171583d40eb0d4d638caf9d16df78ac3d178 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Tue, 1 Jan 2019 23:24:26 +1100 Subject: [PATCH 207/568] rm usage of unsafeDeterministicRng --- README.md | 62 +++++++++++----------- test/integration/addresses.js | 98 +++++++++++++---------------------- 2 files changed, 67 insertions(+), 93 deletions(-) diff --git a/README.md b/README.md index 9342696..b8f70f2 100644 --- a/README.md +++ b/README.md @@ -108,37 +108,37 @@ The below examples are implemented as integration tests, they should be very eas Otherwise, pull requests are appreciated. Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). -- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L22) -- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L40) -- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L47) -- [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L60) -- [Generate a SegWit P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L67) -- [Generate a SegWit 3-of-4 multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L76) -- [Generate a SegWit 2-of-2 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L90) -- [Support the retrieval of transactions for an address (3rd party blockchain)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L104) -- [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L123) -- [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js#L133) -- [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L13) -- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L28) -- [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L47) -- [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L83) -- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L105) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L143) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2WPKH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L174) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2PK input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L218) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L263) -- [Verify a Transaction signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L304) -- [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L12) -- [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L20) -- [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L31) -- [Create a BIP32, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L40) -- [Create a BIP44, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L55) -- [Create a BIP49, bitcoin testnet, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L71) -- [Use BIP39 to generate BIP32 addresses](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js#L86) -- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L43) -- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L88) -- [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L144) -- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js#L190) +- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Generate a SegWit P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Generate a SegWit 3-of-4 multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Generate a SegWit 2-of-2 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Support the retrieval of transactions for an address (3rd party blockchain)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) +- [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2WPKH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2PK input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Verify a Transaction signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) +- [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) +- [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) +- [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) +- [Create a BIP32, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) +- [Create a BIP44, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) +- [Create a BIP49, bitcoin testnet, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) +- [Use BIP39 to generate BIP32 addresses](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js) +- [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js) +- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js) If you have a use case that you feel could be listed here, please [ask for it](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new)! diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 661d5b6..51d2264 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -2,38 +2,28 @@ const { describe, it } = require('mocha') const assert = require('assert') const bitcoin = require('../../') const dhttp = require('dhttp/200') - -// WARNING: DO NOT USE THIS - IT IS NOT RANDOM -// WARNING: It produces the same 'number' every time for the purposes of testing. -function unsafeDeterministicRng (c) { - if (process.env.NODE_ENV !== 'TESTING-BITCOINJS') { - throw new Error('DO NOT USE THIS FUNCTION - IT IS NOT RANDOM - IT IS FOR TESTING ONLY - IT PRODUCES THE SAME NUMBER EVERY TIME') - } - - // deterministic result for TESTING ONLY - return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') -} - -const LITECOIN = { - messagePrefix: '\x19Litecoin Signed Message:\n', - bip32: { - public: 0x019da462, - private: 0x019d9cfe - }, - pubKeyHash: 0x30, - scriptHash: 0x32, - wif: 0xb0 -} +const TESTNET = bitcoin.networks.testnet describe('bitcoinjs-lib (addresses)', function () { - it('can generate a random address', function () { - // const keyPair = bitcoin.ECPair.makeRandom() - - // WARNING: uses unsafeDeterministicRng function for testing, see warning at top of file - const keyPair = bitcoin.ECPair.makeRandom({ rng: unsafeDeterministicRng }) + it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', function (done) { + const keyPair = bitcoin.ECPair.makeRandom() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) - assert.strictEqual(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64') + // bitcoin P2PKH addresses start with a '1' + assert.strictEqual(address.startsWith('1'), true) + + dhttp({ + method: 'GET', + url: 'https://blockchain.info/rawaddr/' + address + }, function (err, result) { + if (err) return done(err) + + // random private keys [probably!] have no transactions + assert.strictEqual(result.n_tx, 0) + assert.strictEqual(result.total_received, 0) + assert.strictEqual(result.total_sent, 0) + done() + }) }) it('can import an address via WIF', function () { @@ -100,47 +90,31 @@ describe('bitcoinjs-lib (addresses)', function () { assert.strictEqual(address, '3P4mrxQfmExfhxqjLnR2Ah4WES5EB1KBrN') }) - it('can support the retrieval of transactions for an address (via 3PBP)', function (done) { - const keyPair = bitcoin.ECPair.makeRandom() - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) - - dhttp({ - method: 'GET', - url: 'https://blockchain.info/rawaddr/' + address - }, function (err, result) { - if (err) return done(err) - - // random private keys [probably!] have no transactions - assert.strictEqual(result.n_tx, 0) - assert.strictEqual(result.total_received, 0) - assert.strictEqual(result.total_sent, 0) - done() - }) - }) - - // other networks + // examples using other network information it('can generate a Testnet address', function () { - const testnet = bitcoin.networks.testnet - // const keyPair = bitcoin.ECPair.makeRandom({ network: testnet }) + const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET }) + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: TESTNET }) - // WARNING: uses unsafeDeterministicRng function for testing, see warning at top of file - const keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: unsafeDeterministicRng }) - const wif = keyPair.toWIF() - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: testnet }) - - assert.strictEqual(address, 'mubSzQNtZfDj1YdNP6pNDuZy6zs6GDn61L') - assert.strictEqual(wif, 'cRgnQe9MUu1JznntrLaoQpB476M8PURvXVQB5R2eqms5tXnzNsrr') + // bitcoin testnet P2PKH addresses start with a 'm' + assert.strictEqual(address.startsWith('m'), true) }) it('can generate a Litecoin address', function () { - // const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }) + // WARNING: although possible, bitcoinjs is NOT necessarily compatible with Litecoin + const LITECOIN = { + messagePrefix: '\x19Litecoin Signed Message:\n', + bip32: { + public: 0x019da462, + private: 0x019d9cfe + }, + pubKeyHash: 0x30, + scriptHash: 0x32, + wif: 0xb0 + } - // WARNING: uses unsafeDeterministicRng function for testing, see warning at top of file - const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: unsafeDeterministicRng }) - const wif = keyPair.toWIF() + const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }) const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: LITECOIN }) - assert.strictEqual(address, 'LZJSxZbjqJ2XVEquqfqHg1RQTDdfST5PTn') - assert.strictEqual(wif, 'T7A4PUSgTDHecBxW1ZiYFrDNRih2o7M8Gf9xpoCgudPF9gDiNvuS') + assert.strictEqual(address.startsWith('L'), true) }) }) From c68dfc058fa984d1fb5431cc4ce4ef23a8b0cbc5 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Tue, 1 Jan 2019 23:30:10 +1100 Subject: [PATCH 208/568] testing: rm NODE_ENV --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c9ab813..755be28 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "coverage-report": "nyc report --reporter=lcov", "coverage-html": "nyc report --reporter=html", "coverage": "nyc --check-coverage --branches 90 --functions 90 mocha", - "integration": "NODE_ENV=TESTING-BITCOINJS mocha --timeout 50000 test/integration/", + "integration": "mocha --timeout 50000 test/integration/", "standard": "standard", "test": "npm run standard && npm run coverage", "unit": "mocha" From f3d918a2cbc6653247e939c367f5aafd5c48f73c Mon Sep 17 00:00:00 2001 From: Daniel Cousens <dcousens@users.noreply.github.com> Date: Tue, 1 Jan 2019 23:49:57 +1100 Subject: [PATCH 209/568] tests: use privateKey 0x000...0001 for examples --- test/integration/addresses.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 51d2264..5afc5e8 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -27,10 +27,10 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can import an address via WIF', function () { - const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) - assert.strictEqual(address, '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31') + assert.strictEqual(address, '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH') }) it('can generate a P2SH, pay-to-multisig (2-of-3) address', function () { @@ -47,19 +47,19 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can generate a SegWit address', function () { - const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) - assert.strictEqual(address, 'bc1qt97wqg464zrhnx23upykca5annqvwkwujjglky') + assert.strictEqual(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4') }) it('can generate a SegWit address (via P2SH)', function () { - const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') const { address } = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) }) - assert.strictEqual(address, '34AgLJhwXrvmkZS1o5TrcdeevMt22Nar53') + assert.strictEqual(address, '3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN') }) it('can generate a P2WSH (SegWit), pay-to-multisig (3-of-4) address', function () { From bc28949056f4cd4841c3391c06085695d2a12c8a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 4 Jan 2019 18:33:02 +0900 Subject: [PATCH 210/568] Commit js, ts, and definitions in separate folders --- .gitignore | 1 - package.json | 8 +- src/address.js | 99 +++ src/block.js | 190 +++++ src/bufferutils.js | 42 ++ src/classify.js | 75 ++ src/crypto.js | 23 + src/ecpair.js | 97 +++ src/index.js | 24 + src/networks.js | 35 + src/payments/embed.js | 52 ++ src/payments/index.js | 18 + src/payments/lazy.js | 32 + src/payments/p2ms.js | 144 ++++ src/payments/p2pk.js | 80 ++ src/payments/p2pkh.js | 144 ++++ src/payments/p2sh.js | 191 +++++ src/payments/p2wpkh.js | 145 ++++ src/payments/p2wsh.js | 178 +++++ src/script.js | 188 +++++ src/script_number.js | 60 ++ src/script_signature.js | 58 ++ src/templates/multisig/index.js | 6 + src/templates/multisig/input.js | 21 + src/templates/multisig/output.js | 34 + src/templates/nulldata.js | 14 + src/templates/pubkey/index.js | 6 + src/templates/pubkey/input.js | 11 + src/templates/pubkey/output.js | 13 + src/templates/pubkeyhash/index.js | 6 + src/templates/pubkeyhash/input.js | 12 + src/templates/pubkeyhash/output.js | 16 + src/templates/scripthash/index.js | 6 + src/templates/scripthash/input.js | 43 ++ src/templates/scripthash/output.js | 14 + src/templates/witnesscommitment/index.js | 4 + src/templates/witnesscommitment/output.js | 30 + src/templates/witnesspubkeyhash/index.js | 6 + src/templates/witnesspubkeyhash/input.js | 15 + src/templates/witnesspubkeyhash/output.js | 13 + src/templates/witnessscripthash/index.js | 6 + src/templates/witnessscripthash/input.js | 34 + src/templates/witnessscripthash/output.js | 13 + src/transaction.js | 448 +++++++++++ src/transaction_builder.js | 699 ++++++++++++++++++ src/types.js | 48 ++ test/address.js | 6 +- test/bufferutils.js | 2 +- test/classify.js | 20 +- test/crypto.js | 2 +- test/ecpair.js | 6 +- test/payments.js | 2 +- test/payments.utils.js | 4 +- test/script.js | 2 +- test/script_number.js | 2 +- test/script_signature.js | 2 +- test/transaction.js | 2 +- test/transaction_builder.js | 10 +- test/types.js | 2 +- {src => ts_src}/address.ts | 0 {src => ts_src}/block.ts | 0 {src => ts_src}/bufferutils.ts | 0 {src => ts_src}/classify.ts | 0 {src => ts_src}/crypto.ts | 0 {src => ts_src}/ecpair.ts | 0 {src => ts_src}/index.ts | 0 {src => ts_src}/networks.ts | 0 {src => ts_src}/payments/embed.ts | 0 {src => ts_src}/payments/index.ts | 0 {src => ts_src}/payments/lazy.ts | 0 {src => ts_src}/payments/p2ms.ts | 0 {src => ts_src}/payments/p2pk.ts | 0 {src => ts_src}/payments/p2pkh.ts | 0 {src => ts_src}/payments/p2sh.ts | 0 {src => ts_src}/payments/p2wpkh.ts | 0 {src => ts_src}/payments/p2wsh.ts | 0 {src => ts_src}/script.ts | 0 {src => ts_src}/script_number.ts | 0 {src => ts_src}/script_signature.ts | 0 {src => ts_src}/templates/multisig/index.ts | 0 {src => ts_src}/templates/multisig/input.ts | 0 {src => ts_src}/templates/multisig/output.ts | 0 {src => ts_src}/templates/nulldata.ts | 0 {src => ts_src}/templates/pubkey/index.ts | 0 {src => ts_src}/templates/pubkey/input.ts | 0 {src => ts_src}/templates/pubkey/output.ts | 0 {src => ts_src}/templates/pubkeyhash/index.ts | 0 {src => ts_src}/templates/pubkeyhash/input.ts | 0 .../templates/pubkeyhash/output.ts | 0 {src => ts_src}/templates/scripthash/index.ts | 0 {src => ts_src}/templates/scripthash/input.ts | 0 .../templates/scripthash/output.ts | 0 .../templates/witnesscommitment/index.ts | 0 .../templates/witnesscommitment/output.ts | 0 .../templates/witnesspubkeyhash/index.ts | 0 .../templates/witnesspubkeyhash/input.ts | 0 .../templates/witnesspubkeyhash/output.ts | 0 .../templates/witnessscripthash/index.ts | 0 .../templates/witnessscripthash/input.ts | 0 .../templates/witnessscripthash/output.ts | 0 {src => ts_src}/transaction.ts | 0 {src => ts_src}/transaction_builder.ts | 0 {src => ts_src}/types.ts | 0 tsconfig.json | 7 +- types/address.d.ts | 17 + types/block.d.ts | 27 + types/bufferutils.d.ts | 4 + types/classify.d.ts | 16 + types/crypto.d.ts | 6 + types/ecpair.d.ts | 34 + types/index.d.ts | 17 + types/networks.d.ts | 16 + types/payments/embed.d.ts | 2 + types/payments/index.d.ts | 30 + types/payments/lazy.d.ts | 2 + types/payments/p2ms.d.ts | 2 + types/payments/p2pk.d.ts | 2 + types/payments/p2pkh.d.ts | 2 + types/payments/p2sh.d.ts | 2 + types/payments/p2wpkh.d.ts | 2 + types/payments/p2wsh.d.ts | 2 + types/script.d.ts | 18 + types/script_number.d.ts | 3 + types/script_signature.d.ts | 8 + types/templates/multisig/index.d.ts | 3 + types/templates/multisig/input.d.ts | 5 + types/templates/multisig/output.d.ts | 5 + types/templates/nulldata.d.ts | 9 + types/templates/pubkey/index.d.ts | 3 + types/templates/pubkey/input.d.ts | 5 + types/templates/pubkey/output.d.ts | 5 + types/templates/pubkeyhash/index.d.ts | 3 + types/templates/pubkeyhash/input.d.ts | 5 + types/templates/pubkeyhash/output.d.ts | 5 + types/templates/scripthash/index.d.ts | 3 + types/templates/scripthash/input.d.ts | 5 + types/templates/scripthash/output.d.ts | 5 + types/templates/witnesscommitment/index.d.ts | 2 + types/templates/witnesscommitment/output.d.ts | 7 + types/templates/witnesspubkeyhash/index.d.ts | 3 + types/templates/witnesspubkeyhash/input.d.ts | 5 + types/templates/witnesspubkeyhash/output.d.ts | 5 + types/templates/witnessscripthash/index.d.ts | 3 + types/templates/witnessscripthash/input.d.ts | 5 + types/templates/witnessscripthash/output.d.ts | 5 + types/transaction.d.ts | 59 ++ types/transaction_builder.d.ts | 26 + types/types.d.ts | 25 + 148 files changed, 3850 insertions(+), 39 deletions(-) create mode 100644 src/address.js create mode 100644 src/block.js create mode 100644 src/bufferutils.js create mode 100644 src/classify.js create mode 100644 src/crypto.js create mode 100644 src/ecpair.js create mode 100644 src/index.js create mode 100644 src/networks.js create mode 100644 src/payments/embed.js create mode 100644 src/payments/index.js create mode 100644 src/payments/lazy.js create mode 100644 src/payments/p2ms.js create mode 100644 src/payments/p2pk.js create mode 100644 src/payments/p2pkh.js create mode 100644 src/payments/p2sh.js create mode 100644 src/payments/p2wpkh.js create mode 100644 src/payments/p2wsh.js create mode 100644 src/script.js create mode 100644 src/script_number.js create mode 100644 src/script_signature.js create mode 100644 src/templates/multisig/index.js create mode 100644 src/templates/multisig/input.js create mode 100644 src/templates/multisig/output.js create mode 100644 src/templates/nulldata.js create mode 100644 src/templates/pubkey/index.js create mode 100644 src/templates/pubkey/input.js create mode 100644 src/templates/pubkey/output.js create mode 100644 src/templates/pubkeyhash/index.js create mode 100644 src/templates/pubkeyhash/input.js create mode 100644 src/templates/pubkeyhash/output.js create mode 100644 src/templates/scripthash/index.js create mode 100644 src/templates/scripthash/input.js create mode 100644 src/templates/scripthash/output.js create mode 100644 src/templates/witnesscommitment/index.js create mode 100644 src/templates/witnesscommitment/output.js create mode 100644 src/templates/witnesspubkeyhash/index.js create mode 100644 src/templates/witnesspubkeyhash/input.js create mode 100644 src/templates/witnesspubkeyhash/output.js create mode 100644 src/templates/witnessscripthash/index.js create mode 100644 src/templates/witnessscripthash/input.js create mode 100644 src/templates/witnessscripthash/output.js create mode 100644 src/transaction.js create mode 100644 src/transaction_builder.js create mode 100644 src/types.js rename {src => ts_src}/address.ts (100%) rename {src => ts_src}/block.ts (100%) rename {src => ts_src}/bufferutils.ts (100%) rename {src => ts_src}/classify.ts (100%) rename {src => ts_src}/crypto.ts (100%) rename {src => ts_src}/ecpair.ts (100%) rename {src => ts_src}/index.ts (100%) rename {src => ts_src}/networks.ts (100%) rename {src => ts_src}/payments/embed.ts (100%) rename {src => ts_src}/payments/index.ts (100%) rename {src => ts_src}/payments/lazy.ts (100%) rename {src => ts_src}/payments/p2ms.ts (100%) rename {src => ts_src}/payments/p2pk.ts (100%) rename {src => ts_src}/payments/p2pkh.ts (100%) rename {src => ts_src}/payments/p2sh.ts (100%) rename {src => ts_src}/payments/p2wpkh.ts (100%) rename {src => ts_src}/payments/p2wsh.ts (100%) rename {src => ts_src}/script.ts (100%) rename {src => ts_src}/script_number.ts (100%) rename {src => ts_src}/script_signature.ts (100%) rename {src => ts_src}/templates/multisig/index.ts (100%) rename {src => ts_src}/templates/multisig/input.ts (100%) rename {src => ts_src}/templates/multisig/output.ts (100%) rename {src => ts_src}/templates/nulldata.ts (100%) rename {src => ts_src}/templates/pubkey/index.ts (100%) rename {src => ts_src}/templates/pubkey/input.ts (100%) rename {src => ts_src}/templates/pubkey/output.ts (100%) rename {src => ts_src}/templates/pubkeyhash/index.ts (100%) rename {src => ts_src}/templates/pubkeyhash/input.ts (100%) rename {src => ts_src}/templates/pubkeyhash/output.ts (100%) rename {src => ts_src}/templates/scripthash/index.ts (100%) rename {src => ts_src}/templates/scripthash/input.ts (100%) rename {src => ts_src}/templates/scripthash/output.ts (100%) rename {src => ts_src}/templates/witnesscommitment/index.ts (100%) rename {src => ts_src}/templates/witnesscommitment/output.ts (100%) rename {src => ts_src}/templates/witnesspubkeyhash/index.ts (100%) rename {src => ts_src}/templates/witnesspubkeyhash/input.ts (100%) rename {src => ts_src}/templates/witnesspubkeyhash/output.ts (100%) rename {src => ts_src}/templates/witnessscripthash/index.ts (100%) rename {src => ts_src}/templates/witnessscripthash/input.ts (100%) rename {src => ts_src}/templates/witnessscripthash/output.ts (100%) rename {src => ts_src}/transaction.ts (100%) rename {src => ts_src}/transaction_builder.ts (100%) rename {src => ts_src}/types.ts (100%) create mode 100644 types/address.d.ts create mode 100644 types/block.d.ts create mode 100644 types/bufferutils.d.ts create mode 100644 types/classify.d.ts create mode 100644 types/crypto.d.ts create mode 100644 types/ecpair.d.ts create mode 100644 types/index.d.ts create mode 100644 types/networks.d.ts create mode 100644 types/payments/embed.d.ts create mode 100644 types/payments/index.d.ts create mode 100644 types/payments/lazy.d.ts create mode 100644 types/payments/p2ms.d.ts create mode 100644 types/payments/p2pk.d.ts create mode 100644 types/payments/p2pkh.d.ts create mode 100644 types/payments/p2sh.d.ts create mode 100644 types/payments/p2wpkh.d.ts create mode 100644 types/payments/p2wsh.d.ts create mode 100644 types/script.d.ts create mode 100644 types/script_number.d.ts create mode 100644 types/script_signature.d.ts create mode 100644 types/templates/multisig/index.d.ts create mode 100644 types/templates/multisig/input.d.ts create mode 100644 types/templates/multisig/output.d.ts create mode 100644 types/templates/nulldata.d.ts create mode 100644 types/templates/pubkey/index.d.ts create mode 100644 types/templates/pubkey/input.d.ts create mode 100644 types/templates/pubkey/output.d.ts create mode 100644 types/templates/pubkeyhash/index.d.ts create mode 100644 types/templates/pubkeyhash/input.d.ts create mode 100644 types/templates/pubkeyhash/output.d.ts create mode 100644 types/templates/scripthash/index.d.ts create mode 100644 types/templates/scripthash/input.d.ts create mode 100644 types/templates/scripthash/output.d.ts create mode 100644 types/templates/witnesscommitment/index.d.ts create mode 100644 types/templates/witnesscommitment/output.d.ts create mode 100644 types/templates/witnesspubkeyhash/index.d.ts create mode 100644 types/templates/witnesspubkeyhash/input.d.ts create mode 100644 types/templates/witnesspubkeyhash/output.d.ts create mode 100644 types/templates/witnessscripthash/index.d.ts create mode 100644 types/templates/witnessscripthash/input.d.ts create mode 100644 types/templates/witnessscripthash/output.d.ts create mode 100644 types/transaction.d.ts create mode 100644 types/transaction_builder.d.ts create mode 100644 types/types.d.ts diff --git a/.gitignore b/.gitignore index 3d940fb..a6c0ab8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ coverage -dist node_modules .nyc_output npm-debug.log diff --git a/package.json b/package.json index 81761a9..c00414a 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "bitcoinjs-lib", "version": "4.0.2", "description": "Client-side Bitcoin JavaScript library", - "main": "./dist/src/index.js", - "types": "./dist/src/index.d.ts", + "main": "./src/index.js", + "types": "./types/index.d.ts", "engines": { "node": ">=8.0.0" }, @@ -24,7 +24,7 @@ "nobuild:coverage-html": "nyc report --reporter=html", "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", "nobuild:integration": "mocha --timeout 50000 test/integration/", - "nobuild:standard": "standard src/**/*.ts", + "nobuild:standard": "standard ts_src/**/*.ts", "nobuild:unit": "mocha", "prepare": "npm run build", "standard": "npm run build && npm run nobuild:standard", @@ -36,7 +36,7 @@ "url": "https://github.com/bitcoinjs/bitcoinjs-lib.git" }, "files": [ - "dist/src" + "src" ], "dependencies": { "@types/node": "^10.12.18", diff --git a/src/address.js b/src/address.js new file mode 100644 index 0000000..ed11e5c --- /dev/null +++ b/src/address.js @@ -0,0 +1,99 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const types = require("./types"); +const bscript = require("./script"); +const networks = require("./networks"); +const payments = require("./payments"); +const bech32 = require('bech32'); +const bs58check = require('bs58check'); +const typeforce = require('typeforce'); +function fromBase58Check(address) { + const payload = bs58check.decode(address); + // TODO: 4.0.0, move to "toOutputScript" + if (payload.length < 21) + throw new TypeError(address + ' is too short'); + if (payload.length > 21) + throw new TypeError(address + ' is too long'); + const version = payload.readUInt8(0); + const hash = payload.slice(1); + return { version: version, hash: hash }; +} +exports.fromBase58Check = fromBase58Check; +function fromBech32(address) { + const result = bech32.decode(address); + const data = bech32.fromWords(result.words.slice(1)); + return { + version: result.words[0], + prefix: result.prefix, + data: Buffer.from(data) + }; +} +exports.fromBech32 = fromBech32; +function toBase58Check(hash, version) { + typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); + const payload = Buffer.allocUnsafe(21); + payload.writeUInt8(version, 0); + hash.copy(payload, 1); + return bs58check.encode(payload); +} +exports.toBase58Check = toBase58Check; +function toBech32(data, version, prefix) { + const words = bech32.toWords(data); + words.unshift(version); + return bech32.encode(prefix, words); +} +exports.toBech32 = toBech32; +function fromOutputScript(output, network) { + network = network || networks.bitcoin; + try { + return payments.p2pkh({ output, network }).address; + } + catch (e) { } + try { + return payments.p2sh({ output, network }).address; + } + catch (e) { } + try { + return payments.p2wpkh({ output, network }).address; + } + catch (e) { } + try { + return payments.p2wsh({ output, network }).address; + } + catch (e) { } + throw new Error(bscript.toASM(output) + ' has no matching Address'); +} +exports.fromOutputScript = fromOutputScript; +function toOutputScript(address, network) { + network = network || networks.bitcoin; + let decodeBase58 = undefined; + let decodeBech32 = undefined; + try { + decodeBase58 = fromBase58Check(address); + } + catch (e) { } + if (decodeBase58) { + if (decodeBase58.version === network.pubKeyHash) + return payments.p2pkh({ hash: decodeBase58.hash }).output; + if (decodeBase58.version === network.scriptHash) + return payments.p2sh({ hash: decodeBase58.hash }).output; + } + else { + try { + decodeBech32 = fromBech32(address); + } + catch (e) { } + if (decodeBech32) { + if (decodeBech32.prefix !== network.bech32) + throw new Error(address + ' has an invalid prefix'); + if (decodeBech32.version === 0) { + if (decodeBech32.data.length === 20) + return payments.p2wpkh({ hash: decodeBech32.data }).output; + if (decodeBech32.data.length === 32) + return payments.p2wsh({ hash: decodeBech32.data }).output; + } + } + } + throw new Error(address + ' has no matching Script'); +} +exports.toOutputScript = toOutputScript; diff --git a/src/block.js b/src/block.js new file mode 100644 index 0000000..5b4776f --- /dev/null +++ b/src/block.js @@ -0,0 +1,190 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const transaction_1 = require("./transaction"); +const types = require("./types"); +const bcrypto = require("./crypto"); +const bufferutils_1 = require("./bufferutils"); +const fastMerkleRoot = require('merkle-lib/fastRoot'); +const typeforce = require('typeforce'); +const varuint = require('varuint-bitcoin'); +const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions'); +const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block'); +const errorBufferTooSmall = new Error('Buffer too small (< 80 bytes)'); +function txesHaveWitness(transactions) { + return transactions !== undefined && + transactions instanceof Array && + transactions[0] && + transactions[0].ins && + transactions[0].ins instanceof Array && + transactions[0].ins[0] && + transactions[0].ins[0].witness && + transactions[0].ins[0].witness instanceof Array && + transactions[0].ins[0].witness.length > 0; +} +class Block { + constructor() { + this.version = 1; + this.timestamp = 0; + this.bits = 0; + this.nonce = 0; + this.prevHash = undefined; + this.merkleRoot = undefined; + this.witnessCommit = undefined; + this.transactions = undefined; + } + static fromBuffer(buffer) { + if (buffer.length < 80) + throw errorBufferTooSmall; + let offset = 0; + const readSlice = (n) => { + offset += n; + return buffer.slice(offset - n, offset); + }; + const readUInt32 = () => { + const i = buffer.readUInt32LE(offset); + offset += 4; + return i; + }; + const readInt32 = () => { + const i = buffer.readInt32LE(offset); + offset += 4; + return i; + }; + const block = new Block(); + block.version = readInt32(); + block.prevHash = readSlice(32); + block.merkleRoot = readSlice(32); + block.timestamp = readUInt32(); + block.bits = readUInt32(); + block.nonce = readUInt32(); + if (buffer.length === 80) + return block; + const readVarInt = () => { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + }; + const readTransaction = () => { + const tx = transaction_1.Transaction.fromBuffer(buffer.slice(offset), true); + offset += tx.byteLength(); + return tx; + }; + const nTransactions = readVarInt(); + block.transactions = []; + for (var i = 0; i < nTransactions; ++i) { + const tx = readTransaction(); + block.transactions.push(tx); + } + // This Block contains a witness commit + if (block.hasWitnessCommit()) { + // The merkle root for the witness data is in an OP_RETURN output. + // There is no rule for the index of the output, so use filter to find it. + // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed + // If multiple commits are found, the output with highest index is assumed. + let witnessCommits = block.transactions[0].outs + .filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))) + .map(out => out.script.slice(6, 38)); + // Use the commit with the highest output (should only be one though) + block.witnessCommit = witnessCommits[witnessCommits.length - 1]; + } + return block; + } + static fromHex(hex) { + return Block.fromBuffer(Buffer.from(hex, 'hex')); + } + static calculateTarget(bits) { + const exponent = ((bits & 0xff000000) >> 24) - 3; + const mantissa = bits & 0x007fffff; + const target = Buffer.alloc(32, 0); + target.writeUIntBE(mantissa, 29 - exponent, 3); + return target; + } + static calculateMerkleRoot(transactions, forWitness) { + typeforce([{ getHash: types.Function }], transactions); + if (transactions.length === 0) + throw errorMerkleNoTxes; + if (forWitness && !txesHaveWitness(transactions)) + throw errorWitnessNotSegwit; + const hashes = transactions.map(transaction => transaction.getHash(forWitness)); + const rootHash = fastMerkleRoot(hashes, bcrypto.hash256); + return forWitness + ? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) + : rootHash; + } + hasWitnessCommit() { + return txesHaveWitness(this.transactions); + } + byteLength(headersOnly) { + if (headersOnly || !this.transactions) + return 80; + return 80 + varuint.encodingLength(this.transactions.length) + + this.transactions.reduce((a, x) => a + x.byteLength(), 0); + } + getHash() { + return bcrypto.hash256(this.toBuffer(true)); + } + getId() { + return bufferutils_1.reverseBuffer(this.getHash()).toString('hex'); + } + getUTCDate() { + const date = new Date(0); // epoch + date.setUTCSeconds(this.timestamp); + return date; + } + // TODO: buffer, offset compatibility + toBuffer(headersOnly) { + const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); + let offset = 0; + const writeSlice = (slice) => { + slice.copy(buffer, offset); + offset += slice.length; + }; + const writeInt32 = (i) => { + buffer.writeInt32LE(i, offset); + offset += 4; + }; + const writeUInt32 = (i) => { + buffer.writeUInt32LE(i, offset); + offset += 4; + }; + writeInt32(this.version); + writeSlice(this.prevHash); + writeSlice(this.merkleRoot); + writeUInt32(this.timestamp); + writeUInt32(this.bits); + writeUInt32(this.nonce); + if (headersOnly || !this.transactions) + return buffer; + varuint.encode(this.transactions.length, buffer, offset); + offset += varuint.encode.bytes; + this.transactions.forEach(tx => { + const txSize = tx.byteLength(); // TODO: extract from toBuffer? + tx.toBuffer(buffer, offset); + offset += txSize; + }); + return buffer; + } + toHex(headersOnly) { + return this.toBuffer(headersOnly).toString('hex'); + } + checkMerkleRoot() { + if (!this.transactions) + throw errorMerkleNoTxes; + const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions); + return this.merkleRoot.compare(actualMerkleRoot) === 0; + } + checkWitnessCommit() { + if (!this.transactions) + throw errorMerkleNoTxes; + if (!this.hasWitnessCommit()) + throw errorWitnessNotSegwit; + const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true); + return this.witnessCommit.compare(actualWitnessCommit) === 0; + } + checkProofOfWork() { + const hash = bufferutils_1.reverseBuffer(this.getHash()); + const target = Block.calculateTarget(this.bits); + return hash.compare(target) <= 0; + } +} +exports.Block = Block; diff --git a/src/bufferutils.js b/src/bufferutils.js new file mode 100644 index 0000000..f768250 --- /dev/null +++ b/src/bufferutils.js @@ -0,0 +1,42 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +// https://github.com/feross/buffer/blob/master/index.js#L1127 +function verifuint(value, max) { + if (typeof value !== 'number') + throw new Error('cannot write a non-number as a number'); + if (value < 0) + throw new Error('specified a negative value for writing an unsigned value'); + if (value > max) + throw new Error('RangeError: value out of range'); + if (Math.floor(value) !== value) + throw new Error('value has a fractional component'); +} +function readUInt64LE(buffer, offset) { + const a = buffer.readUInt32LE(offset); + let b = buffer.readUInt32LE(offset + 4); + b *= 0x100000000; + verifuint(b + a, 0x001fffffffffffff); + return b + a; +} +exports.readUInt64LE = readUInt64LE; +function writeUInt64LE(buffer, value, offset) { + verifuint(value, 0x001fffffffffffff); + buffer.writeInt32LE(value & -1, offset); + buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); + return offset + 8; +} +exports.writeUInt64LE = writeUInt64LE; +function reverseBuffer(buffer) { + if (buffer.length < 1) + return buffer; + let j = buffer.length - 1; + let tmp = 0; + for (let i = 0; i < buffer.length / 2; i++) { + tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + j--; + } + return buffer; +} +exports.reverseBuffer = reverseBuffer; diff --git a/src/classify.js b/src/classify.js new file mode 100644 index 0000000..a2109c2 --- /dev/null +++ b/src/classify.js @@ -0,0 +1,75 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const script_1 = require("./script"); +const multisig = require("./templates/multisig"); +const nullData = require("./templates/nulldata"); +const pubKey = require("./templates/pubkey"); +const pubKeyHash = require("./templates/pubkeyhash"); +const scriptHash = require("./templates/scripthash"); +const witnessPubKeyHash = require("./templates/witnesspubkeyhash"); +const witnessScriptHash = require("./templates/witnessscripthash"); +const witnessCommitment = require("./templates/witnesscommitment"); +const types = { + P2MS: 'multisig', + NONSTANDARD: 'nonstandard', + NULLDATA: 'nulldata', + P2PK: 'pubkey', + P2PKH: 'pubkeyhash', + P2SH: 'scripthash', + P2WPKH: 'witnesspubkeyhash', + P2WSH: 'witnessscripthash', + WITNESS_COMMITMENT: 'witnesscommitment' +}; +exports.types = types; +function classifyOutput(script) { + if (witnessPubKeyHash.output.check(script)) + return types.P2WPKH; + if (witnessScriptHash.output.check(script)) + return types.P2WSH; + if (pubKeyHash.output.check(script)) + return types.P2PKH; + if (scriptHash.output.check(script)) + return types.P2SH; + // XXX: optimization, below functions .decompile before use + const chunks = script_1.decompile(script); + if (!chunks) + throw new TypeError('Invalid script'); + if (multisig.output.check(chunks)) + return types.P2MS; + if (pubKey.output.check(chunks)) + return types.P2PK; + if (witnessCommitment.output.check(chunks)) + return types.WITNESS_COMMITMENT; + if (nullData.output.check(chunks)) + return types.NULLDATA; + return types.NONSTANDARD; +} +exports.output = classifyOutput; +function classifyInput(script, allowIncomplete) { + // XXX: optimization, below functions .decompile before use + const chunks = script_1.decompile(script); + if (!chunks) + throw new TypeError('Invalid script'); + if (pubKeyHash.input.check(chunks)) + return types.P2PKH; + if (scriptHash.input.check(chunks, allowIncomplete)) + return types.P2SH; + if (multisig.input.check(chunks, allowIncomplete)) + return types.P2MS; + if (pubKey.input.check(chunks)) + return types.P2PK; + return types.NONSTANDARD; +} +exports.input = classifyInput; +function classifyWitness(script, allowIncomplete) { + // XXX: optimization, below functions .decompile before use + const chunks = script_1.decompile(script); + if (!chunks) + throw new TypeError('Invalid script'); + if (witnessPubKeyHash.input.check(chunks)) + return types.P2WPKH; + if (witnessScriptHash.input.check(chunks, allowIncomplete)) + return types.P2WSH; + return types.NONSTANDARD; +} +exports.witness = classifyWitness; diff --git a/src/crypto.js b/src/crypto.js new file mode 100644 index 0000000..4165d30 --- /dev/null +++ b/src/crypto.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const createHash = require('create-hash'); +function ripemd160(buffer) { + return createHash('rmd160').update(buffer).digest(); +} +exports.ripemd160 = ripemd160; +function sha1(buffer) { + return createHash('sha1').update(buffer).digest(); +} +exports.sha1 = sha1; +function sha256(buffer) { + return createHash('sha256').update(buffer).digest(); +} +exports.sha256 = sha256; +function hash160(buffer) { + return ripemd160(sha256(buffer)); +} +exports.hash160 = hash160; +function hash256(buffer) { + return sha256(sha256(buffer)); +} +exports.hash256 = hash256; diff --git a/src/ecpair.js b/src/ecpair.js new file mode 100644 index 0000000..d4dc94f --- /dev/null +++ b/src/ecpair.js @@ -0,0 +1,97 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const NETWORKS = require("./networks"); +const types = require("./types"); +const ecc = require('tiny-secp256k1'); +const randomBytes = require('randombytes'); +const typeforce = require('typeforce'); +const wif = require('wif'); +const isOptions = typeforce.maybe(typeforce.compile({ + compressed: types.maybe(types.Boolean), + network: types.maybe(types.Network) +})); +class ECPair { + constructor(d, Q, options) { + if (options === undefined) + options = {}; + this.compressed = options.compressed === undefined ? true : options.compressed; + this.network = options.network || NETWORKS.bitcoin; + this.__d = undefined; + this.__Q = undefined; + if (d !== undefined) + this.__d = d; + if (Q !== undefined) + this.__Q = ecc.pointCompress(Q, this.compressed); + } + get privateKey() { + return this.__d; + } + get publicKey() { + if (!this.__Q) + this.__Q = ecc.pointFromScalar(this.__d, this.compressed); + return this.__Q; + } + toWIF() { + if (!this.__d) + throw new Error('Missing private key'); + return wif.encode(this.network.wif, this.__d, this.compressed); + } + sign(hash) { + if (!this.__d) + throw new Error('Missing private key'); + return ecc.sign(hash, this.__d); + } + verify(hash, signature) { + return ecc.verify(hash, this.publicKey, signature); + } +} +function fromPrivateKey(buffer, options) { + typeforce(types.Buffer256bit, buffer); + if (!ecc.isPrivate(buffer)) + throw new TypeError('Private key not in range [1, n)'); + typeforce(isOptions, options); + return new ECPair(buffer, undefined, options); +} +exports.fromPrivateKey = fromPrivateKey; +function fromPublicKey(buffer, options) { + typeforce(ecc.isPoint, buffer); + typeforce(isOptions, options); + return new ECPair(undefined, buffer, options); +} +exports.fromPublicKey = fromPublicKey; +function fromWIF(string, network) { + const decoded = wif.decode(string); + const version = decoded.version; + // list of networks? + if (types.Array(network)) { + network = network.filter(function (x) { + return version === x.wif; + }).pop(); + if (!network) + throw new Error('Unknown network version'); + // otherwise, assume a network object (or default to bitcoin) + } + else { + network = network || NETWORKS.bitcoin; + if (version !== network.wif) + throw new Error('Invalid network version'); + } + return fromPrivateKey(decoded.privateKey, { + compressed: decoded.compressed, + network: network + }); +} +exports.fromWIF = fromWIF; +function makeRandom(options) { + typeforce(isOptions, options); + if (options === undefined) + options = {}; + const rng = options.rng || randomBytes; + let d; + do { + d = rng(32); + typeforce(types.Buffer256bit, d); + } while (!ecc.isPrivate(d)); + return fromPrivateKey(d, options); +} +exports.makeRandom = makeRandom; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..7455d4a --- /dev/null +++ b/src/index.js @@ -0,0 +1,24 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const bip32 = require("bip32"); +exports.bip32 = bip32; +const ECPair = require("./ecpair"); +exports.ECPair = ECPair; +const address = require("./address"); +exports.address = address; +const crypto = require("./crypto"); +exports.crypto = crypto; +const networks = require("./networks"); +exports.networks = networks; +const payments = require("./payments"); +exports.payments = payments; +const script = require("./script"); +exports.script = script; +var block_1 = require("./block"); +exports.Block = block_1.Block; +var transaction_1 = require("./transaction"); +exports.Transaction = transaction_1.Transaction; +var transaction_builder_1 = require("./transaction_builder"); +exports.TransactionBuilder = transaction_builder_1.TransactionBuilder; +var script_1 = require("./script"); +exports.opcodes = script_1.OPS; diff --git a/src/networks.js b/src/networks.js new file mode 100644 index 0000000..821cd96 --- /dev/null +++ b/src/networks.js @@ -0,0 +1,35 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.bitcoin = { + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'bc', + bip32: { + public: 0x0488b21e, + private: 0x0488ade4 + }, + pubKeyHash: 0x00, + scriptHash: 0x05, + wif: 0x80 +}; +exports.regtest = { + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'bcrt', + bip32: { + public: 0x043587cf, + private: 0x04358394 + }, + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef +}; +exports.testnet = { + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'tb', + bip32: { + public: 0x043587cf, + private: 0x04358394 + }, + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef +}; diff --git a/src/payments/embed.js b/src/payments/embed.js new file mode 100644 index 0000000..7d341ab --- /dev/null +++ b/src/payments/embed.js @@ -0,0 +1,52 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../script"); +const lazy = require("./lazy"); +const networks_1 = require("../networks"); +const typef = require('typeforce'); +const OPS = bscript.OPS; +function stacksEqual(a, b) { + if (a.length !== b.length) + return false; + return a.every(function (x, i) { + return x.equals(b[i]); + }); +} +// output: OP_RETURN ... +function p2data(a, opts) { + if (!a.data && + !a.output) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef({ + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + data: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a); + const network = a.network || networks_1.bitcoin; + const o = { network }; + lazy.prop(o, 'output', function () { + if (!a.data) + return; + return bscript.compile([OPS.OP_RETURN].concat(a.data)); + }); + lazy.prop(o, 'data', function () { + if (!a.output) + return; + return bscript.decompile(a.output).slice(1); + }); + // extended validation + if (opts.validate) { + if (a.output) { + const chunks = bscript.decompile(a.output); + if (chunks[0] !== OPS.OP_RETURN) + throw new TypeError('Output is invalid'); + if (!chunks.slice(1).every(typef.Buffer)) + throw new TypeError('Output is invalid'); + if (a.data && !stacksEqual(a.data, o.data)) + throw new TypeError('Data mismatch'); + } + } + return Object.assign(o, a); +} +exports.p2data = p2data; diff --git a/src/payments/index.js b/src/payments/index.js new file mode 100644 index 0000000..f21762d --- /dev/null +++ b/src/payments/index.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const embed_1 = require("./embed"); +exports.embed = embed_1.p2data; +const p2ms_1 = require("./p2ms"); +exports.p2ms = p2ms_1.p2ms; +const p2pk_1 = require("./p2pk"); +exports.p2pk = p2pk_1.p2pk; +const p2pkh_1 = require("./p2pkh"); +exports.p2pkh = p2pkh_1.p2pkh; +const p2sh_1 = require("./p2sh"); +exports.p2sh = p2sh_1.p2sh; +const p2wpkh_1 = require("./p2wpkh"); +exports.p2wpkh = p2wpkh_1.p2wpkh; +const p2wsh_1 = require("./p2wsh"); +exports.p2wsh = p2wsh_1.p2wsh; +// TODO +// witness commitment diff --git a/src/payments/lazy.js b/src/payments/lazy.js new file mode 100644 index 0000000..2c848e1 --- /dev/null +++ b/src/payments/lazy.js @@ -0,0 +1,32 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function prop(object, name, f) { + Object.defineProperty(object, name, { + configurable: true, + enumerable: true, + get: function () { + let value = f.call(this); + this[name] = value; + return value; + }, + set: function (value) { + Object.defineProperty(this, name, { + configurable: true, + enumerable: true, + value: value, + writable: true + }); + } + }); +} +exports.prop = prop; +function value(f) { + let value; + return function () { + if (value !== undefined) + return value; + value = f(); + return value; + }; +} +exports.value = value; diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js new file mode 100644 index 0000000..a45bd48 --- /dev/null +++ b/src/payments/p2ms.js @@ -0,0 +1,144 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../script"); +const lazy = require("./lazy"); +const networks_1 = require("../networks"); +const OPS = bscript.OPS; +const typef = require('typeforce'); +const ecc = require('tiny-secp256k1'); +const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 +function stacksEqual(a, b) { + if (a.length !== b.length) + return false; + return a.every(function (x, i) { + return x.equals(b[i]); + }); +} +// input: OP_0 [signatures ...] +// output: m [pubKeys ...] n OP_CHECKMULTISIG +function p2ms(a, opts) { + if (!a.input && + !a.output && + !(a.pubkeys && a.m !== undefined) && + !a.signatures) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + function isAcceptableSignature(x) { + return bscript.isCanonicalScriptSignature(x) || + (opts.allowIncomplete && + (x === OPS.OP_0)) !== undefined; // eslint-disable-line + } + typef({ + network: typef.maybe(typef.Object), + m: typef.maybe(typef.Number), + n: typef.maybe(typef.Number), + output: typef.maybe(typef.Buffer), + pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), + signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), + input: typef.maybe(typef.Buffer) + }, a); + const network = a.network || networks_1.bitcoin; + const o = { network }; + let chunks = []; + let decoded = false; + function decode(output) { + if (decoded) + return; + decoded = true; + chunks = bscript.decompile(output); + o.m = chunks[0] - OP_INT_BASE; // eslint-disable-line + o.n = chunks[chunks.length - 2] - OP_INT_BASE; // eslint-disable-line + o.pubkeys = chunks.slice(1, -2); + } + lazy.prop(o, 'output', function () { + if (!a.m) + return; + if (!o.n) + return; + if (!a.pubkeys) + return; + return bscript.compile([].concat(OP_INT_BASE + a.m, a.pubkeys, OP_INT_BASE + o.n, OPS.OP_CHECKMULTISIG)); + }); + lazy.prop(o, 'm', function () { + if (!o.output) + return; + decode(o.output); + return o.m; + }); + lazy.prop(o, 'n', function () { + if (!o.pubkeys) + return; + return o.pubkeys.length; + }); + lazy.prop(o, 'pubkeys', function () { + if (!a.output) + return; + decode(a.output); + return o.pubkeys; + }); + lazy.prop(o, 'signatures', function () { + if (!a.input) + return; + return bscript.decompile(a.input).slice(1); + }); + lazy.prop(o, 'input', function () { + if (!a.signatures) + return; + return bscript.compile([OPS.OP_0].concat(a.signatures)); + }); + lazy.prop(o, 'witness', function () { + if (!o.input) + return; + return []; + }); + // extended validation + if (opts.validate) { + if (a.output) { + decode(a.output); + if (!typef.Number(chunks[0])) + throw new TypeError('Output is invalid'); + if (!typef.Number(chunks[chunks.length - 2])) + throw new TypeError('Output is invalid'); + if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) + throw new TypeError('Output is invalid'); + if (o.m <= 0 || // eslint-disable-line + o.n > 16 || // eslint-disable-line + o.m > o.n || // eslint-disable-line + o.n !== chunks.length - 3) + throw new TypeError('Output is invalid'); + if (!o.pubkeys.every(x => ecc.isPoint(x))) + throw new TypeError('Output is invalid'); + if (a.m !== undefined && a.m !== o.m) + throw new TypeError('m mismatch'); + if (a.n !== undefined && a.n !== o.n) + throw new TypeError('n mismatch'); + if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys)) + throw new TypeError('Pubkeys mismatch'); + } + if (a.pubkeys) { + if (a.n !== undefined && a.n !== a.pubkeys.length) + throw new TypeError('Pubkey count mismatch'); + o.n = a.pubkeys.length; + if (o.n < o.m) + throw new TypeError('Pubkey count cannot be less than m'); + } + if (a.signatures) { + if (a.signatures.length < o.m) + throw new TypeError('Not enough signatures provided'); + if (a.signatures.length > o.m) + throw new TypeError('Too many signatures provided'); + } + if (a.input) { + if (a.input[0] !== OPS.OP_0) + throw new TypeError('Input is invalid'); + if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) + throw new TypeError('Input has invalid signature(s)'); + if (a.signatures && !stacksEqual(a.signatures, o.signatures)) + throw new TypeError('Signature mismatch'); + if (a.m !== undefined && a.m !== a.signatures.length) + throw new TypeError('Signature count mismatch'); + } + } + return Object.assign(o, a); +} +exports.p2ms = p2ms; diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js new file mode 100644 index 0000000..ab8654d --- /dev/null +++ b/src/payments/p2pk.js @@ -0,0 +1,80 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../script"); +const lazy = require("./lazy"); +const networks_1 = require("../networks"); +const typef = require('typeforce'); +const OPS = bscript.OPS; +const ecc = require('tiny-secp256k1'); +// input: {signature} +// output: {pubKey} OP_CHECKSIG +function p2pk(a, opts) { + if (!a.input && + !a.output && + !a.pubkey && + !a.input && + !a.signature) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef({ + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + input: typef.maybe(typef.Buffer) + }, a); + const _chunks = lazy.value(function () { return bscript.decompile(a.input); }); + const network = a.network || networks_1.bitcoin; + const o = { network }; + lazy.prop(o, 'output', function () { + if (!a.pubkey) + return; + return bscript.compile([ + a.pubkey, + OPS.OP_CHECKSIG + ]); + }); + lazy.prop(o, 'pubkey', function () { + if (!a.output) + return; + return a.output.slice(1, -1); + }); + lazy.prop(o, 'signature', function () { + if (!a.input) + return; + return _chunks()[0]; + }); + lazy.prop(o, 'input', function () { + if (!a.signature) + return; + return bscript.compile([a.signature]); + }); + lazy.prop(o, 'witness', function () { + if (!o.input) + return; + return []; + }); + // extended validation + if (opts.validate) { + if (a.output) { + if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) + throw new TypeError('Output is invalid'); + if (!ecc.isPoint(o.pubkey)) + throw new TypeError('Output pubkey is invalid'); + if (a.pubkey && !a.pubkey.equals(o.pubkey)) + throw new TypeError('Pubkey mismatch'); + } + if (a.signature) { + if (a.input && !a.input.equals(o.input)) + throw new TypeError('Signature mismatch'); + } + if (a.input) { + if (_chunks().length !== 1) + throw new TypeError('Input is invalid'); + if (!bscript.isCanonicalScriptSignature(o.signature)) + throw new TypeError('Input has invalid signature'); + } + } + return Object.assign(o, a); +} +exports.p2pk = p2pk; diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js new file mode 100644 index 0000000..443464a --- /dev/null +++ b/src/payments/p2pkh.js @@ -0,0 +1,144 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../script"); +const bcrypto = require("../crypto"); +const lazy = require("./lazy"); +const networks_1 = require("../networks"); +const typef = require('typeforce'); +const OPS = bscript.OPS; +const ecc = require('tiny-secp256k1'); +const bs58check = require('bs58check'); +// input: {signature} {pubkey} +// output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG +function p2pkh(a, opts) { + if (!a.address && + !a.hash && + !a.output && + !a.pubkey && + !a.input) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef({ + network: typef.maybe(typef.Object), + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + output: typef.maybe(typef.BufferN(25)), + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + input: typef.maybe(typef.Buffer) + }, a); + const _address = lazy.value(function () { + const payload = bs58check.decode(a.address); + const version = payload.readUInt8(0); + const hash = payload.slice(1); + return { version, hash }; + }); + const _chunks = lazy.value(function () { return bscript.decompile(a.input); }); + const network = a.network || networks_1.bitcoin; + const o = { network }; + lazy.prop(o, 'address', function () { + if (!o.hash) + return; + const payload = Buffer.allocUnsafe(21); + payload.writeUInt8(network.pubKeyHash, 0); + o.hash.copy(payload, 1); + return bs58check.encode(payload); + }); + lazy.prop(o, 'hash', function () { + if (a.output) + return a.output.slice(3, 23); + if (a.address) + return _address().hash; + if (a.pubkey || o.pubkey) + return bcrypto.hash160(a.pubkey || o.pubkey); // eslint-disable-line + }); + lazy.prop(o, 'output', function () { + if (!o.hash) + return; + return bscript.compile([ + OPS.OP_DUP, + OPS.OP_HASH160, + o.hash, + OPS.OP_EQUALVERIFY, + OPS.OP_CHECKSIG + ]); + }); + lazy.prop(o, 'pubkey', function () { + if (!a.input) + return; + return _chunks()[1]; + }); + lazy.prop(o, 'signature', function () { + if (!a.input) + return; + return _chunks()[0]; + }); + lazy.prop(o, 'input', function () { + if (!a.pubkey) + return; + if (!a.signature) + return; + return bscript.compile([a.signature, a.pubkey]); + }); + lazy.prop(o, 'witness', function () { + if (!o.input) + return; + return []; + }); + // extended validation + if (opts.validate) { + let hash = Buffer.from([]); + if (a.address) { + if (_address().version !== network.pubKeyHash) + throw new TypeError('Invalid version or Network mismatch'); + if (_address().hash.length !== 20) + throw new TypeError('Invalid address'); + hash = _address().hash; + } + if (a.hash) { + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else + hash = a.hash; + } + if (a.output) { + if (a.output.length !== 25 || + a.output[0] !== OPS.OP_DUP || + a.output[1] !== OPS.OP_HASH160 || + a.output[2] !== 0x14 || + a.output[23] !== OPS.OP_EQUALVERIFY || + a.output[24] !== OPS.OP_CHECKSIG) + throw new TypeError('Output is invalid'); + const hash2 = a.output.slice(3, 23); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else + hash = hash2; + } + if (a.pubkey) { + const pkh = bcrypto.hash160(a.pubkey); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + else + hash = pkh; + } + if (a.input) { + const chunks = _chunks(); + if (chunks.length !== 2) + throw new TypeError('Input is invalid'); + if (!bscript.isCanonicalScriptSignature(chunks[0])) + throw new TypeError('Input has invalid signature'); + if (!ecc.isPoint(chunks[1])) + throw new TypeError('Input has invalid pubkey'); + if (a.signature && !a.signature.equals(chunks[0])) + throw new TypeError('Signature mismatch'); + if (a.pubkey && !a.pubkey.equals(chunks[1])) + throw new TypeError('Pubkey mismatch'); + const pkh = bcrypto.hash160(chunks[1]); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + } + } + return Object.assign(o, a); +} +exports.p2pkh = p2pkh; diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js new file mode 100644 index 0000000..8ec9910 --- /dev/null +++ b/src/payments/p2sh.js @@ -0,0 +1,191 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const networks_1 = require("../networks"); // eslint-disable-line +const bscript = require("../script"); +const bcrypto = require("../crypto"); +const lazy = require("./lazy"); +const typef = require('typeforce'); +const OPS = bscript.OPS; +const bs58check = require('bs58check'); +function stacksEqual(a, b) { + if (a.length !== b.length) + return false; + return a.every(function (x, i) { + return x.equals(b[i]); + }); +} +// input: [redeemScriptSig ...] {redeemScript} +// witness: <?> +// output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL +function p2sh(a, opts) { + if (!a.address && + !a.hash && + !a.output && + !a.redeem && + !a.input) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef({ + network: typef.maybe(typef.Object), + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + output: typef.maybe(typef.BufferN(23)), + redeem: typef.maybe({ + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + input: typef.maybe(typef.Buffer), + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }), + input: typef.maybe(typef.Buffer), + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a); + let network = a.network; + if (!network) { + network = (a.redeem && a.redeem.network) || networks_1.bitcoin; + } + const o = { network }; + const _address = lazy.value(function () { + const payload = bs58check.decode(a.address); + const version = payload.readUInt8(0); + const hash = payload.slice(1); + return { version, hash }; + }); + const _chunks = lazy.value(function () { return bscript.decompile(a.input); }); + const _redeem = lazy.value(function () { + const chunks = _chunks(); + return { + network, + output: chunks[chunks.length - 1], + input: bscript.compile(chunks.slice(0, -1)), + witness: a.witness || [] + }; + }); + // output dependents + lazy.prop(o, 'address', function () { + if (!o.hash) + return; + const payload = Buffer.allocUnsafe(21); + payload.writeUInt8(o.network.scriptHash, 0); + o.hash.copy(payload, 1); + return bs58check.encode(payload); + }); + lazy.prop(o, 'hash', function () { + // in order of least effort + if (a.output) + return a.output.slice(2, 22); + if (a.address) + return _address().hash; + if (o.redeem && o.redeem.output) + return bcrypto.hash160(o.redeem.output); + }); + lazy.prop(o, 'output', function () { + if (!o.hash) + return; + return bscript.compile([ + OPS.OP_HASH160, + o.hash, + OPS.OP_EQUAL + ]); + }); + // input dependents + lazy.prop(o, 'redeem', function () { + if (!a.input) + return; + return _redeem(); + }); + lazy.prop(o, 'input', function () { + if (!a.redeem || !a.redeem.input || !a.redeem.output) + return; + return bscript.compile([].concat(bscript.decompile(a.redeem.input), a.redeem.output)); + }); + lazy.prop(o, 'witness', function () { + if (o.redeem && o.redeem.witness) + return o.redeem.witness; + if (o.input) + return []; + }); + if (opts.validate) { + let hash = Buffer.from([]); + if (a.address) { + if (_address().version !== network.scriptHash) + throw new TypeError('Invalid version or Network mismatch'); + if (_address().hash.length !== 20) + throw new TypeError('Invalid address'); + hash = _address().hash; + } + if (a.hash) { + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else + hash = a.hash; + } + if (a.output) { + if (a.output.length !== 23 || + a.output[0] !== OPS.OP_HASH160 || + a.output[1] !== 0x14 || + a.output[22] !== OPS.OP_EQUAL) + throw new TypeError('Output is invalid'); + const hash2 = a.output.slice(2, 22); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else + hash = hash2; + } + // inlined to prevent 'no-inner-declarations' failing + const checkRedeem = function (redeem) { + // is the redeem output empty/invalid? + if (redeem.output) { + const decompile = bscript.decompile(redeem.output); + if (!decompile || decompile.length < 1) + throw new TypeError('Redeem.output too short'); + // match hash against other sources + const hash2 = bcrypto.hash160(redeem.output); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else + hash = hash2; + } + if (redeem.input) { + const hasInput = redeem.input.length > 0; + const hasWitness = redeem.witness && redeem.witness.length > 0; + if (!hasInput && !hasWitness) + throw new TypeError('Empty input'); + if (hasInput && hasWitness) + throw new TypeError('Input and witness provided'); + if (hasInput) { + const richunks = bscript.decompile(redeem.input); + if (!bscript.isPushOnly(richunks)) + throw new TypeError('Non push-only scriptSig'); + } + } + }; + if (a.input) { + const chunks = _chunks(); + if (!chunks || chunks.length < 1) + throw new TypeError('Input too short'); + if (!Buffer.isBuffer(_redeem().output)) + throw new TypeError('Input is invalid'); + checkRedeem(_redeem()); + } + if (a.redeem) { + if (a.redeem.network && a.redeem.network !== network) + throw new TypeError('Network mismatch'); + if (a.input) { + const redeem = _redeem(); + if (a.redeem.output && !a.redeem.output.equals(redeem.output)) + throw new TypeError('Redeem.output mismatch'); + if (a.redeem.input && !a.redeem.input.equals(redeem.input)) + throw new TypeError('Redeem.input mismatch'); + } + checkRedeem(a.redeem); + } + if (a.witness) { + if (a.redeem && + a.redeem.witness && + !stacksEqual(a.redeem.witness, a.witness)) + throw new TypeError('Witness and redeem.witness mismatch'); + } + } + return Object.assign(o, a); +} +exports.p2sh = p2sh; diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js new file mode 100644 index 0000000..98951c6 --- /dev/null +++ b/src/payments/p2wpkh.js @@ -0,0 +1,145 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../script"); +const bcrypto = require("../crypto"); +const lazy = require("./lazy"); +const networks_1 = require("../networks"); +const typef = require('typeforce'); +const OPS = bscript.OPS; +const ecc = require('tiny-secp256k1'); +const bech32 = require('bech32'); +const EMPTY_BUFFER = Buffer.alloc(0); +// witness: {signature} {pubKey} +// input: <> +// output: OP_0 {pubKeyHash} +function p2wpkh(a, opts) { + if (!a.address && + !a.hash && + !a.output && + !a.pubkey && + !a.witness) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef({ + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + input: typef.maybe(typef.BufferN(0)), + network: typef.maybe(typef.Object), + output: typef.maybe(typef.BufferN(22)), + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a); + const _address = lazy.value(function () { + const result = bech32.decode(a.address); + const version = result.words.shift(); + const data = bech32.fromWords(result.words); + return { + version, + prefix: result.prefix, + data: Buffer.from(data) + }; + }); + const network = a.network || networks_1.bitcoin; + const o = { network }; + lazy.prop(o, 'address', function () { + if (!o.hash) + return; + const words = bech32.toWords(o.hash); + words.unshift(0x00); + return bech32.encode(network.bech32, words); + }); + lazy.prop(o, 'hash', function () { + if (a.output) + return a.output.slice(2, 22); + if (a.address) + return _address().data; + if (a.pubkey || o.pubkey) + return bcrypto.hash160(a.pubkey || o.pubkey); // eslint-disable-line + }); + lazy.prop(o, 'output', function () { + if (!o.hash) + return; + return bscript.compile([ + OPS.OP_0, + o.hash + ]); + }); + lazy.prop(o, 'pubkey', function () { + if (a.pubkey) + return a.pubkey; + if (!a.witness) + return; + return a.witness[1]; + }); + lazy.prop(o, 'signature', function () { + if (!a.witness) + return; + return a.witness[0]; + }); + lazy.prop(o, 'input', function () { + if (!o.witness) + return; + return EMPTY_BUFFER; + }); + lazy.prop(o, 'witness', function () { + if (!a.pubkey) + return; + if (!a.signature) + return; + return [a.signature, a.pubkey]; + }); + // extended validation + if (opts.validate) { + let hash = Buffer.from([]); + if (a.address) { + if (network && network.bech32 !== _address().prefix) + throw new TypeError('Invalid prefix or Network mismatch'); + if (_address().version !== 0x00) + throw new TypeError('Invalid address version'); + if (_address().data.length !== 20) + throw new TypeError('Invalid address data'); + hash = _address().data; + } + if (a.hash) { + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else + hash = a.hash; + } + if (a.output) { + if (a.output.length !== 22 || + a.output[0] !== OPS.OP_0 || + a.output[1] !== 0x14) + throw new TypeError('Output is invalid'); + if (hash.length > 0 && !hash.equals(a.output.slice(2))) + throw new TypeError('Hash mismatch'); + else + hash = a.output.slice(2); + } + if (a.pubkey) { + const pkh = bcrypto.hash160(a.pubkey); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + else + hash = pkh; + } + if (a.witness) { + if (a.witness.length !== 2) + throw new TypeError('Witness is invalid'); + if (!bscript.isCanonicalScriptSignature(a.witness[0])) + throw new TypeError('Witness has invalid signature'); + if (!ecc.isPoint(a.witness[1])) + throw new TypeError('Witness has invalid pubkey'); + if (a.signature && !a.signature.equals(a.witness[0])) + throw new TypeError('Signature mismatch'); + if (a.pubkey && !a.pubkey.equals(a.witness[1])) + throw new TypeError('Pubkey mismatch'); + const pkh = bcrypto.hash160(a.witness[1]); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + } + } + return Object.assign(o, a); +} +exports.p2wpkh = p2wpkh; diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js new file mode 100644 index 0000000..a7e9f8b --- /dev/null +++ b/src/payments/p2wsh.js @@ -0,0 +1,178 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const networks_1 = require("../networks"); // eslint-disable-line +const bscript = require("../script"); +const bcrypto = require("../crypto"); +const lazy = require("./lazy"); +const typef = require('typeforce'); +const OPS = bscript.OPS; +const bech32 = require('bech32'); +const EMPTY_BUFFER = Buffer.alloc(0); +function stacksEqual(a, b) { + if (a.length !== b.length) + return false; + return a.every(function (x, i) { + return x.equals(b[i]); + }); +} +// input: <> +// witness: [redeemScriptSig ...] {redeemScript} +// output: OP_0 {sha256(redeemScript)} +function p2wsh(a, opts) { + if (!a.address && + !a.hash && + !a.output && + !a.redeem && + !a.witness) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef({ + network: typef.maybe(typef.Object), + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(32)), + output: typef.maybe(typef.BufferN(34)), + redeem: typef.maybe({ + input: typef.maybe(typef.Buffer), + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }), + input: typef.maybe(typef.BufferN(0)), + witness: typef.maybe(typef.arrayOf(typef.Buffer)) + }, a); + const _address = lazy.value(function () { + const result = bech32.decode(a.address); + const version = result.words.shift(); + const data = bech32.fromWords(result.words); + return { + version, + prefix: result.prefix, + data: Buffer.from(data) + }; + }); + const _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input); }); + let network = a.network; + if (!network) { + network = (a.redeem && a.redeem.network) || networks_1.bitcoin; + } + const o = { network }; + lazy.prop(o, 'address', function () { + if (!o.hash) + return; + const words = bech32.toWords(o.hash); + words.unshift(0x00); + return bech32.encode(network.bech32, words); + }); + lazy.prop(o, 'hash', function () { + if (a.output) + return a.output.slice(2); + if (a.address) + return _address().data; + if (o.redeem && o.redeem.output) + return bcrypto.sha256(o.redeem.output); + }); + lazy.prop(o, 'output', function () { + if (!o.hash) + return; + return bscript.compile([ + OPS.OP_0, + o.hash + ]); + }); + lazy.prop(o, 'redeem', function () { + if (!a.witness) + return; + return { + output: a.witness[a.witness.length - 1], + input: EMPTY_BUFFER, + witness: a.witness.slice(0, -1) + }; + }); + lazy.prop(o, 'input', function () { + if (!o.witness) + return; + return EMPTY_BUFFER; + }); + lazy.prop(o, 'witness', function () { + // transform redeem input to witness stack? + if (a.redeem && + a.redeem.input && + a.redeem.input.length > 0 && + a.redeem.output && + a.redeem.output.length > 0) { + const stack = bscript.toStack(_rchunks()); + // assign, and blank the existing input + o.redeem = Object.assign({ witness: stack }, a.redeem); + o.redeem.input = EMPTY_BUFFER; + return [].concat(stack, a.redeem.output); + } + if (!a.redeem) + return; + if (!a.redeem.output) + return; + if (!a.redeem.witness) + return; + return [].concat(a.redeem.witness, a.redeem.output); + }); + // extended validation + if (opts.validate) { + let hash = Buffer.from([]); + if (a.address) { + if (_address().prefix !== network.bech32) + throw new TypeError('Invalid prefix or Network mismatch'); + if (_address().version !== 0x00) + throw new TypeError('Invalid address version'); + if (_address().data.length !== 32) + throw new TypeError('Invalid address data'); + hash = _address().data; + } + if (a.hash) { + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else + hash = a.hash; + } + if (a.output) { + if (a.output.length !== 34 || + a.output[0] !== OPS.OP_0 || + a.output[1] !== 0x20) + throw new TypeError('Output is invalid'); + const hash2 = a.output.slice(2); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else + hash = hash2; + } + if (a.redeem) { + if (a.redeem.network && a.redeem.network !== network) + throw new TypeError('Network mismatch'); + // is there two redeem sources? + if (a.redeem.input && + a.redeem.input.length > 0 && + a.redeem.witness && + a.redeem.witness.length > 0) + throw new TypeError('Ambiguous witness source'); + // is the redeem output non-empty? + if (a.redeem.output) { + if (bscript.decompile(a.redeem.output).length === 0) + throw new TypeError('Redeem.output is invalid'); + // match hash against other sources + const hash2 = bcrypto.sha256(a.redeem.output); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else + hash = hash2; + } + if (a.redeem.input && !bscript.isPushOnly(_rchunks())) + throw new TypeError('Non push-only scriptSig'); + if (a.witness && a.redeem.witness && !stacksEqual(a.witness, a.redeem.witness)) + throw new TypeError('Witness and redeem.witness mismatch'); + } + if (a.witness) { + if (a.redeem && a.redeem.output && !a.redeem.output.equals(a.witness[a.witness.length - 1])) + throw new TypeError('Witness and redeem.output mismatch'); + } + } + return Object.assign(o, a); +} +exports.p2wsh = p2wsh; diff --git a/src/script.js b/src/script.js new file mode 100644 index 0000000..3d115cb --- /dev/null +++ b/src/script.js @@ -0,0 +1,188 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const types = require("./types"); +const scriptNumber = require("./script_number"); +const scriptSignature = require("./script_signature"); +const bip66 = require('bip66'); +const ecc = require('tiny-secp256k1'); +const pushdata = require('pushdata-bitcoin'); +const typeforce = require('typeforce'); +exports.OPS = require('bitcoin-ops'); +const REVERSE_OPS = require('bitcoin-ops/map'); +const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1 +function isOPInt(value) { + return types.Number(value) && + ((value === exports.OPS.OP_0) || + (value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) || + (value === exports.OPS.OP_1NEGATE)); +} +function isPushOnlyChunk(value) { + return types.Buffer(value) || isOPInt(value); +} +function isPushOnly(value) { + return types.Array(value) && value.every(isPushOnlyChunk); +} +exports.isPushOnly = isPushOnly; +function asMinimalOP(buffer) { + if (buffer.length === 0) + return exports.OPS.OP_0; + if (buffer.length !== 1) + return; + if (buffer[0] >= 1 && buffer[0] <= 16) + return OP_INT_BASE + buffer[0]; + if (buffer[0] === 0x81) + return exports.OPS.OP_1NEGATE; +} +function chunksIsBuffer(buf) { + return Buffer.isBuffer(buf); +} +function chunksIsArray(buf) { + return types.Array(buf); +} +function singleChunkIsBuffer(buf) { + return Buffer.isBuffer(buf); +} +function compile(chunks) { + // TODO: remove me + if (chunksIsBuffer(chunks)) + return chunks; + typeforce(types.Array, chunks); + const bufferSize = chunks.reduce(function (accum, chunk) { + // data chunk + if (singleChunkIsBuffer(chunk)) { + // adhere to BIP62.3, minimal push policy + if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { + return accum + 1; + } + return accum + pushdata.encodingLength(chunk.length) + chunk.length; + } + // opcode + return accum + 1; + }, 0.0); + const buffer = Buffer.allocUnsafe(bufferSize); + let offset = 0; + chunks.forEach(function (chunk) { + // data chunk + if (singleChunkIsBuffer(chunk)) { + // adhere to BIP62.3, minimal push policy + const opcode = asMinimalOP(chunk); + if (opcode !== undefined) { + buffer.writeUInt8(opcode, offset); + offset += 1; + return; + } + offset += pushdata.encode(buffer, chunk.length, offset); + chunk.copy(buffer, offset); + offset += chunk.length; + // opcode + } + else { + buffer.writeUInt8(chunk, offset); + offset += 1; + } + }); + if (offset !== buffer.length) + throw new Error('Could not decode chunks'); + return buffer; +} +exports.compile = compile; +function decompile(buffer) { + // TODO: remove me + if (chunksIsArray(buffer)) + return buffer; + typeforce(types.Buffer, buffer); + const chunks = []; + let i = 0; + while (i < buffer.length) { + const opcode = buffer[i]; + // data chunk + if ((opcode > exports.OPS.OP_0) && (opcode <= exports.OPS.OP_PUSHDATA4)) { + const d = pushdata.decode(buffer, i); + // did reading a pushDataInt fail? + if (d === null) + return null; + i += d.size; + // attempt to read too much data? + if (i + d.number > buffer.length) + return null; + const data = buffer.slice(i, i + d.number); + i += d.number; + // decompile minimally + const op = asMinimalOP(data); + if (op !== undefined) { + chunks.push(op); + } + else { + chunks.push(data); + } + // opcode + } + else { + chunks.push(opcode); + i += 1; + } + } + return chunks; +} +exports.decompile = decompile; +function toASM(chunks) { + if (chunksIsBuffer(chunks)) { + chunks = decompile(chunks); + } + return chunks.map(function (chunk) { + // data? + if (singleChunkIsBuffer(chunk)) { + const op = asMinimalOP(chunk); + if (op === undefined) + return chunk.toString('hex'); + chunk = op; + } + // opcode! + return REVERSE_OPS[chunk]; + }).join(' '); +} +exports.toASM = toASM; +function fromASM(asm) { + typeforce(types.String, asm); + return compile(asm.split(' ').map(function (chunkStr) { + // opcode? + if (exports.OPS[chunkStr] !== undefined) + return exports.OPS[chunkStr]; + typeforce(types.Hex, chunkStr); + // data! + return Buffer.from(chunkStr, 'hex'); + })); +} +exports.fromASM = fromASM; +function toStack(chunks) { + chunks = decompile(chunks); + typeforce(isPushOnly, chunks); + return chunks.map(function (op) { + if (singleChunkIsBuffer(op)) + return op; + if (op === exports.OPS.OP_0) + return Buffer.allocUnsafe(0); + return scriptNumber.encode(op - OP_INT_BASE); + }); +} +exports.toStack = toStack; +function isCanonicalPubKey(buffer) { + return ecc.isPoint(buffer); +} +exports.isCanonicalPubKey = isCanonicalPubKey; +function isDefinedHashType(hashType) { + const hashTypeMod = hashType & ~0x80; + // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE + return hashTypeMod > 0x00 && hashTypeMod < 0x04; +} +exports.isDefinedHashType = isDefinedHashType; +function isCanonicalScriptSignature(buffer) { + if (!Buffer.isBuffer(buffer)) + return false; + if (!isDefinedHashType(buffer[buffer.length - 1])) + return false; + return bip66.check(buffer.slice(0, -1)); +} +exports.isCanonicalScriptSignature = isCanonicalScriptSignature; +exports.number = scriptNumber; +exports.signature = scriptSignature; diff --git a/src/script_number.js b/src/script_number.js new file mode 100644 index 0000000..da12de6 --- /dev/null +++ b/src/script_number.js @@ -0,0 +1,60 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +function decode(buffer, maxLength, minimal) { + maxLength = maxLength || 4; + minimal = minimal === undefined ? true : minimal; + const length = buffer.length; + if (length === 0) + return 0; + if (length > maxLength) + throw new TypeError('Script number overflow'); + if (minimal) { + if ((buffer[length - 1] & 0x7f) === 0) { + if (length <= 1 || (buffer[length - 2] & 0x80) === 0) + throw new Error('Non-minimally encoded script number'); + } + } + // 40-bit + if (length === 5) { + const a = buffer.readUInt32LE(0); + const b = buffer.readUInt8(4); + if (b & 0x80) + return -(((b & ~0x80) * 0x100000000) + a); + return (b * 0x100000000) + a; + } + // 32-bit / 24-bit / 16-bit / 8-bit + let result = 0; + for (var i = 0; i < length; ++i) { + result |= buffer[i] << (8 * i); + } + if (buffer[length - 1] & 0x80) + return -(result & ~(0x80 << (8 * (length - 1)))); + return result; +} +exports.decode = decode; +function scriptNumSize(i) { + return i > 0x7fffffff ? 5 + : i > 0x7fffff ? 4 + : i > 0x7fff ? 3 + : i > 0x7f ? 2 + : i > 0x00 ? 1 + : 0; +} +function encode(number) { + let value = Math.abs(number); + const size = scriptNumSize(value); + const buffer = Buffer.allocUnsafe(size); + const negative = number < 0; + for (var i = 0; i < size; ++i) { + buffer.writeUInt8(value & 0xff, i); + value >>= 8; + } + if (buffer[size - 1] & 0x80) { + buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1); + } + else if (negative) { + buffer[size - 1] |= 0x80; + } + return buffer; +} +exports.encode = encode; diff --git a/src/script_signature.js b/src/script_signature.js new file mode 100644 index 0000000..c3372cd --- /dev/null +++ b/src/script_signature.js @@ -0,0 +1,58 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const types = require("./types"); +const bip66 = require('bip66'); +const typeforce = require('typeforce'); +const ZERO = Buffer.alloc(1, 0); +function toDER(x) { + let i = 0; + while (x[i] === 0) + ++i; + if (i === x.length) + return ZERO; + x = x.slice(i); + if (x[0] & 0x80) + return Buffer.concat([ZERO, x], 1 + x.length); + return x; +} +function fromDER(x) { + if (x[0] === 0x00) + x = x.slice(1); + const buffer = Buffer.alloc(32, 0); + const bstart = Math.max(0, 32 - x.length); + x.copy(buffer, bstart); + return buffer; +} +// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) +function decode(buffer) { + const hashType = buffer.readUInt8(buffer.length - 1); + const hashTypeMod = hashType & ~0x80; + if (hashTypeMod <= 0 || hashTypeMod >= 4) + throw new Error('Invalid hashType ' + hashType); + const decode = bip66.decode(buffer.slice(0, -1)); + const r = fromDER(decode.r); + const s = fromDER(decode.s); + return { + signature: Buffer.concat([r, s], 64), + hashType: hashType + }; +} +exports.decode = decode; +function encode(signature, hashType) { + typeforce({ + signature: types.BufferN(64), + hashType: types.UInt8 + }, { signature, hashType }); + const hashTypeMod = hashType & ~0x80; + if (hashTypeMod <= 0 || hashTypeMod >= 4) + throw new Error('Invalid hashType ' + hashType); + const hashTypeBuffer = Buffer.allocUnsafe(1); + hashTypeBuffer.writeUInt8(hashType, 0); + const r = toDER(signature.slice(0, 32)); + const s = toDER(signature.slice(32, 64)); + return Buffer.concat([ + bip66.encode(r, s), + hashTypeBuffer + ]); +} +exports.encode = encode; diff --git a/src/templates/multisig/index.js b/src/templates/multisig/index.js new file mode 100644 index 0000000..85a15b9 --- /dev/null +++ b/src/templates/multisig/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const input = require("./input"); +exports.input = input; +const output = require("./output"); +exports.output = output; diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js new file mode 100644 index 0000000..f8dd3d3 --- /dev/null +++ b/src/templates/multisig/input.js @@ -0,0 +1,21 @@ +"use strict"; +// OP_0 [signatures ...] +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const script_1 = require("../../script"); +function partialSignature(value) { + return value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value); +} +function check(script, allowIncomplete) { + const chunks = bscript.decompile(script); + if (chunks.length < 2) + return false; + if (chunks[0] !== script_1.OPS.OP_0) + return false; + if (allowIncomplete) { + return chunks.slice(1).every(partialSignature); + } + return chunks.slice(1).every(bscript.isCanonicalScriptSignature); +} +exports.check = check; +check.toJSON = function () { return 'multisig input'; }; diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.js new file mode 100644 index 0000000..811ed12 --- /dev/null +++ b/src/templates/multisig/output.js @@ -0,0 +1,34 @@ +"use strict"; +// m [pubKeys ...] n OP_CHECKMULTISIG +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const types = require("../../types"); +const script_1 = require("../../script"); +const OP_INT_BASE = script_1.OPS.OP_RESERVED; // OP_1 - 1 +function check(script, allowIncomplete) { + const chunks = bscript.decompile(script); + if (chunks.length < 4) + return false; + if (chunks[chunks.length - 1] !== script_1.OPS.OP_CHECKMULTISIG) + return false; + if (!types.Number(chunks[0])) + return false; + if (!types.Number(chunks[chunks.length - 2])) + return false; + const m = chunks[0] - OP_INT_BASE; + const n = chunks[chunks.length - 2] - OP_INT_BASE; + if (m <= 0) + return false; + if (n > 16) + return false; + if (m > n) + return false; + if (n !== chunks.length - 3) + return false; + if (allowIncomplete) + return true; + const keys = chunks.slice(1, -2); + return keys.every(bscript.isCanonicalPubKey); +} +exports.check = check; +check.toJSON = function () { return 'multi-sig output'; }; diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js new file mode 100644 index 0000000..fd5320d --- /dev/null +++ b/src/templates/nulldata.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +// OP_RETURN {data} +const bscript = require("../script"); +const OPS = bscript.OPS; +function check(script) { + const buffer = bscript.compile(script); + return buffer.length > 1 && + buffer[0] === OPS.OP_RETURN; +} +exports.check = check; +check.toJSON = function () { return 'null data output'; }; +const output = { check }; +exports.output = output; diff --git a/src/templates/pubkey/index.js b/src/templates/pubkey/index.js new file mode 100644 index 0000000..85a15b9 --- /dev/null +++ b/src/templates/pubkey/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const input = require("./input"); +exports.input = input; +const output = require("./output"); +exports.output = output; diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js new file mode 100644 index 0000000..bc5e566 --- /dev/null +++ b/src/templates/pubkey/input.js @@ -0,0 +1,11 @@ +"use strict"; +// {signature} +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +function check(script) { + const chunks = bscript.decompile(script); + return chunks.length === 1 && + bscript.isCanonicalScriptSignature(chunks[0]); +} +exports.check = check; +check.toJSON = function () { return 'pubKey input'; }; diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.js new file mode 100644 index 0000000..043150e --- /dev/null +++ b/src/templates/pubkey/output.js @@ -0,0 +1,13 @@ +"use strict"; +// {pubKey} OP_CHECKSIG +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const script_1 = require("../../script"); +function check(script) { + const chunks = bscript.decompile(script); + return chunks.length === 2 && + bscript.isCanonicalPubKey(chunks[0]) && + chunks[1] === script_1.OPS.OP_CHECKSIG; +} +exports.check = check; +check.toJSON = function () { return 'pubKey output'; }; diff --git a/src/templates/pubkeyhash/index.js b/src/templates/pubkeyhash/index.js new file mode 100644 index 0000000..85a15b9 --- /dev/null +++ b/src/templates/pubkeyhash/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const input = require("./input"); +exports.input = input; +const output = require("./output"); +exports.output = output; diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js new file mode 100644 index 0000000..29a684e --- /dev/null +++ b/src/templates/pubkeyhash/input.js @@ -0,0 +1,12 @@ +"use strict"; +// {signature} {pubKey} +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +function check(script) { + const chunks = bscript.decompile(script); + return chunks.length === 2 && + bscript.isCanonicalScriptSignature(chunks[0]) && + bscript.isCanonicalPubKey(chunks[1]); +} +exports.check = check; +check.toJSON = function () { return 'pubKeyHash input'; }; diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.js new file mode 100644 index 0000000..9016c6a --- /dev/null +++ b/src/templates/pubkeyhash/output.js @@ -0,0 +1,16 @@ +"use strict"; +// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const script_1 = require("../../script"); +function check(script) { + const buffer = bscript.compile(script); + return buffer.length === 25 && + buffer[0] === script_1.OPS.OP_DUP && + buffer[1] === script_1.OPS.OP_HASH160 && + buffer[2] === 0x14 && + buffer[23] === script_1.OPS.OP_EQUALVERIFY && + buffer[24] === script_1.OPS.OP_CHECKSIG; +} +exports.check = check; +check.toJSON = function () { return 'pubKeyHash output'; }; diff --git a/src/templates/scripthash/index.js b/src/templates/scripthash/index.js new file mode 100644 index 0000000..85a15b9 --- /dev/null +++ b/src/templates/scripthash/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const input = require("./input"); +exports.input = input; +const output = require("./output"); +exports.output = output; diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js new file mode 100644 index 0000000..c40bc26 --- /dev/null +++ b/src/templates/scripthash/input.js @@ -0,0 +1,43 @@ +"use strict"; +// <scriptSig> {serialized scriptPubKey script} +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const p2ms = require("../multisig"); +const p2pk = require("../pubkey"); +const p2pkh = require("../pubkeyhash"); +const p2wpkho = require("../witnesspubkeyhash/output"); +const p2wsho = require("../witnessscripthash/output"); +function check(script, allowIncomplete) { + const chunks = bscript.decompile(script); + if (chunks.length < 1) + return false; + const lastChunk = chunks[chunks.length - 1]; + if (!Buffer.isBuffer(lastChunk)) + return false; + const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))); + const redeemScriptChunks = bscript.decompile(lastChunk); + // is redeemScript a valid script? + if (!redeemScriptChunks) + return false; + // is redeemScriptSig push only? + if (!bscript.isPushOnly(scriptSigChunks)) + return false; + // is witness? + if (chunks.length === 1) { + return p2wsho.check(redeemScriptChunks) || + p2wpkho.check(redeemScriptChunks); + } + // match types + if (p2pkh.input.check(scriptSigChunks) && + p2pkh.output.check(redeemScriptChunks)) + return true; + if (p2ms.input.check(scriptSigChunks, allowIncomplete) && + p2ms.output.check(redeemScriptChunks)) + return true; + if (p2pk.input.check(scriptSigChunks) && + p2pk.output.check(redeemScriptChunks)) + return true; + return false; +} +exports.check = check; +check.toJSON = function () { return 'scriptHash input'; }; diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.js new file mode 100644 index 0000000..bd38d5a --- /dev/null +++ b/src/templates/scripthash/output.js @@ -0,0 +1,14 @@ +"use strict"; +// OP_HASH160 {scriptHash} OP_EQUAL +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const script_1 = require("../../script"); +function check(script) { + const buffer = bscript.compile(script); + return buffer.length === 23 && + buffer[0] === script_1.OPS.OP_HASH160 && + buffer[1] === 0x14 && + buffer[22] === script_1.OPS.OP_EQUAL; +} +exports.check = check; +check.toJSON = function () { return 'scriptHash output'; }; diff --git a/src/templates/witnesscommitment/index.js b/src/templates/witnesscommitment/index.js new file mode 100644 index 0000000..aff0618 --- /dev/null +++ b/src/templates/witnesscommitment/index.js @@ -0,0 +1,4 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const output = require("./output"); +exports.output = output; diff --git a/src/templates/witnesscommitment/output.js b/src/templates/witnesscommitment/output.js new file mode 100644 index 0000000..95a8bc1 --- /dev/null +++ b/src/templates/witnesscommitment/output.js @@ -0,0 +1,30 @@ +"use strict"; +// OP_RETURN {aa21a9ed} {commitment} +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const types = require("../../types"); +const typeforce = require('typeforce'); +const script_1 = require("../../script"); +const HEADER = Buffer.from('aa21a9ed', 'hex'); +function check(script) { + const buffer = bscript.compile(script); + return buffer.length > 37 && + buffer[0] === script_1.OPS.OP_RETURN && + buffer[1] === 0x24 && + buffer.slice(2, 6).equals(HEADER); +} +exports.check = check; +check.toJSON = function () { return 'Witness commitment output'; }; +function encode(commitment) { + typeforce(types.Hash256bit, commitment); + const buffer = Buffer.allocUnsafe(36); + HEADER.copy(buffer, 0); + commitment.copy(buffer, 4); + return bscript.compile([script_1.OPS.OP_RETURN, buffer]); +} +exports.encode = encode; +function decode(buffer) { + typeforce(check, buffer); + return bscript.decompile(buffer)[1].slice(4, 36); +} +exports.decode = decode; diff --git a/src/templates/witnesspubkeyhash/index.js b/src/templates/witnesspubkeyhash/index.js new file mode 100644 index 0000000..85a15b9 --- /dev/null +++ b/src/templates/witnesspubkeyhash/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const input = require("./input"); +exports.input = input; +const output = require("./output"); +exports.output = output; diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.js new file mode 100644 index 0000000..131a49e --- /dev/null +++ b/src/templates/witnesspubkeyhash/input.js @@ -0,0 +1,15 @@ +"use strict"; +// {signature} {pubKey} +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +function isCompressedCanonicalPubKey(pubKey) { + return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; +} +function check(script) { + const chunks = bscript.decompile(script); + return chunks.length === 2 && + bscript.isCanonicalScriptSignature(chunks[0]) && + isCompressedCanonicalPubKey(chunks[1]); +} +exports.check = check; +check.toJSON = function () { return 'witnessPubKeyHash input'; }; diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.js new file mode 100644 index 0000000..2fb6e15 --- /dev/null +++ b/src/templates/witnesspubkeyhash/output.js @@ -0,0 +1,13 @@ +"use strict"; +// OP_0 {pubKeyHash} +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const script_1 = require("../../script"); +function check(script) { + const buffer = bscript.compile(script); + return buffer.length === 22 && + buffer[0] === script_1.OPS.OP_0 && + buffer[1] === 0x14; +} +exports.check = check; +check.toJSON = function () { return 'Witness pubKeyHash output'; }; diff --git a/src/templates/witnessscripthash/index.js b/src/templates/witnessscripthash/index.js new file mode 100644 index 0000000..85a15b9 --- /dev/null +++ b/src/templates/witnessscripthash/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const input = require("./input"); +exports.input = input; +const output = require("./output"); +exports.output = output; diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js new file mode 100644 index 0000000..255ea86 --- /dev/null +++ b/src/templates/witnessscripthash/input.js @@ -0,0 +1,34 @@ +"use strict"; +// <scriptSig> {serialized scriptPubKey script} +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const typeforce = require('typeforce'); +const p2ms = require("../multisig"); +const p2pk = require("../pubkey"); +const p2pkh = require("../pubkeyhash"); +function check(chunks, allowIncomplete) { + typeforce(typeforce.Array, chunks); + if (chunks.length < 1) + return false; + const witnessScript = chunks[chunks.length - 1]; + if (!Buffer.isBuffer(witnessScript)) + return false; + const witnessScriptChunks = bscript.decompile(witnessScript); + // is witnessScript a valid script? + if (!witnessScriptChunks || witnessScriptChunks.length === 0) + return false; + const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); + // match types + if (p2pkh.input.check(witnessRawScriptSig) && + p2pkh.output.check(witnessScriptChunks)) + return true; + if (p2ms.input.check(witnessRawScriptSig, allowIncomplete) && + p2ms.output.check(witnessScriptChunks)) + return true; + if (p2pk.input.check(witnessRawScriptSig) && + p2pk.output.check(witnessScriptChunks)) + return true; + return false; +} +exports.check = check; +check.toJSON = function () { return 'witnessScriptHash input'; }; diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.js new file mode 100644 index 0000000..994a75a --- /dev/null +++ b/src/templates/witnessscripthash/output.js @@ -0,0 +1,13 @@ +"use strict"; +// OP_0 {scriptHash} +Object.defineProperty(exports, "__esModule", { value: true }); +const bscript = require("../../script"); +const script_1 = require("../../script"); +function check(script) { + const buffer = bscript.compile(script); + return buffer.length === 34 && + buffer[0] === script_1.OPS.OP_0 && + buffer[1] === 0x20; +} +exports.check = check; +check.toJSON = function () { return 'Witness scriptHash output'; }; diff --git a/src/transaction.js b/src/transaction.js new file mode 100644 index 0000000..6619b79 --- /dev/null +++ b/src/transaction.js @@ -0,0 +1,448 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const bcrypto = require("./crypto"); +const bscript = require("./script"); +const types = require("./types"); +const bufferutils = require("./bufferutils"); +const bufferutils_1 = require("./bufferutils"); +const script_1 = require("./script"); +const typeforce = require('typeforce'); +const varuint = require('varuint-bitcoin'); +function varSliceSize(someScript) { + const length = someScript.length; + return varuint.encodingLength(length) + length; +} +function vectorSize(someVector) { + const length = someVector.length; + return varuint.encodingLength(length) + someVector.reduce((sum, witness) => { + return sum + varSliceSize(witness); + }, 0); +} +const EMPTY_SCRIPT = Buffer.allocUnsafe(0); +const EMPTY_WITNESS = []; +const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'); +const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'); +const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex'); +const BLANK_OUTPUT = { + script: EMPTY_SCRIPT, + valueBuffer: VALUE_UINT64_MAX +}; +function isOutput(out) { + return out.value !== undefined; +} +class Transaction { + constructor() { + this.version = 1; + this.locktime = 0; + this.ins = []; + this.outs = []; + } + static fromBuffer(buffer, __noStrict) { + let offset = 0; + function readSlice(n) { + offset += n; + return buffer.slice(offset - n, offset); + } + function readUInt32() { + const i = buffer.readUInt32LE(offset); + offset += 4; + return i; + } + function readInt32() { + const i = buffer.readInt32LE(offset); + offset += 4; + return i; + } + function readUInt64() { + const i = bufferutils.readUInt64LE(buffer, offset); + offset += 8; + return i; + } + function readVarInt() { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + } + function readVarSlice() { + return readSlice(readVarInt()); + } + function readVector() { + const count = readVarInt(); + const vector = []; + for (var i = 0; i < count; i++) + vector.push(readVarSlice()); + return vector; + } + const tx = new Transaction(); + tx.version = readInt32(); + const marker = buffer.readUInt8(offset); + const flag = buffer.readUInt8(offset + 1); + let hasWitnesses = false; + if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && + flag === Transaction.ADVANCED_TRANSACTION_FLAG) { + offset += 2; + hasWitnesses = true; + } + const vinLen = readVarInt(); + for (var i = 0; i < vinLen; ++i) { + tx.ins.push({ + hash: readSlice(32), + index: readUInt32(), + script: readVarSlice(), + sequence: readUInt32(), + witness: EMPTY_WITNESS + }); + } + const voutLen = readVarInt(); + for (i = 0; i < voutLen; ++i) { + tx.outs.push({ + value: readUInt64(), + script: readVarSlice() + }); + } + if (hasWitnesses) { + for (i = 0; i < vinLen; ++i) { + tx.ins[i].witness = readVector(); + } + // was this pointless? + if (!tx.hasWitnesses()) + throw new Error('Transaction has superfluous witness data'); + } + tx.locktime = readUInt32(); + if (__noStrict) + return tx; + if (offset !== buffer.length) + throw new Error('Transaction has unexpected data'); + return tx; + } + static fromHex(hex) { + return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false); + } + static isCoinbaseHash(buffer) { + typeforce(types.Hash256bit, buffer); + for (var i = 0; i < 32; ++i) { + if (buffer[i] !== 0) + return false; + } + return true; + } + isCoinbase() { + return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash); + } + addInput(hash, index, sequence, scriptSig) { + typeforce(types.tuple(types.Hash256bit, types.UInt32, types.maybe(types.UInt32), types.maybe(types.Buffer)), arguments); + if (types.Null(sequence)) { + sequence = Transaction.DEFAULT_SEQUENCE; + } + // Add the input and return the input's index + return (this.ins.push({ + hash: hash, + index: index, + script: scriptSig || EMPTY_SCRIPT, + sequence: sequence, + witness: EMPTY_WITNESS + }) - 1); + } + addOutput(scriptPubKey, value) { + typeforce(types.tuple(types.Buffer, types.Satoshi), arguments); + // Add the output and return the output's index + return (this.outs.push({ + script: scriptPubKey, + value: value + }) - 1); + } + hasWitnesses() { + return this.ins.some((x) => { + return x.witness.length !== 0; + }); + } + weight() { + const base = this.__byteLength(false); + const total = this.__byteLength(true); + return base * 3 + total; + } + virtualSize() { + return Math.ceil(this.weight() / 4); + } + byteLength() { + return this.__byteLength(true); + } + __byteLength(__allowWitness) { + const hasWitnesses = __allowWitness && this.hasWitnesses(); + return ((hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script); + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0) + + (hasWitnesses ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) : 0)); + } + clone() { + const newTx = new Transaction(); + newTx.version = this.version; + newTx.locktime = this.locktime; + newTx.ins = this.ins.map((txIn) => { + return { + hash: txIn.hash, + index: txIn.index, + script: txIn.script, + sequence: txIn.sequence, + witness: txIn.witness + }; + }); + newTx.outs = this.outs.map((txOut) => { + return { + script: txOut.script, + value: txOut.value + }; + }); + return newTx; + } + /** + * Hash transaction for signing a specific input. + * + * Bitcoin uses a different hash for each signed transaction input. + * This method copies the transaction, makes the necessary changes based on the + * hashType, and then hashes the result. + * This hash can then be used to sign the provided transaction input. + */ + hashForSignature(inIndex, prevOutScript, hashType) { + typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments); + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 + if (inIndex >= this.ins.length) + return ONE; + // ignore OP_CODESEPARATOR + const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter((x) => { + return x !== script_1.OPS.OP_CODESEPARATOR; + })); + const txTmp = this.clone(); + // SIGHASH_NONE: ignore all outputs? (wildcard payee) + if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { + txTmp.outs = []; + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, i) => { + if (i === inIndex) + return; + input.sequence = 0; + }); + // SIGHASH_SINGLE: ignore all outputs, except at the same index? + } + else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 + if (inIndex >= this.outs.length) + return ONE; + // truncate outputs after + txTmp.outs.length = inIndex + 1; + // "blank" outputs before + for (var i = 0; i < inIndex; i++) { + txTmp.outs[i] = BLANK_OUTPUT; + } + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, y) => { + if (y === inIndex) + return; + input.sequence = 0; + }); + } + // SIGHASH_ANYONECANPAY: ignore inputs entirely? + if (hashType & Transaction.SIGHASH_ANYONECANPAY) { + txTmp.ins = [txTmp.ins[inIndex]]; + txTmp.ins[0].script = ourScript; + // SIGHASH_ALL: only ignore input scripts + } + else { + // "blank" others input scripts + txTmp.ins.forEach((input) => { + input.script = EMPTY_SCRIPT; + }); + txTmp.ins[inIndex].script = ourScript; + } + // serialize and hash + const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4); + buffer.writeInt32LE(hashType, buffer.length - 4); + txTmp.__toBuffer(buffer, 0, false); + return bcrypto.hash256(buffer); + } + hashForWitnessV0(inIndex, prevOutScript, value, hashType) { + typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments); + let tbuffer = Buffer.from([]); + let toffset = 0; + function writeSlice(slice) { + toffset += slice.copy(tbuffer, toffset); + } + function writeUInt32(i) { + toffset = tbuffer.writeUInt32LE(i, toffset); + } + function writeUInt64(i) { + toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset); + } + function writeVarInt(i) { + varuint.encode(i, tbuffer, toffset); + toffset += varuint.encode.bytes; + } + function writeVarSlice(slice) { + writeVarInt(slice.length); + writeSlice(slice); + } + let hashOutputs = ZERO; + let hashPrevouts = ZERO; + let hashSequence = ZERO; + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { + tbuffer = Buffer.allocUnsafe(36 * this.ins.length); + toffset = 0; + this.ins.forEach((txIn) => { + writeSlice(txIn.hash); + writeUInt32(txIn.index); + }); + hashPrevouts = bcrypto.hash256(tbuffer); + } + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && + (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + tbuffer = Buffer.allocUnsafe(4 * this.ins.length); + toffset = 0; + this.ins.forEach((txIn) => { + writeUInt32(txIn.sequence); + }); + hashSequence = bcrypto.hash256(tbuffer); + } + if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + const txOutsSize = this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0); + tbuffer = Buffer.allocUnsafe(txOutsSize); + toffset = 0; + this.outs.forEach((out) => { + writeUInt64(out.value); + writeVarSlice(out.script); + }); + hashOutputs = bcrypto.hash256(tbuffer); + } + else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { + const output = this.outs[inIndex]; + tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); + toffset = 0; + writeUInt64(output.value); + writeVarSlice(output.script); + hashOutputs = bcrypto.hash256(tbuffer); + } + tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)); + toffset = 0; + const input = this.ins[inIndex]; + writeUInt32(this.version); + writeSlice(hashPrevouts); + writeSlice(hashSequence); + writeSlice(input.hash); + writeUInt32(input.index); + writeVarSlice(prevOutScript); + writeUInt64(value); + writeUInt32(input.sequence); + writeSlice(hashOutputs); + writeUInt32(this.locktime); + writeUInt32(hashType); + return bcrypto.hash256(tbuffer); + } + getHash(forWitness) { + // wtxid for coinbase is always 32 bytes of 0x00 + if (forWitness && this.isCoinbase()) + return Buffer.alloc(32, 0); + return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness)); + } + getId() { + // transaction hash's are displayed in reverse order + return bufferutils_1.reverseBuffer(this.getHash(false)).toString('hex'); + } + toBuffer(buffer, initialOffset) { + return this.__toBuffer(buffer, initialOffset, true); + } + __toBuffer(buffer, initialOffset, __allowWitness) { + if (!buffer) + buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)); + let offset = initialOffset || 0; + function writeSlice(slice) { + offset += slice.copy(buffer, offset); + } + function writeUInt8(i) { + offset = buffer.writeUInt8(i, offset); + } + function writeUInt32(i) { + offset = buffer.writeUInt32LE(i, offset); + } + function writeInt32(i) { + offset = buffer.writeInt32LE(i, offset); + } + function writeUInt64(i) { + offset = bufferutils.writeUInt64LE(buffer, i, offset); + } + function writeVarInt(i) { + varuint.encode(i, buffer, offset); + offset += varuint.encode.bytes; + } + function writeVarSlice(slice) { + writeVarInt(slice.length); + writeSlice(slice); + } + function writeVector(vector) { + writeVarInt(vector.length); + vector.forEach(writeVarSlice); + } + writeInt32(this.version); + const hasWitnesses = __allowWitness && this.hasWitnesses(); + if (hasWitnesses) { + writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); + writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); + } + writeVarInt(this.ins.length); + this.ins.forEach((txIn) => { + writeSlice(txIn.hash); + writeUInt32(txIn.index); + writeVarSlice(txIn.script); + writeUInt32(txIn.sequence); + }); + writeVarInt(this.outs.length); + this.outs.forEach((txOut) => { + if (isOutput(txOut)) { + writeUInt64(txOut.value); + } + else { + writeSlice(txOut.valueBuffer); + } + writeVarSlice(txOut.script); + }); + if (hasWitnesses) { + this.ins.forEach((input) => { + writeVector(input.witness); + }); + } + writeUInt32(this.locktime); + // avoid slicing unless necessary + if (initialOffset !== undefined) + return buffer.slice(initialOffset, offset); + return buffer; + } + toHex() { + return this.toBuffer(undefined, undefined).toString('hex'); + } + setInputScript(index, scriptSig) { + typeforce(types.tuple(types.Number, types.Buffer), arguments); + this.ins[index].script = scriptSig; + } + setWitness(index, witness) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments); + this.ins[index].witness = witness; + } +} +Transaction.DEFAULT_SEQUENCE = 0xffffffff; +Transaction.SIGHASH_ALL = 0x01; +Transaction.SIGHASH_NONE = 0x02; +Transaction.SIGHASH_SINGLE = 0x03; +Transaction.SIGHASH_ANYONECANPAY = 0x80; +Transaction.ADVANCED_TRANSACTION_MARKER = 0x00; +Transaction.ADVANCED_TRANSACTION_FLAG = 0x01; +exports.Transaction = Transaction; diff --git a/src/transaction_builder.js b/src/transaction_builder.js new file mode 100644 index 0000000..2121156 --- /dev/null +++ b/src/transaction_builder.js @@ -0,0 +1,699 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const networks = require("./networks"); +const bufferutils_1 = require("./bufferutils"); +const transaction_1 = require("./transaction"); +const ECPair = require("./ecpair"); +const types = require("./types"); +const baddress = require("./address"); +const bcrypto = require("./crypto"); +const bscript = require("./script"); +const payments = require("./payments"); +const classify = require("./classify"); +const script_1 = require("./script"); +const typeforce = require('typeforce'); +const SCRIPT_TYPES = classify.types; +function txIsString(tx) { + return typeof tx === 'string' || tx instanceof String; +} +function txIsTransaction(tx) { + return tx instanceof transaction_1.Transaction; +} +class TransactionBuilder { + constructor(network, maximumFeeRate) { + this.__prevTxSet = {}; + this.network = network || networks.bitcoin; + // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) + this.maximumFeeRate = maximumFeeRate || 2500; + this.__inputs = []; + this.__tx = new transaction_1.Transaction(); + this.__tx.version = 2; + } + static fromTransaction(transaction, network) { + const txb = new TransactionBuilder(network); + // Copy transaction fields + txb.setVersion(transaction.version); + txb.setLockTime(transaction.locktime); + // Copy outputs (done first to avoid signature invalidation) + transaction.outs.forEach(txOut => { + txb.addOutput(txOut.script, txOut.value); + }); + // Copy inputs + transaction.ins.forEach(txIn => { + txb.__addInputUnsafe(txIn.hash, txIn.index, { + sequence: txIn.sequence, + script: txIn.script, + witness: txIn.witness + }); + }); + // fix some things not possible through the public API + txb.__inputs.forEach((input, i) => { + fixMultisigOrder(input, transaction, i); + }); + return txb; + } + setLockTime(locktime) { + typeforce(types.UInt32, locktime); + // if any signatures exist, throw + if (this.__inputs.some(input => { + if (!input.signatures) + return false; + return input.signatures.some(s => s !== undefined); + })) { + throw new Error('No, this would invalidate signatures'); + } + this.__tx.locktime = locktime; + } + setVersion(version) { + typeforce(types.UInt32, version); + // XXX: this might eventually become more complex depending on what the versions represent + this.__tx.version = version; + } + addInput(txHash, vout, sequence, prevOutScript) { + if (!this.__canModifyInputs()) { + throw new Error('No, this would invalidate signatures'); + } + let value = undefined; + // is it a hex string? + if (txIsString(txHash)) { + // transaction hashs's are displayed in reverse order, un-reverse it + txHash = bufferutils_1.reverseBuffer(Buffer.from(txHash, 'hex')); + // is it a Transaction object? + } + else if (txIsTransaction(txHash)) { + const txOut = txHash.outs[vout]; + prevOutScript = txOut.script; + value = txOut.value; + txHash = txHash.getHash(false); + } + return this.__addInputUnsafe(txHash, vout, { + sequence: sequence, + prevOutScript: prevOutScript, + value: value + }); + } + __addInputUnsafe(txHash, vout, options) { + if (transaction_1.Transaction.isCoinbaseHash(txHash)) { + throw new Error('coinbase inputs not supported'); + } + const prevTxOut = txHash.toString('hex') + ':' + vout; + if (this.__prevTxSet[prevTxOut] !== undefined) + throw new Error('Duplicate TxOut: ' + prevTxOut); + let input = {}; + // derive what we can from the scriptSig + if (options.script !== undefined) { + input = expandInput(options.script, options.witness || []); + } + // if an input value was given, retain it + if (options.value !== undefined) { + input.value = options.value; + } + // derive what we can from the previous transactions output script + if (!input.prevOutScript && options.prevOutScript) { + let prevOutType; + if (!input.pubkeys && !input.signatures) { + const expanded = expandOutput(options.prevOutScript); + if (expanded.pubkeys) { + input.pubkeys = expanded.pubkeys; + input.signatures = expanded.signatures; + } + prevOutType = expanded.type; + } + input.prevOutScript = options.prevOutScript; + input.prevOutType = prevOutType || classify.output(options.prevOutScript); + } + const vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig); + this.__inputs[vin] = input; + this.__prevTxSet[prevTxOut] = true; + return vin; + } + addOutput(scriptPubKey, value) { + if (!this.__canModifyOutputs()) { + throw new Error('No, this would invalidate signatures'); + } + // Attempt to get a script if it's a base58 or bech32 address string + if (typeof scriptPubKey === 'string') { + scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); + } + return this.__tx.addOutput(scriptPubKey, value); + } + build() { + return this.__build(false); + } + buildIncomplete() { + return this.__build(true); + } + __build(allowIncomplete) { + if (!allowIncomplete) { + if (!this.__tx.ins.length) + throw new Error('Transaction has no inputs'); + if (!this.__tx.outs.length) + throw new Error('Transaction has no outputs'); + } + const tx = this.__tx.clone(); + // create script signatures from inputs + this.__inputs.forEach((input, i) => { + if (!input.prevOutType && !allowIncomplete) + throw new Error('Transaction is not complete'); + const result = build(input.prevOutType, input, allowIncomplete); + if (!result) { + if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) + throw new Error('Unknown input type'); + if (!allowIncomplete) + throw new Error('Not enough information'); + return; + } + tx.setInputScript(i, result.input); + tx.setWitness(i, result.witness); + }); + if (!allowIncomplete) { + // do not rely on this, its merely a last resort + if (this.__overMaximumFees(tx.virtualSize())) { + throw new Error('Transaction has absurd fees'); + } + } + return tx; + } + sign(vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) { + // TODO: remove keyPair.network matching in 4.0.0 + if (keyPair.network && keyPair.network !== this.network) + throw new TypeError('Inconsistent network'); + if (!this.__inputs[vin]) + throw new Error('No input at index: ' + vin); + hashType = hashType || transaction_1.Transaction.SIGHASH_ALL; + if (this.__needsOutputs(hashType)) + throw new Error('Transaction needs outputs'); + const input = this.__inputs[vin]; + // if redeemScript was previously provided, enforce consistency + if (input.redeemScript !== undefined && + redeemScript && + !input.redeemScript.equals(redeemScript)) { + throw new Error('Inconsistent redeemScript'); + } + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey(); + if (!canSign(input)) { + if (witnessValue !== undefined) { + if (input.value !== undefined && input.value !== witnessValue) + throw new Error('Input didn\'t match witnessValue'); + typeforce(types.Satoshi, witnessValue); + input.value = witnessValue; + } + if (!canSign(input)) { + const prepared = prepareInput(input, ourPubKey, redeemScript, witnessScript); + // updates inline + Object.assign(input, prepared); + } + if (!canSign(input)) + throw Error(input.prevOutType + ' not supported'); + } + // ready to sign + let signatureHash; + if (input.hasWitness) { + signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType); + } + else { + signatureHash = this.__tx.hashForSignature(vin, input.signScript, hashType); + } + // enforce in order signing of public keys + const signed = input.pubkeys.some((pubKey, i) => { + if (!ourPubKey.equals(pubKey)) + return false; + if (input.signatures[i]) + throw new Error('Signature already exists'); + // TODO: add tests + if (ourPubKey.length !== 33 && input.hasWitness) { + throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH'); + } + const signature = keyPair.sign(signatureHash); + input.signatures[i] = bscript.signature.encode(signature, hashType); + return true; + }); + if (!signed) + throw new Error('Key pair cannot sign for this input'); + } + __canModifyInputs() { + return this.__inputs.every(input => { + if (!input.signatures) + return true; + return input.signatures.every(signature => { + if (!signature) + return true; + const hashType = signatureHashType(signature); + // if SIGHASH_ANYONECANPAY is set, signatures would not + // be invalidated by more inputs + return (hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY) !== 0; + }); + }); + } + __needsOutputs(signingHashType) { + if (signingHashType === transaction_1.Transaction.SIGHASH_ALL) { + return this.__tx.outs.length === 0; + } + // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs + // .build() will fail, but .buildIncomplete() is OK + return (this.__tx.outs.length === 0) && this.__inputs.some((input) => { + if (!input.signatures) + return false; + return input.signatures.some((signature) => { + if (!signature) + return false; // no signature, no issue + const hashType = signatureHashType(signature); + if (hashType & transaction_1.Transaction.SIGHASH_NONE) + return false; // SIGHASH_NONE doesn't care about outputs + return true; // SIGHASH_* does care + }); + }); + } + __canModifyOutputs() { + const nInputs = this.__tx.ins.length; + const nOutputs = this.__tx.outs.length; + return this.__inputs.every(input => { + if (input.signatures === undefined) + return true; + return input.signatures.every((signature => { + if (!signature) + return true; + const hashType = signatureHashType(signature); + const hashTypeMod = hashType & 0x1f; + if (hashTypeMod === transaction_1.Transaction.SIGHASH_NONE) + return true; + if (hashTypeMod === transaction_1.Transaction.SIGHASH_SINGLE) { + // if SIGHASH_SINGLE is set, and nInputs > nOutputs + // some signatures would be invalidated by the addition + // of more outputs + return nInputs <= nOutputs; + } + })); + }); + } + __overMaximumFees(bytes) { + // not all inputs will have .value defined + const incoming = this.__inputs.reduce((a, x) => a + (x.value >>> 0), 0); + // but all outputs do, and if we have any input value + // we can immediately determine if the outputs are too small + const outgoing = this.__tx.outs.reduce((a, x) => a + x.value, 0); + const fee = incoming - outgoing; + const feeRate = fee / bytes; + return feeRate > this.maximumFeeRate; + } +} +exports.TransactionBuilder = TransactionBuilder; +function expandInput(scriptSig, witnessStack, type, scriptPubKey) { + if (scriptSig.length === 0 && witnessStack.length === 0) + return {}; + if (!type) { + let ssType = classify.input(scriptSig, true); + let wsType = classify.witness(witnessStack, true); + if (ssType === SCRIPT_TYPES.NONSTANDARD) + ssType = undefined; + if (wsType === SCRIPT_TYPES.NONSTANDARD) + wsType = undefined; + type = ssType || wsType; + } + switch (type) { + case SCRIPT_TYPES.P2WPKH: { + const { output, pubkey, signature } = payments.p2wpkh({ witness: witnessStack }); + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2WPKH, + pubkeys: [pubkey], + signatures: [signature] + }; + } + case SCRIPT_TYPES.P2PKH: { + const { output, pubkey, signature } = payments.p2pkh({ input: scriptSig }); + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2PKH, + pubkeys: [pubkey], + signatures: [signature] + }; + } + case SCRIPT_TYPES.P2PK: { + const { signature } = payments.p2pk({ input: scriptSig }); + return { + prevOutType: SCRIPT_TYPES.P2PK, + pubkeys: [undefined], + signatures: [signature] + }; + } + case SCRIPT_TYPES.P2MS: { + const { m, pubkeys, signatures } = payments.p2ms({ + input: scriptSig, + output: scriptPubKey + }, { allowIncomplete: true }); + return { + prevOutType: SCRIPT_TYPES.P2MS, + pubkeys: pubkeys, + signatures: signatures, + maxSignatures: m + }; + } + } + if (type === SCRIPT_TYPES.P2SH) { + const { output, redeem } = payments.p2sh({ + input: scriptSig, + witness: witnessStack + }); + const outputType = classify.output(redeem.output); + const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output); + if (!expanded.prevOutType) + return {}; + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2SH, + redeemScript: redeem.output, + redeemScriptType: expanded.prevOutType, + witnessScript: expanded.witnessScript, + witnessScriptType: expanded.witnessScriptType, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures + }; + } + if (type === SCRIPT_TYPES.P2WSH) { + const { output, redeem } = payments.p2wsh({ + input: scriptSig, + witness: witnessStack + }); + const outputType = classify.output(redeem.output); + let expanded; + if (outputType === SCRIPT_TYPES.P2WPKH) { + expanded = expandInput(redeem.input, redeem.witness, outputType); + } + else { + expanded = expandInput(bscript.compile(redeem.witness), [], outputType, redeem.output); + } + if (!expanded.prevOutType) + return {}; + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2WSH, + witnessScript: redeem.output, + witnessScriptType: expanded.prevOutType, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures + }; + } + return { + prevOutType: SCRIPT_TYPES.NONSTANDARD, + prevOutScript: scriptSig + }; +} +// could be done in expandInput, but requires the original Transaction for hashForSignature +function fixMultisigOrder(input, transaction, vin) { + if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) + return; + if (input.pubkeys.length === input.signatures.length) + return; + const unmatched = input.signatures.concat(); + input.signatures = input.pubkeys.map(pubKey => { + const keyPair = ECPair.fromPublicKey(pubKey); + let match; + // check for a signature + unmatched.some((signature, i) => { + // skip if undefined || OP_0 + if (!signature) + return false; + // TODO: avoid O(n) hashForSignature + const parsed = bscript.signature.decode(signature); + const hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType); + // skip if signature does not match pubKey + if (!keyPair.verify(hash, parsed.signature)) + return false; + // remove matched signature from unmatched + unmatched[i] = undefined; + match = signature; + return true; + }); + return match; + }); +} +function expandOutput(script, ourPubKey) { + typeforce(types.Buffer, script); + const type = classify.output(script); + switch (type) { + case SCRIPT_TYPES.P2PKH: { + if (!ourPubKey) + return { type }; + // does our hash160(pubKey) match the output scripts? + const pkh1 = payments.p2pkh({ output: script }).hash; + const pkh2 = bcrypto.hash160(ourPubKey); + if (!pkh1.equals(pkh2)) + return { type }; + return { + type, + pubkeys: [ourPubKey], + signatures: [undefined] + }; + } + case SCRIPT_TYPES.P2WPKH: { + if (!ourPubKey) + return { type }; + // does our hash160(pubKey) match the output scripts? + const wpkh1 = payments.p2wpkh({ output: script }).hash; + const wpkh2 = bcrypto.hash160(ourPubKey); + if (!wpkh1.equals(wpkh2)) + return { type }; + return { + type, + pubkeys: [ourPubKey], + signatures: [undefined] + }; + } + case SCRIPT_TYPES.P2PK: { + const p2pk = payments.p2pk({ output: script }); + return { + type, + pubkeys: [p2pk.pubkey], + signatures: [undefined] + }; + } + case SCRIPT_TYPES.P2MS: { + const p2ms = payments.p2ms({ output: script }); + return { + type, + pubkeys: p2ms.pubkeys, + signatures: p2ms.pubkeys.map(() => undefined), + maxSignatures: p2ms.m + }; + } + } + return { type }; +} +function prepareInput(input, ourPubKey, redeemScript, witnessScript) { + if (redeemScript && witnessScript) { + const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }); + const p2wshAlt = payments.p2wsh({ output: redeemScript }); + const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); + const p2shAlt = payments.p2sh({ redeem: p2wsh }); + // enforces P2SH(P2WSH(...)) + if (!p2wsh.hash.equals(p2wshAlt.hash)) + throw new Error('Witness script inconsistent with prevOutScript'); + if (!p2sh.hash.equals(p2shAlt.hash)) + throw new Error('Redeem script inconsistent with prevOutScript'); + const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); + if (!expanded.pubkeys) + throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')'); + if (input.signatures && input.signatures.some(x => x !== undefined)) { + expanded.signatures = input.signatures; + } + let signScript = witnessScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) + throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure'); + return { + redeemScript, + redeemScriptType: SCRIPT_TYPES.P2WSH, + witnessScript, + witnessScriptType: expanded.type, + prevOutType: SCRIPT_TYPES.P2SH, + prevOutScript: p2sh.output, + hasWitness: true, + signScript, + signType: expanded.type, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures + }; + } + if (redeemScript) { + const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); + if (input.prevOutScript) { + let p2shAlt; + try { + p2shAlt = payments.p2sh({ output: input.prevOutScript }); + } + catch (e) { + throw new Error('PrevOutScript must be P2SH'); + } + if (!p2sh.hash.equals(p2shAlt.hash)) + throw new Error('Redeem script inconsistent with prevOutScript'); + } + const expanded = expandOutput(p2sh.redeem.output, ourPubKey); + if (!expanded.pubkeys) + throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')'); + if (input.signatures && input.signatures.some(x => x !== undefined)) { + expanded.signatures = input.signatures; + } + let signScript = redeemScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) { + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output; + } + return { + redeemScript, + redeemScriptType: expanded.type, + prevOutType: SCRIPT_TYPES.P2SH, + prevOutScript: p2sh.output, + hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, + signScript, + signType: expanded.type, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures + }; + } + if (witnessScript) { + const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }); + if (input.prevOutScript) { + const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }); + if (!p2wsh.hash.equals(p2wshAlt.hash)) + throw new Error('Witness script inconsistent with prevOutScript'); + } + const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); + if (!expanded.pubkeys) + throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')'); + if (input.signatures && input.signatures.some(x => x !== undefined)) { + expanded.signatures = input.signatures; + } + let signScript = witnessScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) + throw new Error('P2WSH(P2WPKH) is a consensus failure'); + return { + witnessScript, + witnessScriptType: expanded.type, + prevOutType: SCRIPT_TYPES.P2WSH, + prevOutScript: p2wsh.output, + hasWitness: true, + signScript, + signType: expanded.type, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures + }; + } + if (input.prevOutType && input.prevOutScript) { + // embedded scripts are not possible without extra information + if (input.prevOutType === SCRIPT_TYPES.P2SH) + throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript'); + if (input.prevOutType === SCRIPT_TYPES.P2WSH) + throw new Error('PrevOutScript is ' + input.prevOutType + ', requires witnessScript'); + if (!input.prevOutScript) + throw new Error('PrevOutScript is missing'); + const expanded = expandOutput(input.prevOutScript, ourPubKey); + if (!expanded.pubkeys) + throw new Error(expanded.type + ' not supported (' + bscript.toASM(input.prevOutScript) + ')'); + if (input.signatures && input.signatures.some(x => x !== undefined)) { + expanded.signatures = input.signatures; + } + let signScript = input.prevOutScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) { + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output; + } + return { + prevOutType: expanded.type, + prevOutScript: input.prevOutScript, + hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, + signScript, + signType: expanded.type, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures + }; + } + const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output; + return { + prevOutType: SCRIPT_TYPES.P2PKH, + prevOutScript: prevOutScript, + hasWitness: false, + signScript: prevOutScript, + signType: SCRIPT_TYPES.P2PKH, + pubkeys: [ourPubKey], + signatures: [undefined] + }; +} +function build(type, input, allowIncomplete) { + const pubkeys = (input.pubkeys || []); + let signatures = (input.signatures || []); + switch (type) { + case SCRIPT_TYPES.P2PKH: { + if (pubkeys.length === 0) + break; + if (signatures.length === 0) + break; + return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] }); + } + case SCRIPT_TYPES.P2WPKH: { + if (pubkeys.length === 0) + break; + if (signatures.length === 0) + break; + return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] }); + } + case SCRIPT_TYPES.P2PK: { + if (pubkeys.length === 0) + break; + if (signatures.length === 0) + break; + return payments.p2pk({ signature: signatures[0] }); + } + case SCRIPT_TYPES.P2MS: { + const m = input.maxSignatures; + if (allowIncomplete) { + signatures = signatures.map(x => x || script_1.OPS.OP_0); + } + else { + signatures = signatures.filter(x => x); + } + // if the transaction is not not complete (complete), or if signatures.length === m, validate + // otherwise, the number of OP_0's may be >= m, so don't validate (boo) + const validate = !allowIncomplete || (m === signatures.length); + return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate }); + } + case SCRIPT_TYPES.P2SH: { + const redeem = build(input.redeemScriptType, input, allowIncomplete); + if (!redeem) + return; + return payments.p2sh({ + redeem: { + output: redeem.output || input.redeemScript, + input: redeem.input, + witness: redeem.witness + } + }); + } + case SCRIPT_TYPES.P2WSH: { + const redeem = build(input.witnessScriptType, input, allowIncomplete); + if (!redeem) + return; + return payments.p2wsh({ + redeem: { + output: input.witnessScript, + input: redeem.input, + witness: redeem.witness + } + }); + } + } +} +function canSign(input) { + return input.signScript !== undefined && + input.signType !== undefined && + input.pubkeys !== undefined && + input.signatures !== undefined && + input.signatures.length === input.pubkeys.length && + input.pubkeys.length > 0 && + (input.hasWitness === false || + input.value !== undefined); +} +function signatureHashType(buffer) { + return buffer.readUInt8(buffer.length - 1); +} diff --git a/src/types.js b/src/types.js new file mode 100644 index 0000000..13d1bc8 --- /dev/null +++ b/src/types.js @@ -0,0 +1,48 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const typeforce = require('typeforce'); +const UINT31_MAX = Math.pow(2, 31) - 1; +function UInt31(value) { + return typeforce.UInt32(value) && value <= UINT31_MAX; +} +exports.UInt31 = UInt31; +function BIP32Path(value) { + return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); +} +exports.BIP32Path = BIP32Path; +BIP32Path.toJSON = function () { return 'BIP32 derivation path'; }; +const SATOSHI_MAX = 21 * 1e14; +function Satoshi(value) { + return typeforce.UInt53(value) && value <= SATOSHI_MAX; +} +exports.Satoshi = Satoshi; +// external dependent types +exports.ECPoint = typeforce.quacksLike('Point'); +// exposed, external API +exports.Network = typeforce.compile({ + messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), + bip32: { + public: typeforce.UInt32, + private: typeforce.UInt32 + }, + pubKeyHash: typeforce.UInt8, + scriptHash: typeforce.UInt8, + wif: typeforce.UInt8 +}); +exports.Buffer256bit = typeforce.BufferN(32); +exports.Hash160bit = typeforce.BufferN(20); +exports.Hash256bit = typeforce.BufferN(32); +exports.Number = typeforce.Number; +exports.Array = typeforce.Array; +exports.Boolean = typeforce.Boolean; +exports.String = typeforce.String; +exports.Buffer = typeforce.Buffer; +exports.Hex = typeforce.Hex; +exports.maybe = typeforce.maybe; +exports.tuple = typeforce.tuple; +exports.UInt8 = typeforce.UInt8; +exports.UInt32 = typeforce.UInt32; +exports.Function = typeforce.Function; +exports.BufferN = typeforce.BufferN; +exports.Null = typeforce.Null; +exports.oneOf = typeforce.oneOf; diff --git a/test/address.js b/test/address.js index 19093b6..a0f4df0 100644 --- a/test/address.js +++ b/test/address.js @@ -1,7 +1,7 @@ const { describe, it } = require('mocha') const assert = require('assert') -const baddress = require('../dist/src/address') -const bscript = require('../dist/src/script') +const baddress = require('../src/address') +const bscript = require('../src/script') const fixtures = require('./fixtures/address.json') const NETWORKS = Object.assign({ litecoin: { @@ -14,7 +14,7 @@ const NETWORKS = Object.assign({ scriptHash: 0x32, wif: 0xb0 } -}, require('../dist/src/networks')) +}, require('../src/networks')) describe('address', function () { describe('fromBase58Check', function () { diff --git a/test/bufferutils.js b/test/bufferutils.js index ceb649a..5f2c39e 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bufferutils = require('../dist/src/bufferutils') +const bufferutils = require('../src/bufferutils') const fixtures = require('./fixtures/bufferutils.json') diff --git a/test/classify.js b/test/classify.js index 33225b5..3efcc74 100644 --- a/test/classify.js +++ b/test/classify.js @@ -1,18 +1,18 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bscript = require('../dist/src/script') -const classify = require('../dist/src/classify') +const bscript = require('../src/script') +const classify = require('../src/classify') const fixtures = require('./fixtures/templates.json') -const multisig = require('../dist/src/templates/multisig') -const nullData = require('../dist/src/templates/nulldata') -const pubKey = require('../dist/src/templates/pubkey') -const pubKeyHash = require('../dist/src/templates/pubkeyhash') -const scriptHash = require('../dist/src/templates/scripthash') -const witnessPubKeyHash = require('../dist/src/templates/witnesspubkeyhash') -const witnessScriptHash = require('../dist/src/templates/witnessscripthash') -const witnessCommitment = require('../dist/src/templates/witnesscommitment') +const multisig = require('../src/templates/multisig') +const nullData = require('../src/templates/nulldata') +const pubKey = require('../src/templates/pubkey') +const pubKeyHash = require('../src/templates/pubkeyhash') +const scriptHash = require('../src/templates/scripthash') +const witnessPubKeyHash = require('../src/templates/witnesspubkeyhash') +const witnessScriptHash = require('../src/templates/witnessscripthash') +const witnessCommitment = require('../src/templates/witnesscommitment') const tmap = { pubKey, diff --git a/test/crypto.js b/test/crypto.js index 0a01c98..3f7802a 100644 --- a/test/crypto.js +++ b/test/crypto.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bcrypto = require('../dist/src/crypto') +const bcrypto = require('../src/crypto') const fixtures = require('./fixtures/crypto') diff --git a/test/ecpair.js b/test/ecpair.js index f590e18..4299470 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -5,12 +5,12 @@ const assert = require('assert') const proxyquire = require('proxyquire') const hoodwink = require('hoodwink') -const ECPair = require('../dist/src/ecpair') +const ECPair = require('../src/ecpair') const tinysecp = require('tiny-secp256k1') const fixtures = require('./fixtures/ecpair.json') -const NETWORKS = require('../dist/src/networks') +const NETWORKS = require('../src/networks') const NETWORKS_LIST = [] // Object.values(NETWORKS) for (let networkName in NETWORKS) { NETWORKS_LIST.push(NETWORKS[networkName]) @@ -144,7 +144,7 @@ describe('ECPair', function () { describe('uses randombytes RNG', function () { it('generates a ECPair', function () { const stub = { randombytes: function () { return d } } - const ProxiedECPair = proxyquire('../dist/src/ecpair', stub) + const ProxiedECPair = proxyquire('../src/ecpair', stub) const keyPair = ProxiedECPair.makeRandom() assert.strictEqual(keyPair.toWIF(), exWIF) diff --git a/test/payments.js b/test/payments.js index 6617047..a4f07bb 100644 --- a/test/payments.js +++ b/test/payments.js @@ -5,7 +5,7 @@ const u = require('./payments.utils') ;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { describe(p, function () { let fn - let payment = require('../dist/src/payments/' + p) + let payment = require('../src/payments/' + p) if (p === 'embed') { fn = payment.p2data } else { diff --git a/test/payments.utils.js b/test/payments.utils.js index 086561d..485bf03 100644 --- a/test/payments.utils.js +++ b/test/payments.utils.js @@ -1,6 +1,6 @@ const t = require('assert') -const bscript = require('../dist/src/script') -const BNETWORKS = require('../dist/src/networks') +const bscript = require('../src/script') +const BNETWORKS = require('../src/networks') function tryHex (x) { if (Buffer.isBuffer(x)) return x.toString('hex') diff --git a/test/script.js b/test/script.js index 013d50a..c2a60ad 100644 --- a/test/script.js +++ b/test/script.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bscript = require('../dist/src/script') +const bscript = require('../src/script') const minimalData = require('minimaldata') const fixtures = require('./fixtures/script.json') diff --git a/test/script_number.js b/test/script_number.js index 1d38e48..bc8f395 100644 --- a/test/script_number.js +++ b/test/script_number.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const scriptNumber = require('../dist/src/script_number') +const scriptNumber = require('../src/script_number') const fixtures = require('./fixtures/script_number.json') describe('script-number', function () { diff --git a/test/script_signature.js b/test/script_signature.js index e56ff68..cee69bd 100644 --- a/test/script_signature.js +++ b/test/script_signature.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const bscriptSig = require('../dist/src/script').signature +const bscriptSig = require('../src/script').signature const Buffer = require('safe-buffer').Buffer const fixtures = require('./fixtures/signature.json') diff --git a/test/transaction.js b/test/transaction.js index 74afc8f..d923ce2 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -1,6 +1,6 @@ const { describe, it, beforeEach } = require('mocha') const assert = require('assert') -const bscript = require('../dist/src/script') +const bscript = require('../src/script') const fixtures = require('./fixtures/transaction') const Transaction = require('..').Transaction diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 34d9270..574b669 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -1,13 +1,13 @@ const { describe, it, beforeEach } = require('mocha') const assert = require('assert') -const baddress = require('../dist/src/address') -const bscript = require('../dist/src/script') -const payments = require('../dist/src/payments') +const baddress = require('../src/address') +const bscript = require('../src/script') +const payments = require('../src/payments') -const ECPair = require('../dist/src/ecpair') +const ECPair = require('../src/ecpair') const Transaction = require('..').Transaction const TransactionBuilder = require('..').TransactionBuilder -const NETWORKS = require('../dist/src/networks') +const NETWORKS = require('../src/networks') const fixtures = require('./fixtures/transaction_builder') diff --git a/test/types.js b/test/types.js index 831d415..d245d53 100644 --- a/test/types.js +++ b/test/types.js @@ -1,6 +1,6 @@ const { describe, it } = require('mocha') const assert = require('assert') -const types = require('../dist/src/types') +const types = require('../src/types') const typeforce = require('typeforce') describe('types', function () { diff --git a/src/address.ts b/ts_src/address.ts similarity index 100% rename from src/address.ts rename to ts_src/address.ts diff --git a/src/block.ts b/ts_src/block.ts similarity index 100% rename from src/block.ts rename to ts_src/block.ts diff --git a/src/bufferutils.ts b/ts_src/bufferutils.ts similarity index 100% rename from src/bufferutils.ts rename to ts_src/bufferutils.ts diff --git a/src/classify.ts b/ts_src/classify.ts similarity index 100% rename from src/classify.ts rename to ts_src/classify.ts diff --git a/src/crypto.ts b/ts_src/crypto.ts similarity index 100% rename from src/crypto.ts rename to ts_src/crypto.ts diff --git a/src/ecpair.ts b/ts_src/ecpair.ts similarity index 100% rename from src/ecpair.ts rename to ts_src/ecpair.ts diff --git a/src/index.ts b/ts_src/index.ts similarity index 100% rename from src/index.ts rename to ts_src/index.ts diff --git a/src/networks.ts b/ts_src/networks.ts similarity index 100% rename from src/networks.ts rename to ts_src/networks.ts diff --git a/src/payments/embed.ts b/ts_src/payments/embed.ts similarity index 100% rename from src/payments/embed.ts rename to ts_src/payments/embed.ts diff --git a/src/payments/index.ts b/ts_src/payments/index.ts similarity index 100% rename from src/payments/index.ts rename to ts_src/payments/index.ts diff --git a/src/payments/lazy.ts b/ts_src/payments/lazy.ts similarity index 100% rename from src/payments/lazy.ts rename to ts_src/payments/lazy.ts diff --git a/src/payments/p2ms.ts b/ts_src/payments/p2ms.ts similarity index 100% rename from src/payments/p2ms.ts rename to ts_src/payments/p2ms.ts diff --git a/src/payments/p2pk.ts b/ts_src/payments/p2pk.ts similarity index 100% rename from src/payments/p2pk.ts rename to ts_src/payments/p2pk.ts diff --git a/src/payments/p2pkh.ts b/ts_src/payments/p2pkh.ts similarity index 100% rename from src/payments/p2pkh.ts rename to ts_src/payments/p2pkh.ts diff --git a/src/payments/p2sh.ts b/ts_src/payments/p2sh.ts similarity index 100% rename from src/payments/p2sh.ts rename to ts_src/payments/p2sh.ts diff --git a/src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts similarity index 100% rename from src/payments/p2wpkh.ts rename to ts_src/payments/p2wpkh.ts diff --git a/src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts similarity index 100% rename from src/payments/p2wsh.ts rename to ts_src/payments/p2wsh.ts diff --git a/src/script.ts b/ts_src/script.ts similarity index 100% rename from src/script.ts rename to ts_src/script.ts diff --git a/src/script_number.ts b/ts_src/script_number.ts similarity index 100% rename from src/script_number.ts rename to ts_src/script_number.ts diff --git a/src/script_signature.ts b/ts_src/script_signature.ts similarity index 100% rename from src/script_signature.ts rename to ts_src/script_signature.ts diff --git a/src/templates/multisig/index.ts b/ts_src/templates/multisig/index.ts similarity index 100% rename from src/templates/multisig/index.ts rename to ts_src/templates/multisig/index.ts diff --git a/src/templates/multisig/input.ts b/ts_src/templates/multisig/input.ts similarity index 100% rename from src/templates/multisig/input.ts rename to ts_src/templates/multisig/input.ts diff --git a/src/templates/multisig/output.ts b/ts_src/templates/multisig/output.ts similarity index 100% rename from src/templates/multisig/output.ts rename to ts_src/templates/multisig/output.ts diff --git a/src/templates/nulldata.ts b/ts_src/templates/nulldata.ts similarity index 100% rename from src/templates/nulldata.ts rename to ts_src/templates/nulldata.ts diff --git a/src/templates/pubkey/index.ts b/ts_src/templates/pubkey/index.ts similarity index 100% rename from src/templates/pubkey/index.ts rename to ts_src/templates/pubkey/index.ts diff --git a/src/templates/pubkey/input.ts b/ts_src/templates/pubkey/input.ts similarity index 100% rename from src/templates/pubkey/input.ts rename to ts_src/templates/pubkey/input.ts diff --git a/src/templates/pubkey/output.ts b/ts_src/templates/pubkey/output.ts similarity index 100% rename from src/templates/pubkey/output.ts rename to ts_src/templates/pubkey/output.ts diff --git a/src/templates/pubkeyhash/index.ts b/ts_src/templates/pubkeyhash/index.ts similarity index 100% rename from src/templates/pubkeyhash/index.ts rename to ts_src/templates/pubkeyhash/index.ts diff --git a/src/templates/pubkeyhash/input.ts b/ts_src/templates/pubkeyhash/input.ts similarity index 100% rename from src/templates/pubkeyhash/input.ts rename to ts_src/templates/pubkeyhash/input.ts diff --git a/src/templates/pubkeyhash/output.ts b/ts_src/templates/pubkeyhash/output.ts similarity index 100% rename from src/templates/pubkeyhash/output.ts rename to ts_src/templates/pubkeyhash/output.ts diff --git a/src/templates/scripthash/index.ts b/ts_src/templates/scripthash/index.ts similarity index 100% rename from src/templates/scripthash/index.ts rename to ts_src/templates/scripthash/index.ts diff --git a/src/templates/scripthash/input.ts b/ts_src/templates/scripthash/input.ts similarity index 100% rename from src/templates/scripthash/input.ts rename to ts_src/templates/scripthash/input.ts diff --git a/src/templates/scripthash/output.ts b/ts_src/templates/scripthash/output.ts similarity index 100% rename from src/templates/scripthash/output.ts rename to ts_src/templates/scripthash/output.ts diff --git a/src/templates/witnesscommitment/index.ts b/ts_src/templates/witnesscommitment/index.ts similarity index 100% rename from src/templates/witnesscommitment/index.ts rename to ts_src/templates/witnesscommitment/index.ts diff --git a/src/templates/witnesscommitment/output.ts b/ts_src/templates/witnesscommitment/output.ts similarity index 100% rename from src/templates/witnesscommitment/output.ts rename to ts_src/templates/witnesscommitment/output.ts diff --git a/src/templates/witnesspubkeyhash/index.ts b/ts_src/templates/witnesspubkeyhash/index.ts similarity index 100% rename from src/templates/witnesspubkeyhash/index.ts rename to ts_src/templates/witnesspubkeyhash/index.ts diff --git a/src/templates/witnesspubkeyhash/input.ts b/ts_src/templates/witnesspubkeyhash/input.ts similarity index 100% rename from src/templates/witnesspubkeyhash/input.ts rename to ts_src/templates/witnesspubkeyhash/input.ts diff --git a/src/templates/witnesspubkeyhash/output.ts b/ts_src/templates/witnesspubkeyhash/output.ts similarity index 100% rename from src/templates/witnesspubkeyhash/output.ts rename to ts_src/templates/witnesspubkeyhash/output.ts diff --git a/src/templates/witnessscripthash/index.ts b/ts_src/templates/witnessscripthash/index.ts similarity index 100% rename from src/templates/witnessscripthash/index.ts rename to ts_src/templates/witnessscripthash/index.ts diff --git a/src/templates/witnessscripthash/input.ts b/ts_src/templates/witnessscripthash/input.ts similarity index 100% rename from src/templates/witnessscripthash/input.ts rename to ts_src/templates/witnessscripthash/input.ts diff --git a/src/templates/witnessscripthash/output.ts b/ts_src/templates/witnessscripthash/output.ts similarity index 100% rename from src/templates/witnessscripthash/output.ts rename to ts_src/templates/witnessscripthash/output.ts diff --git a/src/transaction.ts b/ts_src/transaction.ts similarity index 100% rename from src/transaction.ts rename to ts_src/transaction.ts diff --git a/src/transaction_builder.ts b/ts_src/transaction_builder.ts similarity index 100% rename from src/transaction_builder.ts rename to ts_src/transaction_builder.ts diff --git a/src/types.ts b/ts_src/types.ts similarity index 100% rename from src/types.ts rename to ts_src/types.ts diff --git a/tsconfig.json b/tsconfig.json index 1d68be9..787769d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,8 +2,9 @@ "compilerOptions": { "target": "ES2015", "module": "commonjs", - "outDir": "./dist", - "rootDir": "./", + "outDir": "./src", + "declarationDir": "./types", + "rootDir": "./ts_src", "types": [ "node" ], @@ -19,7 +20,7 @@ "esModuleInterop": false }, "include": [ - "src/**/*" + "ts_src/**/*.ts" ], "exclude": [ "**/*.spec.ts", diff --git a/types/address.d.ts b/types/address.d.ts new file mode 100644 index 0000000..68fbedd --- /dev/null +++ b/types/address.d.ts @@ -0,0 +1,17 @@ +/// <reference types="node" /> +import { Network } from './networks'; +export declare type Base58CheckResult = { + hash: Buffer; + version: number; +}; +export declare type Bech32Result = { + version: number; + prefix: string; + data: Buffer; +}; +export declare function fromBase58Check(address: string): Base58CheckResult; +export declare function fromBech32(address: string): Bech32Result; +export declare function toBase58Check(hash: Buffer, version: number): string; +export declare function toBech32(data: Buffer, version: number, prefix: string): string; +export declare function fromOutputScript(output: Buffer, network: Network): string; +export declare function toOutputScript(address: string, network: Network): Buffer; diff --git a/types/block.d.ts b/types/block.d.ts new file mode 100644 index 0000000..ff98de2 --- /dev/null +++ b/types/block.d.ts @@ -0,0 +1,27 @@ +/// <reference types="node" /> +import { Transaction } from './transaction'; +export declare class Block { + version: number; + prevHash?: Buffer; + merkleRoot?: Buffer; + timestamp: number; + witnessCommit?: Buffer; + bits: number; + nonce: number; + transactions?: Array<Transaction>; + constructor(); + static fromBuffer(buffer: Buffer): Block; + static fromHex(hex: string): Block; + static calculateTarget(bits: number): Buffer; + static calculateMerkleRoot(transactions: Array<Transaction>, forWitness?: boolean): Buffer; + hasWitnessCommit(): boolean; + byteLength(headersOnly: boolean): number; + getHash(): Buffer; + getId(): string; + getUTCDate(): Date; + toBuffer(headersOnly: boolean): Buffer; + toHex(headersOnly: boolean): string; + checkMerkleRoot(): boolean; + checkWitnessCommit(): boolean; + checkProofOfWork(): boolean; +} diff --git a/types/bufferutils.d.ts b/types/bufferutils.d.ts new file mode 100644 index 0000000..2686e4e --- /dev/null +++ b/types/bufferutils.d.ts @@ -0,0 +1,4 @@ +/// <reference types="node" /> +export declare function readUInt64LE(buffer: Buffer, offset: number): number; +export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number; +export declare function reverseBuffer(buffer: Buffer): Buffer; diff --git a/types/classify.d.ts b/types/classify.d.ts new file mode 100644 index 0000000..b394c71 --- /dev/null +++ b/types/classify.d.ts @@ -0,0 +1,16 @@ +/// <reference types="node" /> +declare const types: { + P2MS: string; + NONSTANDARD: string; + NULLDATA: string; + P2PK: string; + P2PKH: string; + P2SH: string; + P2WPKH: string; + P2WSH: string; + WITNESS_COMMITMENT: string; +}; +declare function classifyOutput(script: Buffer): string; +declare function classifyInput(script: Buffer, allowIncomplete: boolean): string; +declare function classifyWitness(script: Array<Buffer>, allowIncomplete: boolean): string; +export { classifyInput as input, classifyOutput as output, classifyWitness as witness, types, }; diff --git a/types/crypto.d.ts b/types/crypto.d.ts new file mode 100644 index 0000000..1743681 --- /dev/null +++ b/types/crypto.d.ts @@ -0,0 +1,6 @@ +/// <reference types="node" /> +export declare function ripemd160(buffer: Buffer): Buffer; +export declare function sha1(buffer: Buffer): Buffer; +export declare function sha256(buffer: Buffer): Buffer; +export declare function hash160(buffer: Buffer): Buffer; +export declare function hash256(buffer: Buffer): Buffer; diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts new file mode 100644 index 0000000..58ea4be --- /dev/null +++ b/types/ecpair.d.ts @@ -0,0 +1,34 @@ +/// <reference types="node" /> +import { Network } from './networks'; +interface ECPairOptions { + compressed?: boolean; + network?: Network; + rng?(arg0: Buffer): Buffer; +} +export interface ECPairInterface { + compressed: boolean; + network: Network; + privateKey?: Buffer; + publicKey?: Buffer; + toWIF(): string; + sign(hash: Buffer): Buffer; + verify(hash: Buffer, signature: Buffer): Buffer; + getPublicKey?(): Buffer; +} +declare class ECPair implements ECPairInterface { + compressed: boolean; + network: Network; + private __d?; + private __Q?; + constructor(d?: Buffer, Q?: Buffer, options?: ECPairOptions); + readonly privateKey: Buffer | undefined; + readonly publicKey: Buffer | undefined; + toWIF(): string; + sign(hash: Buffer): Buffer; + verify(hash: Buffer, signature: Buffer): Buffer; +} +declare function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair; +declare function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair; +declare function fromWIF(string: string, network?: Network | Array<Network>): ECPair; +declare function makeRandom(options?: ECPairOptions): ECPair; +export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF }; diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..9682271 --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,17 @@ +import * as bip32 from 'bip32'; +import * as ECPair from './ecpair'; +import * as address from './address'; +import * as crypto from './crypto'; +import * as networks from './networks'; +import * as payments from './payments'; +import * as script from './script'; +export { ECPair, address, bip32, crypto, networks, payments, script, }; +export { Block } from './block'; +export { Transaction } from './transaction'; +export { TransactionBuilder } from './transaction_builder'; +export { OPS as opcodes } from './script'; +export { Payment, PaymentOpts } from './payments'; +export { Input as TxInput, Output as TxOutput } from './transaction'; +export { Network } from './networks'; +export { OpCode } from './script'; +export { BIP32Interface } from 'bip32'; diff --git a/types/networks.d.ts b/types/networks.d.ts new file mode 100644 index 0000000..e402873 --- /dev/null +++ b/types/networks.d.ts @@ -0,0 +1,16 @@ +export declare type Network = { + messagePrefix: string; + bech32: string; + bip32: bip32; + pubKeyHash: number; + scriptHash: number; + wif: number; +}; +declare type bip32 = { + public: number; + private: number; +}; +export declare const bitcoin: Network; +export declare const regtest: Network; +export declare const testnet: Network; +export {}; diff --git a/types/payments/embed.d.ts b/types/payments/embed.d.ts new file mode 100644 index 0000000..76a9ed2 --- /dev/null +++ b/types/payments/embed.d.ts @@ -0,0 +1,2 @@ +import { Payment, PaymentOpts } from './index'; +export declare function p2data(a: Payment, opts?: PaymentOpts): Payment; diff --git a/types/payments/index.d.ts b/types/payments/index.d.ts new file mode 100644 index 0000000..5afc847 --- /dev/null +++ b/types/payments/index.d.ts @@ -0,0 +1,30 @@ +/// <reference types="node" /> +import { Network } from '../networks'; +import { p2data as embed } from './embed'; +import { p2ms } from './p2ms'; +import { p2pk } from './p2pk'; +import { p2pkh } from './p2pkh'; +import { p2sh } from './p2sh'; +import { p2wpkh } from './p2wpkh'; +import { p2wsh } from './p2wsh'; +export interface Payment { + network?: Network; + output?: Buffer; + data?: Array<Buffer>; + m?: number; + n?: number; + pubkeys?: Array<Buffer>; + input?: Buffer; + signatures?: Array<Buffer>; + pubkey?: Buffer; + signature?: Buffer; + address?: string; + hash?: Buffer; + redeem?: Payment; + witness?: Array<Buffer>; +} +export interface PaymentOpts { + validate?: boolean; + allowIncomplete?: boolean; +} +export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }; diff --git a/types/payments/lazy.d.ts b/types/payments/lazy.d.ts new file mode 100644 index 0000000..705f530 --- /dev/null +++ b/types/payments/lazy.d.ts @@ -0,0 +1,2 @@ +export declare function prop(object: Object, name: string, f: () => any): void; +export declare function value<T>(f: () => T): () => T; diff --git a/types/payments/p2ms.d.ts b/types/payments/p2ms.d.ts new file mode 100644 index 0000000..199e029 --- /dev/null +++ b/types/payments/p2ms.d.ts @@ -0,0 +1,2 @@ +import { Payment, PaymentOpts } from './index'; +export declare function p2ms(a: Payment, opts?: PaymentOpts): Payment; diff --git a/types/payments/p2pk.d.ts b/types/payments/p2pk.d.ts new file mode 100644 index 0000000..d7e824d --- /dev/null +++ b/types/payments/p2pk.d.ts @@ -0,0 +1,2 @@ +import { Payment, PaymentOpts } from './index'; +export declare function p2pk(a: Payment, opts?: PaymentOpts): Payment; diff --git a/types/payments/p2pkh.d.ts b/types/payments/p2pkh.d.ts new file mode 100644 index 0000000..a33eeb0 --- /dev/null +++ b/types/payments/p2pkh.d.ts @@ -0,0 +1,2 @@ +import { Payment, PaymentOpts } from './index'; +export declare function p2pkh(a: Payment, opts?: PaymentOpts): Payment; diff --git a/types/payments/p2sh.d.ts b/types/payments/p2sh.d.ts new file mode 100644 index 0000000..bb76772 --- /dev/null +++ b/types/payments/p2sh.d.ts @@ -0,0 +1,2 @@ +import { Payment, PaymentOpts } from './index'; +export declare function p2sh(a: Payment, opts?: PaymentOpts): Payment; diff --git a/types/payments/p2wpkh.d.ts b/types/payments/p2wpkh.d.ts new file mode 100644 index 0000000..3609391 --- /dev/null +++ b/types/payments/p2wpkh.d.ts @@ -0,0 +1,2 @@ +import { Payment, PaymentOpts } from './index'; +export declare function p2wpkh(a: Payment, opts?: PaymentOpts): Payment; diff --git a/types/payments/p2wsh.d.ts b/types/payments/p2wsh.d.ts new file mode 100644 index 0000000..d9ae925 --- /dev/null +++ b/types/payments/p2wsh.d.ts @@ -0,0 +1,2 @@ +import { Payment, PaymentOpts } from './index'; +export declare function p2wsh(a: Payment, opts?: PaymentOpts): Payment; diff --git a/types/script.d.ts b/types/script.d.ts new file mode 100644 index 0000000..702e76b --- /dev/null +++ b/types/script.d.ts @@ -0,0 +1,18 @@ +/// <reference types="node" /> +import * as scriptNumber from './script_number'; +import * as scriptSignature from './script_signature'; +export declare type OpCode = number; +export declare const OPS: { + [index: string]: number; +}; +export declare function isPushOnly(value: Array<number | Buffer>): boolean; +export declare function compile(chunks: Buffer | Array<number | Buffer>): Buffer; +export declare function decompile(buffer: Buffer | Array<number | Buffer>): Array<number | Buffer> | null; +export declare function toASM(chunks: Buffer | Array<number | Buffer>): string; +export declare function fromASM(asm: string): Buffer; +export declare function toStack(chunks: Buffer | Array<number | Buffer>): Array<Buffer>; +export declare function isCanonicalPubKey(buffer: Buffer): boolean; +export declare function isDefinedHashType(hashType: number): boolean; +export declare function isCanonicalScriptSignature(buffer: Buffer): boolean; +export declare const number: typeof scriptNumber; +export declare const signature: typeof scriptSignature; diff --git a/types/script_number.d.ts b/types/script_number.d.ts new file mode 100644 index 0000000..d0b87b1 --- /dev/null +++ b/types/script_number.d.ts @@ -0,0 +1,3 @@ +/// <reference types="node" /> +export declare function decode(buffer: Buffer, maxLength?: number, minimal?: boolean): number; +export declare function encode(number: number): Buffer; diff --git a/types/script_signature.d.ts b/types/script_signature.d.ts new file mode 100644 index 0000000..2057dd9 --- /dev/null +++ b/types/script_signature.d.ts @@ -0,0 +1,8 @@ +/// <reference types="node" /> +interface ScriptSignature { + signature: Buffer; + hashType: number; +} +export declare function decode(buffer: Buffer): ScriptSignature; +export declare function encode(signature: Buffer, hashType: number): Buffer; +export {}; diff --git a/types/templates/multisig/index.d.ts b/types/templates/multisig/index.d.ts new file mode 100644 index 0000000..cff90ee --- /dev/null +++ b/types/templates/multisig/index.d.ts @@ -0,0 +1,3 @@ +import * as input from './input'; +import * as output from './output'; +export { input, output, }; diff --git a/types/templates/multisig/input.d.ts b/types/templates/multisig/input.d.ts new file mode 100644 index 0000000..a04d03c --- /dev/null +++ b/types/templates/multisig/input.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/multisig/output.d.ts b/types/templates/multisig/output.d.ts new file mode 100644 index 0000000..a04d03c --- /dev/null +++ b/types/templates/multisig/output.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/nulldata.d.ts b/types/templates/nulldata.d.ts new file mode 100644 index 0000000..aff3cd0 --- /dev/null +++ b/types/templates/nulldata.d.ts @@ -0,0 +1,9 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} +declare const output: { + check: typeof check; +}; +export { output }; diff --git a/types/templates/pubkey/index.d.ts b/types/templates/pubkey/index.d.ts new file mode 100644 index 0000000..cff90ee --- /dev/null +++ b/types/templates/pubkey/index.d.ts @@ -0,0 +1,3 @@ +import * as input from './input'; +import * as output from './output'; +export { input, output, }; diff --git a/types/templates/pubkey/input.d.ts b/types/templates/pubkey/input.d.ts new file mode 100644 index 0000000..091758f --- /dev/null +++ b/types/templates/pubkey/input.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/pubkey/output.d.ts b/types/templates/pubkey/output.d.ts new file mode 100644 index 0000000..091758f --- /dev/null +++ b/types/templates/pubkey/output.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/pubkeyhash/index.d.ts b/types/templates/pubkeyhash/index.d.ts new file mode 100644 index 0000000..cff90ee --- /dev/null +++ b/types/templates/pubkeyhash/index.d.ts @@ -0,0 +1,3 @@ +import * as input from './input'; +import * as output from './output'; +export { input, output, }; diff --git a/types/templates/pubkeyhash/input.d.ts b/types/templates/pubkeyhash/input.d.ts new file mode 100644 index 0000000..091758f --- /dev/null +++ b/types/templates/pubkeyhash/input.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/pubkeyhash/output.d.ts b/types/templates/pubkeyhash/output.d.ts new file mode 100644 index 0000000..091758f --- /dev/null +++ b/types/templates/pubkeyhash/output.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/scripthash/index.d.ts b/types/templates/scripthash/index.d.ts new file mode 100644 index 0000000..cff90ee --- /dev/null +++ b/types/templates/scripthash/index.d.ts @@ -0,0 +1,3 @@ +import * as input from './input'; +import * as output from './output'; +export { input, output, }; diff --git a/types/templates/scripthash/input.d.ts b/types/templates/scripthash/input.d.ts new file mode 100644 index 0000000..a04d03c --- /dev/null +++ b/types/templates/scripthash/input.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/scripthash/output.d.ts b/types/templates/scripthash/output.d.ts new file mode 100644 index 0000000..091758f --- /dev/null +++ b/types/templates/scripthash/output.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/witnesscommitment/index.d.ts b/types/templates/witnesscommitment/index.d.ts new file mode 100644 index 0000000..a072ea9 --- /dev/null +++ b/types/templates/witnesscommitment/index.d.ts @@ -0,0 +1,2 @@ +import * as output from './output'; +export { output, }; diff --git a/types/templates/witnesscommitment/output.d.ts b/types/templates/witnesscommitment/output.d.ts new file mode 100644 index 0000000..778c9a1 --- /dev/null +++ b/types/templates/witnesscommitment/output.d.ts @@ -0,0 +1,7 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} +export declare function encode(commitment: Buffer): Buffer; +export declare function decode(buffer: Buffer): Buffer; diff --git a/types/templates/witnesspubkeyhash/index.d.ts b/types/templates/witnesspubkeyhash/index.d.ts new file mode 100644 index 0000000..cff90ee --- /dev/null +++ b/types/templates/witnesspubkeyhash/index.d.ts @@ -0,0 +1,3 @@ +import * as input from './input'; +import * as output from './output'; +export { input, output, }; diff --git a/types/templates/witnesspubkeyhash/input.d.ts b/types/templates/witnesspubkeyhash/input.d.ts new file mode 100644 index 0000000..091758f --- /dev/null +++ b/types/templates/witnesspubkeyhash/input.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/witnesspubkeyhash/output.d.ts b/types/templates/witnesspubkeyhash/output.d.ts new file mode 100644 index 0000000..091758f --- /dev/null +++ b/types/templates/witnesspubkeyhash/output.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/witnessscripthash/index.d.ts b/types/templates/witnessscripthash/index.d.ts new file mode 100644 index 0000000..cff90ee --- /dev/null +++ b/types/templates/witnessscripthash/index.d.ts @@ -0,0 +1,3 @@ +import * as input from './input'; +import * as output from './output'; +export { input, output, }; diff --git a/types/templates/witnessscripthash/input.d.ts b/types/templates/witnessscripthash/input.d.ts new file mode 100644 index 0000000..7786731 --- /dev/null +++ b/types/templates/witnessscripthash/input.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(chunks: Array<Buffer>, allowIncomplete?: boolean): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/templates/witnessscripthash/output.d.ts b/types/templates/witnessscripthash/output.d.ts new file mode 100644 index 0000000..091758f --- /dev/null +++ b/types/templates/witnessscripthash/output.d.ts @@ -0,0 +1,5 @@ +/// <reference types="node" /> +export declare function check(script: Buffer | Array<number | Buffer>): boolean; +export declare namespace check { + var toJSON: () => string; +} diff --git a/types/transaction.d.ts b/types/transaction.d.ts new file mode 100644 index 0000000..60bbf45 --- /dev/null +++ b/types/transaction.d.ts @@ -0,0 +1,59 @@ +/// <reference types="node" /> +export declare type BlankOutput = { + script: Buffer; + valueBuffer: Buffer; +}; +export declare type Output = { + script: Buffer; + value: number; +}; +export declare type Input = { + hash: Buffer; + index: number; + script: Buffer; + sequence: number; + witness: Array<Buffer>; +}; +export declare class Transaction { + version: number; + locktime: number; + ins: Array<Input>; + outs: Array<Output | BlankOutput>; + static readonly DEFAULT_SEQUENCE = 4294967295; + static readonly SIGHASH_ALL = 1; + static readonly SIGHASH_NONE = 2; + static readonly SIGHASH_SINGLE = 3; + static readonly SIGHASH_ANYONECANPAY = 128; + static readonly ADVANCED_TRANSACTION_MARKER = 0; + static readonly ADVANCED_TRANSACTION_FLAG = 1; + constructor(); + static fromBuffer(buffer: Buffer, __noStrict?: boolean): Transaction; + static fromHex(hex: string): Transaction; + static isCoinbaseHash(buffer: Buffer): boolean; + isCoinbase(): boolean; + addInput(hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number; + addOutput(scriptPubKey: Buffer, value: number): number; + hasWitnesses(): boolean; + weight(): number; + virtualSize(): number; + byteLength(): number; + private __byteLength; + clone(): Transaction; + /** + * Hash transaction for signing a specific input. + * + * Bitcoin uses a different hash for each signed transaction input. + * This method copies the transaction, makes the necessary changes based on the + * hashType, and then hashes the result. + * This hash can then be used to sign the provided transaction input. + */ + hashForSignature(inIndex: number, prevOutScript: Buffer, hashType: number): Buffer; + hashForWitnessV0(inIndex: number, prevOutScript: Buffer, value: number, hashType: number): Buffer; + getHash(forWitness?: boolean): Buffer; + getId(): string; + toBuffer(buffer?: Buffer, initialOffset?: number): Buffer; + private __toBuffer; + toHex(): string; + setInputScript(index: number, scriptSig: Buffer): void; + setWitness(index: number, witness: Array<Buffer>): void; +} diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts new file mode 100644 index 0000000..4be5968 --- /dev/null +++ b/types/transaction_builder.d.ts @@ -0,0 +1,26 @@ +/// <reference types="node" /> +import { Network } from './networks'; +import { Transaction } from './transaction'; +import { ECPairInterface } from './ecpair'; +export declare class TransactionBuilder { + network: Network; + maximumFeeRate: number; + private __prevTxSet; + private __inputs; + private __tx; + constructor(network?: Network, maximumFeeRate?: number); + static fromTransaction(transaction: Transaction, network?: Network): TransactionBuilder; + setLockTime(locktime: number): void; + setVersion(version: number): void; + addInput(txHash: Buffer | string | Transaction, vout: number, sequence: number, prevOutScript: Buffer): number; + private __addInputUnsafe; + addOutput(scriptPubKey: string | Buffer, value: number): number; + build(): Transaction; + buildIncomplete(): Transaction; + private __build; + sign(vin: number, keyPair: ECPairInterface, redeemScript: Buffer, hashType: number, witnessValue: number, witnessScript: Buffer): void; + private __canModifyInputs; + private __needsOutputs; + private __canModifyOutputs; + private __overMaximumFees; +} diff --git a/types/types.d.ts b/types/types.d.ts new file mode 100644 index 0000000..242bab8 --- /dev/null +++ b/types/types.d.ts @@ -0,0 +1,25 @@ +export declare function UInt31(value: number): boolean; +export declare function BIP32Path(value: string): boolean; +export declare namespace BIP32Path { + var toJSON: () => string; +} +export declare function Satoshi(value: number): boolean; +export declare const ECPoint: any; +export declare const Network: any; +export declare const Buffer256bit: any; +export declare const Hash160bit: any; +export declare const Hash256bit: any; +export declare const Number: any; +export declare const Array: any; +export declare const Boolean: any; +export declare const String: any; +export declare const Buffer: any; +export declare const Hex: any; +export declare const maybe: any; +export declare const tuple: any; +export declare const UInt8: any; +export declare const UInt32: any; +export declare const Function: any; +export declare const BufferN: any; +export declare const Null: any; +export declare const oneOf: any; From 1ed037025c69cf65c50db0b1bef16022cff91cf1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 7 Jan 2019 16:33:57 +0900 Subject: [PATCH 211/568] Fix testnet address test --- test/integration/addresses.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 5afc5e8..7cf2612 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -95,8 +95,8 @@ describe('bitcoinjs-lib (addresses)', function () { const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET }) const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: TESTNET }) - // bitcoin testnet P2PKH addresses start with a 'm' - assert.strictEqual(address.startsWith('m'), true) + // bitcoin testnet P2PKH addresses start with a 'm' or 'n' + assert.strictEqual(address.startsWith('m') || address.startsWith('n'), true) }) it('can generate a Litecoin address', function () { From 1732bafbc14d6926fd6074e02566954eed79e1f1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 15 Jan 2019 17:47:30 +0900 Subject: [PATCH 212/568] Update TypeScript to use ! instead of casting --- src/payments/p2pkh.js | 2 +- src/payments/p2wpkh.js | 2 +- src/transaction.js | 6 +- src/transaction_builder.js | 5 +- ts_src/block.ts | 12 +-- ts_src/payments/embed.ts | 6 +- ts_src/payments/p2ms.ts | 26 +++---- ts_src/payments/p2pk.ts | 8 +- ts_src/payments/p2pkh.ts | 4 +- ts_src/payments/p2sh.ts | 8 +- ts_src/payments/p2wpkh.ts | 2 +- ts_src/payments/p2wsh.ts | 6 +- ts_src/script.ts | 2 +- ts_src/templates/scripthash/input.ts | 6 +- ts_src/templates/witnesscommitment/output.ts | 2 +- ts_src/transaction.ts | 18 ++--- ts_src/transaction_builder.ts | 81 ++++++++++---------- 17 files changed, 99 insertions(+), 97 deletions(-) diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index 443464a..55f4817 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -50,7 +50,7 @@ function p2pkh(a, opts) { if (a.address) return _address().hash; if (a.pubkey || o.pubkey) - return bcrypto.hash160(a.pubkey || o.pubkey); // eslint-disable-line + return bcrypto.hash160(a.pubkey || o.pubkey); }); lazy.prop(o, 'output', function () { if (!o.hash) diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index 98951c6..7b269b7 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -55,7 +55,7 @@ function p2wpkh(a, opts) { if (a.address) return _address().data; if (a.pubkey || o.pubkey) - return bcrypto.hash160(a.pubkey || o.pubkey); // eslint-disable-line + return bcrypto.hash160(a.pubkey || o.pubkey); }); lazy.prop(o, 'output', function () { if (!o.hash) diff --git a/src/transaction.js b/src/transaction.js index 6619b79..34d7b40 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -369,13 +369,13 @@ class Transaction { offset += slice.copy(buffer, offset); } function writeUInt8(i) { - offset = buffer.writeUInt8(i, offset); + offset = (buffer).writeUInt8(i, offset); } function writeUInt32(i) { - offset = buffer.writeUInt32LE(i, offset); + offset = (buffer).writeUInt32LE(i, offset); } function writeInt32(i) { - offset = buffer.writeInt32LE(i, offset); + offset = (buffer).writeInt32LE(i, offset); } function writeUInt64(i) { offset = bufferutils.writeUInt64LE(buffer, i, offset); diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 2121156..1cdc6bf 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -270,7 +270,7 @@ class TransactionBuilder { return this.__inputs.every(input => { if (input.signatures === undefined) return true; - return input.signatures.every((signature => { + return input.signatures.every(signature => { if (!signature) return true; const hashType = signatureHashType(signature); @@ -283,7 +283,8 @@ class TransactionBuilder { // of more outputs return nInputs <= nOutputs; } - })); + return false; + }); }); } __overMaximumFees(bytes) { diff --git a/ts_src/block.ts b/ts_src/block.ts index 82700aa..cb50559 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -129,7 +129,7 @@ export class Block { if (transactions.length === 0) throw errorMerkleNoTxes if (forWitness && !txesHaveWitness(transactions)) throw errorWitnessNotSegwit - const hashes = transactions.map(transaction => transaction.getHash((<boolean>forWitness))) + const hashes = transactions.map(transaction => transaction.getHash(forWitness!)) const rootHash = fastMerkleRoot(hashes, bcrypto.hash256) @@ -139,7 +139,7 @@ export class Block { } hasWitnessCommit (): boolean { - return txesHaveWitness(<Array<Transaction>>this.transactions) + return txesHaveWitness(this.transactions!) } byteLength (headersOnly: boolean): number { @@ -184,8 +184,8 @@ export class Block { } writeInt32(this.version) - writeSlice(<Buffer>this.prevHash) - writeSlice(<Buffer>this.merkleRoot) + writeSlice(this.prevHash!) + writeSlice(this.merkleRoot!) writeUInt32(this.timestamp) writeUInt32(this.bits) writeUInt32(this.nonce) @@ -212,7 +212,7 @@ export class Block { if (!this.transactions) throw errorMerkleNoTxes const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions) - return (<Buffer>this.merkleRoot).compare(actualMerkleRoot) === 0 + return this.merkleRoot!.compare(actualMerkleRoot) === 0 } checkWitnessCommit (): boolean { @@ -220,7 +220,7 @@ export class Block { if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true) - return (<Buffer>this.witnessCommit).compare(actualWitnessCommit) === 0 + return this.witnessCommit!.compare(actualWitnessCommit) === 0 } checkProofOfWork (): boolean { diff --git a/ts_src/payments/embed.ts b/ts_src/payments/embed.ts index 8cc8899..bfaeb8b 100644 --- a/ts_src/payments/embed.ts +++ b/ts_src/payments/embed.ts @@ -36,15 +36,15 @@ export function p2data (a: Payment, opts?: PaymentOpts): Payment { }) lazy.prop(o, 'data', function () { if (!a.output) return - return (<Array<Buffer | number>>bscript.decompile(a.output)).slice(1) + return bscript.decompile(a.output)!.slice(1) }) // extended validation if (opts.validate) { if (a.output) { const chunks = bscript.decompile(a.output) - if ((<Array<Buffer | number>>chunks)[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') - if (!(<Array<Buffer | number>>chunks).slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') + if (chunks![0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') + if (!chunks!.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') if (a.data && !stacksEqual(a.data, <Array<Buffer>>o.data)) throw new TypeError('Data mismatch') } diff --git a/ts_src/payments/p2ms.ts b/ts_src/payments/p2ms.ts index 67b9692..34e2d31 100644 --- a/ts_src/payments/p2ms.ts +++ b/ts_src/payments/p2ms.ts @@ -29,7 +29,7 @@ export function p2ms (a: Payment, opts?: PaymentOpts): Payment { function isAcceptableSignature (x: Buffer | number) { return bscript.isCanonicalScriptSignature(<Buffer>x) || - ((<PaymentOpts>opts).allowIncomplete && + (opts!.allowIncomplete && (<number> x === OPS.OP_0)) !== undefined // eslint-disable-line } @@ -85,7 +85,7 @@ export function p2ms (a: Payment, opts?: PaymentOpts): Payment { }) lazy.prop(o, 'signatures', function () { if (!a.input) return - return (<Array<Buffer | number>>bscript.decompile(a.input)).slice(1) + return bscript.decompile(a.input)!.slice(1) }) lazy.prop(o, 'input', function () { if (!a.signatures) return @@ -105,35 +105,35 @@ export function p2ms (a: Payment, opts?: PaymentOpts): Payment { if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid') if ( - <number>(<Payment>o).m <= 0 || // eslint-disable-line - <number>(<Payment>o).n > 16 || // eslint-disable-line - <number>(<Payment>o).m > <number>(<Payment>o).n || // eslint-disable-line + o.m! <= 0 || // eslint-disable-line + o.n! > 16 || // eslint-disable-line + o.m! > o.n! || // eslint-disable-line o.n !== chunks.length - 3) throw new TypeError('Output is invalid') - if (!(<Array<Buffer>>o.pubkeys).every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid') + if (!o.pubkeys!.every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid') if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch') if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch') - if (a.pubkeys && !stacksEqual(a.pubkeys, (<Array<Buffer>>o.pubkeys))) throw new TypeError('Pubkeys mismatch') + if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys!)) throw new TypeError('Pubkeys mismatch') } if (a.pubkeys) { if (a.n !== undefined && a.n !== a.pubkeys.length) throw new TypeError('Pubkey count mismatch') o.n = a.pubkeys.length - if (o.n < <number>(<Payment>o).m) throw new TypeError('Pubkey count cannot be less than m') + if (o.n < o.m!) throw new TypeError('Pubkey count cannot be less than m') } if (a.signatures) { - if (a.signatures.length < <number>(<Payment>o).m) throw new TypeError('Not enough signatures provided') - if (a.signatures.length > <number>(<Payment>o).m) throw new TypeError('Too many signatures provided') + if (a.signatures.length < o.m!) throw new TypeError('Not enough signatures provided') + if (a.signatures.length > o.m!) throw new TypeError('Too many signatures provided') } if (a.input) { if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') - if ((<Array<Buffer>>o.signatures).length === 0 || !(<Array<Buffer>>o.signatures).every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') + if (o.signatures!.length === 0 || !o.signatures!.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') - if (a.signatures && !stacksEqual(a.signatures, (<Array<Buffer>>o.signatures))) throw new TypeError('Signature mismatch') - if (a.m !== undefined && a.m !== (<Array<Buffer>>a.signatures).length) throw new TypeError('Signature count mismatch') + if (a.signatures && !stacksEqual(a.signatures, o.signatures!)) throw new TypeError('Signature mismatch') + if (a.m !== undefined && a.m !== a.signatures!.length) throw new TypeError('Signature count mismatch') } } diff --git a/ts_src/payments/p2pk.ts b/ts_src/payments/p2pk.ts index 0825955..3385c56 100644 --- a/ts_src/payments/p2pk.ts +++ b/ts_src/payments/p2pk.ts @@ -27,7 +27,7 @@ export function p2pk (a: Payment, opts?: PaymentOpts): Payment { input: typef.maybe(typef.Buffer) }, a) - const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(<Buffer>a.input) }) + const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.input!) }) const network = a.network || BITCOIN_NETWORK const o: Payment = { network } @@ -61,16 +61,16 @@ export function p2pk (a: Payment, opts?: PaymentOpts): Payment { if (a.output) { if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') if (!ecc.isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid') - if (a.pubkey && !a.pubkey.equals(<Buffer>o.pubkey)) throw new TypeError('Pubkey mismatch') + if (a.pubkey && !a.pubkey.equals(o.pubkey!)) throw new TypeError('Pubkey mismatch') } if (a.signature) { - if (a.input && !a.input.equals(<Buffer>o.input)) throw new TypeError('Signature mismatch') + if (a.input && !a.input.equals(o.input!)) throw new TypeError('Signature mismatch') } if (a.input) { if (_chunks().length !== 1) throw new TypeError('Input is invalid') - if (!bscript.isCanonicalScriptSignature(<Buffer>o.signature)) throw new TypeError('Input has invalid signature') + if (!bscript.isCanonicalScriptSignature(o.signature!)) throw new TypeError('Input has invalid signature') } } diff --git a/ts_src/payments/p2pkh.ts b/ts_src/payments/p2pkh.ts index f12faf8..ddb9cc3 100644 --- a/ts_src/payments/p2pkh.ts +++ b/ts_src/payments/p2pkh.ts @@ -38,7 +38,7 @@ export function p2pkh (a: Payment, opts?: PaymentOpts): Payment { const hash = payload.slice(1) return { version, hash } }) - const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(<Buffer>a.input) }) + const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.input!) }) const network = a.network || BITCOIN_NETWORK const o: Payment = { network } @@ -54,7 +54,7 @@ export function p2pkh (a: Payment, opts?: PaymentOpts): Payment { lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(3, 23) if (a.address) return _address().hash - if (a.pubkey || o.pubkey) return bcrypto.hash160(<Buffer> a.pubkey || <Buffer>o.pubkey) // eslint-disable-line + if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!) }) lazy.prop(o, 'output', function () { if (!o.hash) return diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index e0292f3..1aa1a94 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -59,7 +59,7 @@ export function p2sh (a: Payment, opts?: PaymentOpts): Payment { const hash = payload.slice(1) return { version, hash } }) - const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(<Buffer>a.input) }) + const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.input!) }) const _redeem = lazy.value(function (): Payment { const chunks = _chunks() return { @@ -75,7 +75,7 @@ export function p2sh (a: Payment, opts?: PaymentOpts): Payment { if (!o.hash) return const payload = Buffer.allocUnsafe(21) - payload.writeUInt8((<Network>o.network).scriptHash, 0) + payload.writeUInt8(o.network!.scriptHash, 0) o.hash.copy(payload, 1) return bs58check.encode(payload) }) @@ -173,8 +173,8 @@ export function p2sh (a: Payment, opts?: PaymentOpts): Payment { if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') if (a.input) { const redeem = _redeem() - if (a.redeem.output && !a.redeem.output.equals(<Buffer>redeem.output)) throw new TypeError('Redeem.output mismatch') - if (a.redeem.input && !a.redeem.input.equals(<Buffer>redeem.input)) throw new TypeError('Redeem.input mismatch') + if (a.redeem.output && !a.redeem.output.equals(redeem.output!)) throw new TypeError('Redeem.output mismatch') + if (a.redeem.input && !a.redeem.input.equals(redeem.input!)) throw new TypeError('Redeem.input mismatch') } checkRedeem(a.redeem) diff --git a/ts_src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts index b40085b..6cfecc4 100644 --- a/ts_src/payments/p2wpkh.ts +++ b/ts_src/payments/p2wpkh.ts @@ -59,7 +59,7 @@ export function p2wpkh (a: Payment, opts?: PaymentOpts): Payment { lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(2, 22) if (a.address) return _address().data - if (a.pubkey || o.pubkey) return bcrypto.hash160(<Buffer> a.pubkey || <Buffer>o.pubkey) // eslint-disable-line + if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!) }) lazy.prop(o, 'output', function () { if (!o.hash) return diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 7e2f237..452dcc3 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -58,7 +58,7 @@ export function p2wsh (a: Payment, opts?: PaymentOpts): Payment { data: Buffer.from(data) } }) - const _rchunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(<Buffer>(<Payment>a.redeem).input) }) + const _rchunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.redeem!.input!) }) let network = a.network if (!network) { @@ -71,7 +71,7 @@ export function p2wsh (a: Payment, opts?: PaymentOpts): Payment { if (!o.hash) return const words = bech32.toWords(o.hash) words.unshift(0x00) - return bech32.encode((<Network>network).bech32, words) + return bech32.encode(network!.bech32, words) }) lazy.prop(o, 'hash', function () { if (a.output) return a.output.slice(2) @@ -158,7 +158,7 @@ export function p2wsh (a: Payment, opts?: PaymentOpts): Payment { // is the redeem output non-empty? if (a.redeem.output) { - if ((<Array<Buffer | number>>bscript.decompile(a.redeem.output)).length === 0) throw new TypeError('Redeem.output is invalid') + if (bscript.decompile(a.redeem.output)!.length === 0) throw new TypeError('Redeem.output is invalid') // match hash against other sources const hash2 = bcrypto.sha256(a.redeem.output) diff --git a/ts_src/script.ts b/ts_src/script.ts index 79e3342..64b74ad 100644 --- a/ts_src/script.ts +++ b/ts_src/script.ts @@ -125,7 +125,7 @@ export function decompile (buffer: Buffer | Array<number | Buffer>): Array<numbe // decompile minimally const op = asMinimalOP(data) if (op !== undefined) { - chunks.push(<number>op) + chunks.push(op) } else { chunks.push(data) } diff --git a/ts_src/templates/scripthash/input.ts b/ts_src/templates/scripthash/input.ts index 90af0fe..7b3107d 100644 --- a/ts_src/templates/scripthash/input.ts +++ b/ts_src/templates/scripthash/input.ts @@ -10,14 +10,14 @@ import * as p2wsho from '../witnessscripthash/output' export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script) + const chunks = bscript.decompile(script)! if (chunks.length < 1) return false const lastChunk = chunks[chunks.length - 1] if (!Buffer.isBuffer(lastChunk)) return false - const scriptSigChunks = <Array<number | Buffer>>bscript.decompile(bscript.compile(chunks.slice(0, -1))) - const redeemScriptChunks = bscript.decompile(<Buffer>lastChunk) + const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1)))! + const redeemScriptChunks = bscript.decompile(lastChunk) // is redeemScript a valid script? if (!redeemScriptChunks) return false diff --git a/ts_src/templates/witnesscommitment/output.ts b/ts_src/templates/witnesscommitment/output.ts index 71e8df3..0bdf11c 100644 --- a/ts_src/templates/witnesscommitment/output.ts +++ b/ts_src/templates/witnesscommitment/output.ts @@ -32,5 +32,5 @@ export function encode (commitment: Buffer): Buffer { export function decode (buffer: Buffer): Buffer { typeforce(check, buffer) - return (<Buffer>(<Array<number | Buffer>>bscript.decompile(buffer))[1]).slice(4, 36) + return (<Buffer>bscript.decompile(buffer)![1]).slice(4, 36) } diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index 29b49c8..e6e1d24 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -294,8 +294,8 @@ export class Transaction { if (inIndex >= this.ins.length) return ONE // ignore OP_CODESEPARATOR - const ourScript = bscript.compile((<Array<Buffer | number>>bscript.decompile(prevOutScript)).filter((x) => { - return x !== <number>opcodes.OP_CODESEPARATOR + const ourScript = bscript.compile(bscript.decompile(prevOutScript)!.filter((x) => { + return x !== opcodes.OP_CODESEPARATOR })) const txTmp = this.clone() @@ -471,28 +471,28 @@ export class Transaction { } private __toBuffer (buffer?: Buffer, initialOffset?: number, __allowWitness?: boolean): Buffer { - if (!buffer) buffer = <Buffer> Buffer.allocUnsafe(this.__byteLength((<boolean>__allowWitness))) + if (!buffer) buffer = <Buffer> Buffer.allocUnsafe(this.__byteLength(__allowWitness!)) let offset = initialOffset || 0 function writeSlice (slice: Buffer): void { - offset += slice.copy(<Buffer>buffer, offset) + offset += slice.copy(buffer!, offset) } function writeUInt8 (i: number) { - offset = (<Buffer>buffer).writeUInt8(i, offset) + offset = (buffer!).writeUInt8(i, offset) } function writeUInt32 (i: number) { - offset = (<Buffer>buffer).writeUInt32LE(i, offset) + offset = (buffer!).writeUInt32LE(i, offset) } function writeInt32 (i: number) { - offset = (<Buffer>buffer).writeInt32LE(i, offset) + offset = (buffer!).writeInt32LE(i, offset) } function writeUInt64 (i: number) { - offset = bufferutils.writeUInt64LE(<Buffer>buffer, i, offset) + offset = bufferutils.writeUInt64LE(buffer!, i, offset) } function writeVarInt (i: number) { @@ -548,7 +548,7 @@ export class Transaction { writeUInt32(this.locktime) // avoid slicing unless necessary - if (initialOffset !== undefined) return buffer.slice((<number>initialOffset), offset) + if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) return buffer } diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 59fc102..969462c 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -50,11 +50,11 @@ interface TxbOutput { } function txIsString(tx: Buffer | string | Transaction): tx is string { - return typeof (<string>tx) === 'string' || tx instanceof String + return typeof tx === 'string' || tx instanceof String } function txIsTransaction(tx: Buffer | string | Transaction): tx is Transaction { - return (<Transaction>tx) instanceof Transaction + return tx instanceof Transaction } export class TransactionBuilder { @@ -209,7 +209,7 @@ export class TransactionBuilder { scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network) } - return this.__tx.addOutput(<Buffer>scriptPubKey, value) + return this.__tx.addOutput(scriptPubKey, value) } build (): Transaction { @@ -232,15 +232,15 @@ export class TransactionBuilder { this.__inputs.forEach((input, i) => { if (!input.prevOutType && !allowIncomplete) throw new Error('Transaction is not complete') - const result = build(<string>input.prevOutType, input, allowIncomplete) + const result = build(input.prevOutType!, input, allowIncomplete) if (!result) { if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) throw new Error('Unknown input type') if (!allowIncomplete) throw new Error('Not enough information') return } - tx.setInputScript(i, <Buffer>result.input) - tx.setWitness(i, <Array<Buffer>>result.witness) + tx.setInputScript(i, result.input!) + tx.setWitness(i, result.witness!) }) if (!allowIncomplete) { @@ -270,7 +270,7 @@ export class TransactionBuilder { throw new Error('Inconsistent redeemScript') } - const ourPubKey = keyPair.publicKey || (<()=>Buffer>keyPair.getPublicKey)() + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!() if (!canSign(input)) { if (witnessValue !== undefined) { if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue') @@ -297,9 +297,9 @@ export class TransactionBuilder { } // enforce in order signing of public keys - const signed = (<Array<Buffer>>input.pubkeys).some((pubKey, i) => { - if (!ourPubKey.equals(pubKey)) return false - if ((<Array<Buffer>>input.signatures)[i]) throw new Error('Signature already exists') + const signed = input.pubkeys!.some((pubKey, i) => { + if (!ourPubKey.equals(pubKey!)) return false + if (input.signatures![i]) throw new Error('Signature already exists') // TODO: add tests if (ourPubKey.length !== 33 && input.hasWitness) { @@ -307,7 +307,7 @@ export class TransactionBuilder { } const signature = keyPair.sign(signatureHash) - ;(<Array<Buffer>>input.signatures)[i] = bscript.signature.encode(signature, hashType) + input.signatures![i] = bscript.signature.encode(signature, hashType) return true }) @@ -355,7 +355,7 @@ export class TransactionBuilder { return this.__inputs.every(input => { if (input.signatures === undefined) return true - return input.signatures.every(<(signature: Buffer | undefined)=>boolean>(signature => { + return input.signatures.every(signature => { if (!signature) return true const hashType = signatureHashType(signature) @@ -367,13 +367,14 @@ export class TransactionBuilder { // of more outputs return nInputs <= nOutputs } - })) + return false + }) }) } private __overMaximumFees (bytes: number): boolean { // not all inputs will have .value defined - const incoming = this.__inputs.reduce((a, x) => a + (<number>x.value >>> 0), 0) + const incoming = this.__inputs.reduce((a, x) => a + (x.value! >>> 0), 0) // but all outputs do, and if we have any input value // we can immediately determine if the outputs are too small @@ -449,14 +450,14 @@ function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: str witness: witnessStack }) - const outputType = classify.output(<Buffer>(<Payment>redeem).output) - const expanded = expandInput(<Buffer>(<Payment>redeem).input, <Array<Buffer>>(<Payment>redeem).witness, outputType, (<Payment>redeem).output) + const outputType = classify.output(redeem!.output!) + const expanded = expandInput(redeem!.input!, redeem!.witness!, outputType, redeem!.output) if (!expanded.prevOutType) return {} return { prevOutScript: output, prevOutType: SCRIPT_TYPES.P2SH, - redeemScript: (<Payment>redeem).output, + redeemScript: redeem!.output, redeemScriptType: expanded.prevOutType, witnessScript: expanded.witnessScript, witnessScriptType: expanded.witnessScriptType, @@ -471,19 +472,19 @@ function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: str input: scriptSig, witness: witnessStack }) - const outputType = classify.output(<Buffer>(<Payment>redeem).output) + const outputType = classify.output(redeem!.output!) let expanded if (outputType === SCRIPT_TYPES.P2WPKH) { - expanded = expandInput(<Buffer>(<Payment>redeem).input, <Array<Buffer>>(<Payment>redeem).witness, outputType) + expanded = expandInput(redeem!.input!, redeem!.witness!, outputType) } else { - expanded = expandInput(bscript.compile(<Array<Buffer>>(<Payment>redeem).witness), [], outputType, (<Payment>redeem).output) + expanded = expandInput(bscript.compile(redeem!.witness!), [], outputType, redeem!.output) } if (!expanded.prevOutType) return {} return { prevOutScript: output, prevOutType: SCRIPT_TYPES.P2WSH, - witnessScript: (<Payment>redeem).output, + witnessScript: redeem!.output, witnessScriptType: expanded.prevOutType, pubkeys: expanded.pubkeys, @@ -500,12 +501,12 @@ function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: str // could be done in expandInput, but requires the original Transaction for hashForSignature function fixMultisigOrder (input: TxbInput, transaction: Transaction, vin: number): void { if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) return - if ((<Array<Buffer>>input.pubkeys).length === (<Array<Buffer>>input.signatures).length) return + if (input.pubkeys!.length === input.signatures!.length) return - const unmatched = (<Array<Buffer | undefined>>input.signatures).concat() + const unmatched = input.signatures!.concat() - input.signatures = <Array<Buffer | undefined>>(<Array<Buffer>>input.pubkeys).map(pubKey => { - const keyPair = ECPair.fromPublicKey(pubKey) + input.signatures = input.pubkeys!.map(pubKey => { + const keyPair = ECPair.fromPublicKey(pubKey!) let match: Buffer | undefined // check for a signature @@ -515,7 +516,7 @@ function fixMultisigOrder (input: TxbInput, transaction: Transaction, vin: numbe // TODO: avoid O(n) hashForSignature const parsed = bscript.signature.decode(signature) - const hash = transaction.hashForSignature(vin, (<Buffer>input.redeemScript), parsed.hashType) + const hash = transaction.hashForSignature(vin, input.redeemScript!, parsed.hashType) // skip if signature does not match pubKey if (!keyPair.verify(hash, parsed.signature)) return false @@ -542,7 +543,7 @@ function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { // does our hash160(pubKey) match the output scripts? const pkh1 = payments.p2pkh({ output: script }).hash const pkh2 = bcrypto.hash160(ourPubKey) - if (!(<Buffer>pkh1).equals(pkh2)) return { type } + if (!pkh1!.equals(pkh2)) return { type } return { type, @@ -557,7 +558,7 @@ function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { // does our hash160(pubKey) match the output scripts? const wpkh1 = payments.p2wpkh({ output: script }).hash const wpkh2 = bcrypto.hash160(ourPubKey) - if (!(<Buffer>wpkh1).equals(wpkh2)) return { type } + if (!wpkh1!.equals(wpkh2)) return { type } return { type, @@ -580,7 +581,7 @@ function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { return { type, pubkeys: p2ms.pubkeys, - signatures: (<Array<Buffer>>p2ms.pubkeys).map((): undefined => undefined), + signatures: p2ms.pubkeys!.map((): undefined => undefined), maxSignatures: p2ms.m } } @@ -597,10 +598,10 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, const p2shAlt = <Payment> payments.p2sh({ redeem: p2wsh }) // enforces P2SH(P2WSH(...)) - if (!(<Buffer>p2wsh.hash).equals(<Buffer>p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript') - if (!(<Buffer>p2sh.hash).equals(<Buffer>p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript') + if (!p2wsh.hash!.equals(p2wshAlt.hash!)) throw new Error('Witness script inconsistent with prevOutScript') + if (!p2sh.hash!.equals(p2shAlt.hash!)) throw new Error('Redeem script inconsistent with prevOutScript') - const expanded = expandOutput(<Buffer>(<Payment>p2wsh.redeem).output, ourPubKey) + const expanded = expandOutput(p2wsh.redeem!.output!, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures @@ -637,10 +638,10 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, try { p2shAlt = <Payment> payments.p2sh({ output: input.prevOutScript }) } catch (e) { throw new Error('PrevOutScript must be P2SH') } - if (!(<Buffer>p2sh.hash).equals(<Buffer>p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript') + if (!p2sh.hash!.equals(p2shAlt.hash!)) throw new Error('Redeem script inconsistent with prevOutScript') } - const expanded = expandOutput(<Buffer>(<Payment>p2sh.redeem).output, ourPubKey) + const expanded = expandOutput(p2sh.redeem!.output!, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')') if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures @@ -648,7 +649,7 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, let signScript = redeemScript if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = <Buffer> payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output! } return { @@ -669,14 +670,14 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, } if (witnessScript) { - const p2wsh = <Payment> payments.p2wsh({ redeem: { output: witnessScript } }) + const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }) if (input.prevOutScript) { const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }) - if (!(<Buffer>p2wsh.hash).equals(<Buffer>p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript') + if (!p2wsh.hash!.equals(p2wshAlt.hash!)) throw new Error('Witness script inconsistent with prevOutScript') } - const expanded = expandOutput(<Buffer>(<Payment>p2wsh.redeem).output, ourPubKey) + const expanded = expandOutput(p2wsh.redeem!.output!, ourPubKey) if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures @@ -784,7 +785,7 @@ function build (type: string, input: TxbInput, allowIncomplete?: boolean): Payme return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate }) } case SCRIPT_TYPES.P2SH: { - const redeem = build(<string>input.redeemScriptType, input, allowIncomplete) + const redeem = build(input.redeemScriptType!, input, allowIncomplete) if (!redeem) return return payments.p2sh({ @@ -796,7 +797,7 @@ function build (type: string, input: TxbInput, allowIncomplete?: boolean): Payme }) } case SCRIPT_TYPES.P2WSH: { - const redeem = build(<string>input.witnessScriptType, input, allowIncomplete) + const redeem = build(input.witnessScriptType!, input, allowIncomplete) if (!redeem) return return payments.p2wsh({ From c143e9c855912d6d60e5bd5fc9aea188a868cdf3 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 15 Jan 2019 17:48:10 +0900 Subject: [PATCH 213/568] Specify version to help prevent large diffs --- package.json | 6 +++--- tsconfig.json | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c00414a..a77761b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "bitcoinjs" ], "scripts": { - "build": "tsc -d -p ./tsconfig.json", + "build": "tsc -p ./tsconfig.json", "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", @@ -39,7 +39,7 @@ "src" ], "dependencies": { - "@types/node": "^10.12.18", + "@types/node": "10.12.18", "bech32": "^1.1.2", "bip32": "git+https://github.com/junderw/bip32.git#typeScript", "bip66": "^1.1.0", @@ -52,7 +52,7 @@ "randombytes": "^2.0.1", "tiny-secp256k1": "^1.0.0", "typeforce": "^1.11.3", - "typescript": "^3.2.2", + "typescript": "3.2.2", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" }, diff --git a/tsconfig.json b/tsconfig.json index 787769d..0ab4612 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "target": "ES2015", "module": "commonjs", "outDir": "./src", + "declaration": true, "declarationDir": "./types", "rootDir": "./ts_src", "types": [ From 581ab5f13622fde017ff8a07f1af7703dd89545e Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 17 Jan 2019 16:52:58 +0900 Subject: [PATCH 214/568] Add files to package.json --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a77761b..2a38c47 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "url": "https://github.com/bitcoinjs/bitcoinjs-lib.git" }, "files": [ - "src" + "src", + "types" ], "dependencies": { "@types/node": "10.12.18", From e52abecee2425917e703984903bf5d3d13f99bbf Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 17 Jan 2019 17:01:15 +0900 Subject: [PATCH 215/568] Move to checkTxRoots and warn checkMerkleRoot deprecation --- src/block.js | 11 ++++++++++- test/block.js | 10 ++-------- ts_src/block.ts | 13 ++++++++++++- types/block.d.ts | 4 +++- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/block.js b/src/block.js index 5b4776f..6f4721a 100644 --- a/src/block.js +++ b/src/block.js @@ -167,13 +167,22 @@ class Block { toHex(headersOnly) { return this.toBuffer(headersOnly).toString('hex'); } + checkTxRoots() { + return this.__checkMerkleRoot() && + (this.hasWitnessCommit() ? this.__checkWitnessCommit() : true); + } checkMerkleRoot() { + console.warn('Deprecation Warning: Block method checkMerkleRoot will be ' + + 'deprecated in v5. Please use checkTxRoots instead.'); + return this.checkTxRoots(); + } + __checkMerkleRoot() { if (!this.transactions) throw errorMerkleNoTxes; const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions); return this.merkleRoot.compare(actualMerkleRoot) === 0; } - checkWitnessCommit() { + __checkWitnessCommit() { if (!this.transactions) throw errorMerkleNoTxes; if (!this.hasWitnessCommit()) diff --git a/test/block.js b/test/block.js index 76b043d..e646737 100644 --- a/test/block.js +++ b/test/block.js @@ -125,7 +125,7 @@ describe('Block', function () { }) }) - describe('checkMerkleRoot', function () { + describe('checkTxRoots', function () { fixtures.valid.forEach(function (f) { if (f.hex.length === 160) return @@ -136,14 +136,8 @@ describe('Block', function () { }) it('returns ' + f.valid + ' for ' + f.id, function () { - assert.strictEqual(block.checkMerkleRoot(), true) + assert.strictEqual(block.checkTxRoots(), true) }) - - if (f.witnessCommit) { - it('validates witness commit for ' + f.id, function () { - assert.strictEqual(block.checkWitnessCommit(), true) - }) - } }) }) diff --git a/ts_src/block.ts b/ts_src/block.ts index cb50559..e5ea071 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -208,14 +208,25 @@ export class Block { return this.toBuffer(headersOnly).toString('hex') } + checkTxRoots (): boolean { + return this.__checkMerkleRoot() && + (this.hasWitnessCommit() ? this.__checkWitnessCommit() : true) + } + checkMerkleRoot (): boolean { + console.warn('Deprecation Warning: Block method checkMerkleRoot will be ' + + 'deprecated in v5. Please use checkTxRoots instead.') + return this.checkTxRoots() + } + + __checkMerkleRoot (): boolean { if (!this.transactions) throw errorMerkleNoTxes const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions) return this.merkleRoot!.compare(actualMerkleRoot) === 0 } - checkWitnessCommit (): boolean { + __checkWitnessCommit (): boolean { if (!this.transactions) throw errorMerkleNoTxes if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit diff --git a/types/block.d.ts b/types/block.d.ts index ff98de2..3ff2926 100644 --- a/types/block.d.ts +++ b/types/block.d.ts @@ -21,7 +21,9 @@ export declare class Block { getUTCDate(): Date; toBuffer(headersOnly: boolean): Buffer; toHex(headersOnly: boolean): string; + checkTxRoots(): boolean; checkMerkleRoot(): boolean; - checkWitnessCommit(): boolean; + __checkMerkleRoot(): boolean; + __checkWitnessCommit(): boolean; checkProofOfWork(): boolean; } From 4c6ea8045937b87bb11db1da255bd0bea1a3a403 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 17 Jan 2019 17:10:52 +0900 Subject: [PATCH 216/568] Check for segwit block with no witness commit --- src/block.js | 67 ++++++++++++++++++++++++++++++++------------- ts_src/block.ts | 70 ++++++++++++++++++++++++++++++++++-------------- types/block.d.ts | 2 ++ 3 files changed, 100 insertions(+), 39 deletions(-) diff --git a/src/block.js b/src/block.js index 6f4721a..cb593ac 100644 --- a/src/block.js +++ b/src/block.js @@ -9,10 +9,8 @@ const typeforce = require('typeforce'); const varuint = require('varuint-bitcoin'); const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions'); const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block'); -const errorBufferTooSmall = new Error('Buffer too small (< 80 bytes)'); -function txesHaveWitness(transactions) { - return transactions !== undefined && - transactions instanceof Array && +function txesHaveWitnessCommit(transactions) { + return transactions instanceof Array && transactions[0] && transactions[0].ins && transactions[0].ins instanceof Array && @@ -21,6 +19,14 @@ function txesHaveWitness(transactions) { transactions[0].ins[0].witness instanceof Array && transactions[0].ins[0].witness.length > 0; } +function anyTxHasWitness(transactions) { + return transactions instanceof Array && + transactions.some(tx => typeof tx === 'object' && + tx.ins instanceof Array && + tx.ins.some(input => typeof input === 'object' && + input.witness instanceof Array && + input.witness.length > 0)); +} class Block { constructor() { this.version = 1; @@ -34,7 +40,7 @@ class Block { } static fromBuffer(buffer) { if (buffer.length < 80) - throw errorBufferTooSmall; + throw new Error('Buffer too small (< 80 bytes)'); let offset = 0; const readSlice = (n) => { offset += n; @@ -75,18 +81,10 @@ class Block { const tx = readTransaction(); block.transactions.push(tx); } + let witnessCommit = block.getWitnessCommit(); // This Block contains a witness commit - if (block.hasWitnessCommit()) { - // The merkle root for the witness data is in an OP_RETURN output. - // There is no rule for the index of the output, so use filter to find it. - // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed - // If multiple commits are found, the output with highest index is assumed. - let witnessCommits = block.transactions[0].outs - .filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))) - .map(out => out.script.slice(6, 38)); - // Use the commit with the highest output (should only be one though) - block.witnessCommit = witnessCommits[witnessCommits.length - 1]; - } + if (witnessCommit) + block.witnessCommit = witnessCommit; return block; } static fromHex(hex) { @@ -103,7 +101,7 @@ class Block { typeforce([{ getHash: types.Function }], transactions); if (transactions.length === 0) throw errorMerkleNoTxes; - if (forWitness && !txesHaveWitness(transactions)) + if (forWitness && !txesHaveWitnessCommit(transactions)) throw errorWitnessNotSegwit; const hashes = transactions.map(transaction => transaction.getHash(forWitness)); const rootHash = fastMerkleRoot(hashes, bcrypto.hash256); @@ -111,8 +109,34 @@ class Block { ? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) : rootHash; } + getWitnessCommit() { + if (!txesHaveWitnessCommit(this.transactions)) + return null; + // The merkle root for the witness data is in an OP_RETURN output. + // There is no rule for the index of the output, so use filter to find it. + // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed + // If multiple commits are found, the output with highest index is assumed. + let witnessCommits = this.transactions[0].outs + .filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))) + .map(out => out.script.slice(6, 38)); + if (witnessCommits.length === 0) + return null; + // Use the commit with the highest output (should only be one though) + let result = witnessCommits[witnessCommits.length - 1]; + if (!(result instanceof Buffer && result.length === 32)) + return null; + return result; + } hasWitnessCommit() { - return txesHaveWitness(this.transactions); + if (this.witnessCommit instanceof Buffer && + this.witnessCommit.length === 32) + return true; + if (this.getWitnessCommit() !== null) + return true; + return false; + } + hasWitness() { + return anyTxHasWitness(this.transactions); } byteLength(headersOnly) { if (headersOnly || !this.transactions) @@ -168,8 +192,13 @@ class Block { return this.toBuffer(headersOnly).toString('hex'); } checkTxRoots() { + // If the Block has segwit transactions but no witness commit, + // there's no way it can be valid, so fail the check. + let hasWitnessCommit = this.hasWitnessCommit(); + if (!hasWitnessCommit && this.hasWitness()) + return false; return this.__checkMerkleRoot() && - (this.hasWitnessCommit() ? this.__checkWitnessCommit() : true); + (hasWitnessCommit ? this.__checkWitnessCommit() : true); } checkMerkleRoot() { console.warn('Deprecation Warning: Block method checkMerkleRoot will be ' + diff --git a/ts_src/block.ts b/ts_src/block.ts index e5ea071..e3d1fc5 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -9,11 +9,9 @@ const varuint = require('varuint-bitcoin') const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions') const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block') -const errorBufferTooSmall = new Error('Buffer too small (< 80 bytes)') -function txesHaveWitness (transactions: Array<Transaction>): boolean { - return transactions !== undefined && - transactions instanceof Array && +function txesHaveWitnessCommit (transactions: Array<Transaction>): boolean { + return transactions instanceof Array && transactions[0] && transactions[0].ins && transactions[0].ins instanceof Array && @@ -23,6 +21,19 @@ function txesHaveWitness (transactions: Array<Transaction>): boolean { transactions[0].ins[0].witness.length > 0 } +function anyTxHasWitness (transactions: Array<Transaction>): boolean { + return transactions instanceof Array && + transactions.some(tx => + typeof tx === 'object' && + tx.ins instanceof Array && + tx.ins.some(input => + typeof input === 'object' && + input.witness instanceof Array && + input.witness.length > 0 + ) + ) +} + export class Block { version: number prevHash?: Buffer @@ -45,7 +56,7 @@ export class Block { } static fromBuffer (buffer: Buffer): Block { - if (buffer.length < 80) throw errorBufferTooSmall + if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)') let offset: number = 0 const readSlice = (n: number): Buffer => { @@ -95,19 +106,9 @@ export class Block { block.transactions.push(tx) } + let witnessCommit = block.getWitnessCommit() // This Block contains a witness commit - if (block.hasWitnessCommit()) { - // The merkle root for the witness data is in an OP_RETURN output. - // There is no rule for the index of the output, so use filter to find it. - // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed - // If multiple commits are found, the output with highest index is assumed. - let witnessCommits = block.transactions[0].outs - .filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))) - .map(out => out.script.slice(6, 38)) - - // Use the commit with the highest output (should only be one though) - block.witnessCommit = witnessCommits[witnessCommits.length - 1] - } + if (witnessCommit) block.witnessCommit = witnessCommit return block } @@ -127,7 +128,7 @@ export class Block { static calculateMerkleRoot (transactions: Array<Transaction>, forWitness?: boolean): Buffer { typeforce([{ getHash: types.Function }], transactions) if (transactions.length === 0) throw errorMerkleNoTxes - if (forWitness && !txesHaveWitness(transactions)) throw errorWitnessNotSegwit + if (forWitness && !txesHaveWitnessCommit(transactions)) throw errorWitnessNotSegwit const hashes = transactions.map(transaction => transaction.getHash(forWitness!)) @@ -138,8 +139,33 @@ export class Block { : rootHash } + getWitnessCommit (): Buffer | null { + if (!txesHaveWitnessCommit(this.transactions!)) return null + + // The merkle root for the witness data is in an OP_RETURN output. + // There is no rule for the index of the output, so use filter to find it. + // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed + // If multiple commits are found, the output with highest index is assumed. + let witnessCommits = this.transactions![0].outs + .filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))) + .map(out => out.script.slice(6, 38)) + if (witnessCommits.length === 0) return null + // Use the commit with the highest output (should only be one though) + let result = witnessCommits[witnessCommits.length - 1] + + if (!(result instanceof Buffer && result.length === 32)) return null + return result + } + hasWitnessCommit (): boolean { - return txesHaveWitness(this.transactions!) + if (this.witnessCommit instanceof Buffer && + this.witnessCommit.length === 32) return true + if (this.getWitnessCommit() !== null) return true + return false + } + + hasWitness (): boolean { + return anyTxHasWitness(this.transactions!) } byteLength (headersOnly: boolean): number { @@ -209,8 +235,12 @@ export class Block { } checkTxRoots (): boolean { + // If the Block has segwit transactions but no witness commit, + // there's no way it can be valid, so fail the check. + let hasWitnessCommit = this.hasWitnessCommit() + if (!hasWitnessCommit && this.hasWitness()) return false return this.__checkMerkleRoot() && - (this.hasWitnessCommit() ? this.__checkWitnessCommit() : true) + (hasWitnessCommit ? this.__checkWitnessCommit() : true) } checkMerkleRoot (): boolean { diff --git a/types/block.d.ts b/types/block.d.ts index 3ff2926..8b4f2f1 100644 --- a/types/block.d.ts +++ b/types/block.d.ts @@ -14,7 +14,9 @@ export declare class Block { static fromHex(hex: string): Block; static calculateTarget(bits: number): Buffer; static calculateMerkleRoot(transactions: Array<Transaction>, forWitness?: boolean): Buffer; + getWitnessCommit(): Buffer | null; hasWitnessCommit(): boolean; + hasWitness(): boolean; byteLength(headersOnly: boolean): number; getHash(): Buffer; getId(): string; From 5990ece108b00ccd0d0f61c0b5fbbf3b1fb4fa38 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 17 Jan 2019 17:35:49 +0900 Subject: [PATCH 217/568] Merge Integration test changes from master --- test/integration/addresses.js | 95 +++++++++++++++----------------- test/integration/transactions.js | 34 +++++++++++- 2 files changed, 75 insertions(+), 54 deletions(-) diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 0024410..7cf2612 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -2,34 +2,35 @@ const { describe, it } = require('mocha') const assert = require('assert') const bitcoin = require('../../') const dhttp = require('dhttp/200') - -const LITECOIN = { - messagePrefix: '\x19Litecoin Signed Message:\n', - bip32: { - public: 0x019da462, - private: 0x019d9cfe - }, - pubKeyHash: 0x30, - scriptHash: 0x32, - wif: 0xb0 -} - -// deterministic RNG for testing only -function rng () { return Buffer.from('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } +const TESTNET = bitcoin.networks.testnet describe('bitcoinjs-lib (addresses)', function () { - it('can generate a random address', function () { - const keyPair = bitcoin.ECPair.makeRandom({ rng: rng }) + it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', function (done) { + const keyPair = bitcoin.ECPair.makeRandom() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) - assert.strictEqual(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64') + // bitcoin P2PKH addresses start with a '1' + assert.strictEqual(address.startsWith('1'), true) + + dhttp({ + method: 'GET', + url: 'https://blockchain.info/rawaddr/' + address + }, function (err, result) { + if (err) return done(err) + + // random private keys [probably!] have no transactions + assert.strictEqual(result.n_tx, 0) + assert.strictEqual(result.total_received, 0) + assert.strictEqual(result.total_sent, 0) + done() + }) }) it('can import an address via WIF', function () { - const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) - assert.strictEqual(address, '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31') + assert.strictEqual(address, '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH') }) it('can generate a P2SH, pay-to-multisig (2-of-3) address', function () { @@ -46,19 +47,19 @@ describe('bitcoinjs-lib (addresses)', function () { }) it('can generate a SegWit address', function () { - const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) - assert.strictEqual(address, 'bc1qt97wqg464zrhnx23upykca5annqvwkwujjglky') + assert.strictEqual(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4') }) it('can generate a SegWit address (via P2SH)', function () { - const keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') const { address } = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) }) - assert.strictEqual(address, '34AgLJhwXrvmkZS1o5TrcdeevMt22Nar53') + assert.strictEqual(address, '3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN') }) it('can generate a P2WSH (SegWit), pay-to-multisig (3-of-4) address', function () { @@ -89,41 +90,31 @@ describe('bitcoinjs-lib (addresses)', function () { assert.strictEqual(address, '3P4mrxQfmExfhxqjLnR2Ah4WES5EB1KBrN') }) - it('can support the retrieval of transactions for an address (via 3PBP)', function (done) { - const keyPair = bitcoin.ECPair.makeRandom() - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) - - dhttp({ - method: 'GET', - url: 'https://blockchain.info/rawaddr/' + address - }, function (err, result) { - if (err) return done(err) - - // random private keys [probably!] have no transactions - assert.strictEqual(result.n_tx, 0) - assert.strictEqual(result.total_received, 0) - assert.strictEqual(result.total_sent, 0) - done() - }) - }) - - // other networks + // examples using other network information it('can generate a Testnet address', function () { - const testnet = bitcoin.networks.testnet - const keyPair = bitcoin.ECPair.makeRandom({ network: testnet, rng: rng }) - const wif = keyPair.toWIF() - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: testnet }) + const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET }) + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: TESTNET }) - assert.strictEqual(address, 'mubSzQNtZfDj1YdNP6pNDuZy6zs6GDn61L') - assert.strictEqual(wif, 'cRgnQe9MUu1JznntrLaoQpB476M8PURvXVQB5R2eqms5tXnzNsrr') + // bitcoin testnet P2PKH addresses start with a 'm' or 'n' + assert.strictEqual(address.startsWith('m') || address.startsWith('n'), true) }) it('can generate a Litecoin address', function () { - const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN, rng: rng }) - const wif = keyPair.toWIF() + // WARNING: although possible, bitcoinjs is NOT necessarily compatible with Litecoin + const LITECOIN = { + messagePrefix: '\x19Litecoin Signed Message:\n', + bip32: { + public: 0x019da462, + private: 0x019d9cfe + }, + pubKeyHash: 0x30, + scriptHash: 0x32, + wif: 0xb0 + } + + const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }) const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: LITECOIN }) - assert.strictEqual(address, 'LZJSxZbjqJ2XVEquqfqHg1RQTDdfST5PTn') - assert.strictEqual(wif, 'T7A4PUSgTDHecBxW1ZiYFrDNRih2o7M8Gf9xpoCgudPF9gDiNvuS') + assert.strictEqual(address.startsWith('L'), true) }) }) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index e828414..f725241 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -64,7 +64,7 @@ describe('bitcoinjs-lib (transactions)', function () { txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" txb.addOutput(aliceCpkh.address, 1e4) // Alice's change - // (in)(4e4 + 2e4) - (out)(1e4 + 3e4) = (fee)2e4 = 20000, this is the miner fee + // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee // Alice signs each input with the respective private keys txb.sign(0, alice1) @@ -258,7 +258,7 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) - it('can verify Transaction signatures', function () { + it('can verify Transaction (P2PKH) signatures', function () { const txHex = '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700' const keyPairs = [ '032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d', @@ -281,4 +281,34 @@ describe('bitcoinjs-lib (transactions)', function () { assert.strictEqual(keyPair.verify(hash, ss.signature), true) }) }) + + it('can verify Transaction (P2SH(P2WPKH)) signatures', function () { + const utxos = { + 'f72d1d83ac40fcedd01415751556a905844ab5f44bbb7728565ebb91b1590109:0': { + value: 50000 + } + } + + const txHex = '02000000000101090159b191bb5e562877bb4bf4b54a8405a95615751514d0edfc40ac831d2df7000000001716001435a179e5516947a39ae9c8a25e9fe62c0fc598edffffffff01204e0000000000001976a91431d43308d3c886d53e9ae8a45728370571ff456988ac0247304402206ec41f685b997a51f325b07ee852e82a535f6b52ef54485cc133e05168aa052a022070bafa86108acb51c77b2b259ae8fb7fd1efa10fef804fcfe9b13c2db719acf5012103fb03e9d0a9af86cbed94225dbb8bb70f6b82109bce0a61ddcf41dab6cbb4871100000000' + const tx = bitcoin.Transaction.fromHex(txHex) + + tx.ins.forEach(function (input, i) { + const txId = Buffer.from(input.hash).reverse().toString('hex') + const utxo = utxos[`${txId}:${i}`] + if (!utxo) throw new Error('Missing utxo') + + const p2sh = bitcoin.payments.p2sh({ + input: input.script, + witness: input.witness + }) + const p2wpkh = bitcoin.payments.p2wpkh(p2sh.redeem) + const p2pkh = bitcoin.payments.p2pkh({ pubkey: p2wpkh.pubkey }) // because P2WPKH is annoying + + const ss = bitcoin.script.signature.decode(p2wpkh.signature) + const hash = tx.hashForWitnessV0(i, p2pkh.output, utxo.value, ss.hashType) + const keyPair = bitcoin.ECPair.fromPublicKey(p2wpkh.pubkey) // aka, cQ3EtF4mApRcogNGSeyPTKbmfxxn3Yfb1wecfKSws9a8bnYuxoAk + + assert.strictEqual(keyPair.verify(hash, ss.signature), true) + }) + }) }) From 3fa86d0f3a034eb8dc8ee4781225852d396d3166 Mon Sep 17 00:00:00 2001 From: johnta0 <22534726+johnta0@users.noreply.github.com> Date: Sat, 19 Jan 2019 12:44:54 +0900 Subject: [PATCH 218/568] Remove unused module declaration --- ts_src/address.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ts_src/address.ts b/ts_src/address.ts index 7d9d351..4299321 100644 --- a/ts_src/address.ts +++ b/ts_src/address.ts @@ -1,4 +1,3 @@ -import * as Networks from './networks' import { Network } from './networks' import * as types from './types' import * as bscript from './script' From e1ca3775958b20f8249e993a06966346e5411d91 Mon Sep 17 00:00:00 2001 From: johnta0 <22534726+johnta0@users.noreply.github.com> Date: Thu, 24 Jan 2019 12:08:29 +0900 Subject: [PATCH 219/568] Generate package-lock.json Co-authored-by: junderw <junderwood@bitcoinbank.co.jp> --- package-lock.json | 4745 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4745 insertions(+) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e34ecd4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4745 @@ +{ + "name": "bitcoinjs-lib", + "version": "4.0.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base-x": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz", + "integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "bech32": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.3.tgz", + "integrity": "sha512-yuVFUvrNcoJi0sv5phmqc6P+Fl1HjRDRNOOkHY2X/3LBy2bIGNSFx4fZ95HMaXHupuS7cZR15AsvtmCIF4UEyg==" + }, + "bindings": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz", + "integrity": "sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew==" + }, + "bip32": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-1.0.2.tgz", + "integrity": "sha512-kedLYj8yvYzND+EfzeoMSlGiN7ImiRBF/MClJSZPkMfcU+OQO7ZpL5L/Yg+TunebBZIHhunstiQF//KLKSF5rg==", + "requires": { + "bs58check": "^2.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "tiny-secp256k1": "^1.0.0", + "typeforce": "^1.11.5", + "wif": "^2.0.6" + } + }, + "bip39": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-2.5.0.tgz", + "integrity": "sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA==", + "dev": true, + "requires": { + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1", + "safe-buffer": "^5.0.1", + "unorm": "^1.3.3" + } + }, + "bip65": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bip65/-/bip65-1.0.3.tgz", + "integrity": "sha512-RQ1nc7xtnLa5XltnCqkoR2zmhuz498RjMJwrLKQzOE049D1HUqnYfon7cVSbwS5UGm0/EQlC2CH+NY3MyITA4Q==", + "dev": true + }, + "bip66": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "bip68": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bip68/-/bip68-1.0.4.tgz", + "integrity": "sha512-O1htyufFTYy3EO0JkHg2CLykdXEtV2ssqw47Gq9A0WByp662xpJnMEB9m43LZjsSDjIAOozWRExlFQk2hlV1XQ==", + "dev": true + }, + "bitcoin-ops": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz", + "integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "deglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.1.tgz", + "integrity": "sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw==", + "dev": true, + "requires": { + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^3.0.9", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1" + } + }, + "dhttp": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dhttp/-/dhttp-3.0.3.tgz", + "integrity": "sha512-map1b8iyvxSv0uEw3DUDDK5XvH3aYA7QU9DcXy8e3FBIXSwHPHTZWVrOot7Iu9mieWq5XcrZemEJlob6IdCBmg==", + "dev": true, + "requires": { + "statuses": "^1.5.0" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.18.2.tgz", + "integrity": "sha512-qy4i3wODqKMYfz9LUI8N2qYDkHkoieTbiHpMrYUI/WbjhXJQr7lI4VngixTgaG+yHX+NBCv7nW4hA0ShbvaNKw==", + "dev": true, + "requires": { + "ajv": "^5.3.0", + "babel-code-frame": "^6.22.0", + "chalk": "^2.1.0", + "concat-stream": "^1.6.0", + "cross-spawn": "^5.1.0", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.2", + "esquery": "^1.0.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.0.1", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^3.0.6", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.9.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "require-uncached": "^1.0.3", + "semver": "^5.3.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "~2.0.1", + "table": "4.0.2", + "text-table": "~0.2.0" + } + }, + "eslint-config-standard": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-11.0.0.tgz", + "integrity": "sha512-oDdENzpViEe5fwuRCWla7AXQd++/oyIp8zP+iP9jiUPG6NBj3SHgdgtl/kTn00AjeN+1HNvavTKmYbMo+xMOlw==", + "dev": true + }, + "eslint-config-standard-jsx": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-5.0.0.tgz", + "integrity": "sha512-rLToPAEqLMPBfWnYTu6xRhm2OWziS2n40QFqJ8jAM8NSVzeVKTa3nclhsU4DpPJQRY60F34Oo1wi/71PN/eITg==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "eslint-module-utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz", + "integrity": "sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w==", + "dev": true, + "requires": { + "debug": "^2.6.8", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.9.0.tgz", + "integrity": "sha1-JgAu+/ylmJtyiKwEdQi9JPIXsWk=", + "dev": true, + "requires": { + "builtin-modules": "^1.1.1", + "contains-path": "^0.1.0", + "debug": "^2.6.8", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.1", + "eslint-module-utils": "^2.1.1", + "has": "^1.0.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.3", + "read-pkg-up": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + } + } + }, + "eslint-plugin-node": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz", + "integrity": "sha512-Q/Cc2sW1OAISDS+Ji6lZS2KV4b7ueA/WydVWd1BECTQwVvfQy5JAi3glhINoKzoMnfnuRgNP+ZWKrGAbp3QDxw==", + "dev": true, + "requires": { + "ignore": "^3.3.6", + "minimatch": "^3.0.4", + "resolve": "^1.3.3", + "semver": "^5.4.1" + } + }, + "eslint-plugin-promise": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.7.0.tgz", + "integrity": "sha512-2WO+ZFh7vxUKRfR0cOIMrWgYKdR6S1AlOezw6pC52B6oYpd5WFghN+QHxvrRdZMtbo8h3dfUZ2o1rWb0UPbKtg==", + "dev": true + }, + "eslint-plugin-react": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz", + "integrity": "sha512-KC7Snr4YsWZD5flu6A5c0AcIZidzW3Exbqp7OT67OaD2AppJtlBr/GuPrW/vaQM/yfZotEvKAdrxrO+v8vwYJA==", + "dev": true, + "requires": { + "doctrine": "^2.0.2", + "has": "^1.0.1", + "jsx-ast-utils": "^2.0.1", + "prop-types": "^15.6.0" + } + }, + "eslint-plugin-standard": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz", + "integrity": "sha1-NNDJFbRe3G8BA5PH7vOCOwhWXPI=", + "dev": true + }, + "eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "fill-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", + "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", + "dev": true, + "requires": { + "is-object": "~1.0.1", + "merge-descriptors": "~1.0.0" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", + "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoodwink": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hoodwink/-/hoodwink-2.0.0.tgz", + "integrity": "sha512-j1jog3tDfhpWlqbVbh29qc7FG7w+NT4ed+QQFGqvww83+50AzzretB7wykZGOe28mBdvCYH3GdHaVWJQ2lJ/4w==", + "dev": true + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "jsx-ast-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", + "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", + "dev": true, + "requires": { + "array-includes": "^3.0.3" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merkle-lib": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", + "integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY=" + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimaldata": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minimaldata/-/minimaldata-1.0.2.tgz", + "integrity": "sha1-AfOywB2LJzmEP9hT1AY2xaLrdms=", + "dev": true, + "requires": { + "bitcoin-ops": "^1.3.0", + "pushdata-bitcoin": "^1.0.1" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + } + }, + "module-not-found-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", + "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "nyc": { + "version": "11.9.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.9.0.tgz", + "integrity": "sha512-w8OdJAhXL5izerzZMdqzYKMj/pgHJyY3qEPYBjLLxrhcVoHEY9pU5ENIiZyCgG9OR7x3VcUMoD40o6PtVpfR4g==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "arrify": "^1.0.1", + "caching-transform": "^1.0.0", + "convert-source-map": "^1.5.1", + "debug-log": "^1.0.1", + "default-require-extensions": "^1.0.0", + "find-cache-dir": "^0.1.1", + "find-up": "^2.1.0", + "foreground-child": "^1.5.3", + "glob": "^7.0.6", + "istanbul-lib-coverage": "^1.1.2", + "istanbul-lib-hook": "^1.1.0", + "istanbul-lib-instrument": "^1.10.0", + "istanbul-lib-report": "^1.1.3", + "istanbul-lib-source-maps": "^1.2.3", + "istanbul-reports": "^1.4.0", + "md5-hex": "^1.2.0", + "merge-source-map": "^1.1.0", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.0", + "resolve-from": "^2.0.0", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.1", + "spawn-wrap": "^1.4.2", + "test-exclude": "^4.2.0", + "yargs": "11.1.0", + "yargs-parser": "^8.0.0" + }, + "dependencies": { + "align-text": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "amdefine": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "append-transform": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "requires": { + "default-require-extensions": "^1.0.0" + } + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "bundled": true, + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "arrify": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "async": { + "version": "1.5.2", + "bundled": true, + "dev": true + }, + "atob": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "babel-generator": { + "version": "6.26.1", + "bundled": true, + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "bundled": true, + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "base": { + "version": "0.11.2", + "bundled": true, + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "builtin-modules": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "caching-transform": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "md5-hex": "^1.2.0", + "mkdirp": "^0.5.1", + "write-file-atomic": "^1.1.4" + } + }, + "camelcase": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true + }, + "center-align": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "class-utils": { + "version": "0.3.6", + "bundled": true, + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "cliui": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "commondir": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "convert-source-map": { + "version": "1.5.1", + "bundled": true, + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "core-js": { + "version": "2.5.6", + "bundled": true, + "dev": true + }, + "cross-spawn": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "default-require-extensions": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "strip-bom": "^2.0.0" + } + }, + "define-property": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "detect-indent": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "error-ex": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "esutils": { + "version": "2.0.2", + "bundled": true, + "dev": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "fill-range": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "requires": { + "commondir": "^1.0.1", + "mkdirp": "^0.5.1", + "pkg-dir": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "for-in": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "foreground-child": { + "version": "1.5.6", + "bundled": true, + "dev": true, + "requires": { + "cross-spawn": "^4", + "signal-exit": "^3.0.0" + } + }, + "fragment-cache": { + "version": "0.2.1", + "bundled": true, + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "get-value": { + "version": "2.0.6", + "bundled": true, + "dev": true + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "9.18.0", + "bundled": true, + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "handlebars": { + "version": "4.0.11", + "bundled": true, + "dev": true, + "requires": { + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "bundled": true, + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "has-ansi": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "has-value": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "has-values": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hosted-git-info": { + "version": "2.6.0", + "bundled": true, + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "invariant": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-arrayish": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "bundled": true, + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-odd": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "bundled": true, + "dev": true + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "isobject": "^3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "is-stream": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "istanbul-lib-coverage": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "istanbul-lib-hook": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "append-transform": "^0.4.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.10.1", + "bundled": true, + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.0", + "semver": "^5.3.0" + } + }, + "istanbul-lib-report": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "istanbul-lib-coverage": "^1.1.2", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" + }, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "1.2.3", + "bundled": true, + "dev": true, + "requires": { + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.1.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "istanbul-reports": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "handlebars": "^4.0.3" + } + }, + "js-tokens": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "jsesc": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lazy-cache": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "optional": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "bundled": true, + "dev": true + } + } + }, + "lodash": { + "version": "4.17.10", + "bundled": true, + "dev": true + }, + "longest": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "loose-envify": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "js-tokens": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.3", + "bundled": true, + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "map-cache": { + "version": "0.2.2", + "bundled": true, + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5-hex": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "md5-o-matic": "^0.1.1" + } + }, + "md5-o-matic": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "merge-source-map": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "bundled": true, + "dev": true + } + } + }, + "micromatch": { + "version": "3.1.10", + "bundled": true, + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "mimic-fn": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mixin-deep": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "nanomatch": { + "version": "1.2.9", + "bundled": true, + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-odd": "^2.0.0", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.4.0", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "bundled": true, + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "isobject": "^3.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "object.pick": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "isobject": "^3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "p-limit": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "bundled": true, + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "pascalcase": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "path-type": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "find-up": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "bundled": true, + "dev": true + }, + "regex-not": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "repeat-element": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "bundled": true, + "dev": true + }, + "repeating": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "resolve-from": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "bundled": true, + "dev": true + }, + "ret": { + "version": "0.1.15", + "bundled": true, + "dev": true + }, + "right-align": { + "version": "0.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "safe-regex": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "semver": { + "version": "5.5.0", + "bundled": true, + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "set-value": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "slide": { + "version": "1.1.6", + "bundled": true, + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "bundled": true, + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.2.0" + } + }, + "source-map": { + "version": "0.5.7", + "bundled": true, + "dev": true + }, + "source-map-resolve": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "atob": "^2.0.0", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "bundled": true, + "dev": true + }, + "spawn-wrap": { + "version": "1.4.2", + "bundled": true, + "dev": true, + "requires": { + "foreground-child": "^1.5.6", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.2", + "which": "^1.3.0" + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "split-string": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "static-extend": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "test-exclude": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "requires": { + "arrify": "^1.0.1", + "micromatch": "^3.1.8", + "object-assign": "^4.1.0", + "read-pkg-up": "^1.0.1", + "require-main-filename": "^1.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "braces": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "bundled": true, + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "bundled": true, + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "bundled": true, + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "to-fast-properties": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + } + } + }, + "trim-right": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "union-value": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "bundled": true, + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unset-value": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "bundled": true, + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "isobject": { + "version": "3.0.1", + "bundled": true, + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "bundled": true, + "dev": true + }, + "use": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "kind-of": "^6.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "bundled": true, + "dev": true + } + } + }, + "validate-npm-package-license": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "window-size": { + "version": "0.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "wordwrap": { + "version": "0.0.3", + "bundled": true, + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" + } + }, + "y18n": { + "version": "3.2.1", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "yargs": { + "version": "11.1.0", + "bundled": true, + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "cliui": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "8.1.0", + "bundled": true, + "dev": true, + "requires": { + "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "bundled": true, + "dev": true + } + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-keys": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "dev": true, + "requires": { + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prop-types": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", + "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "dev": true, + "requires": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "proxyquire": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.0.tgz", + "integrity": "sha512-kptdFArCfGRtQFv3Qwjr10lwbEV0TBJYvfqzhwucyfEXqVgmnAkyEw/S3FYzR5HI9i5QOq4rcqQjZ6AlknlCDQ==", + "dev": true, + "requires": { + "fill-keys": "^1.0.2", + "module-not-found-error": "^1.0.0", + "resolve": "~1.8.1" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "pushdata-bitcoin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz", + "integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=", + "requires": { + "bitcoin-ops": "^1.3.0" + } + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + } + }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "*" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "standard": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/standard/-/standard-11.0.1.tgz", + "integrity": "sha512-nu0jAcHiSc8H+gJCXeiziMVZNDYi8MuqrYJKxTgjP4xKXZMKm311boqQIzDrYI/ktosltxt2CbDjYQs9ANC8IA==", + "dev": true, + "requires": { + "eslint": "~4.18.0", + "eslint-config-standard": "11.0.0", + "eslint-config-standard-jsx": "5.0.0", + "eslint-plugin-import": "~2.9.0", + "eslint-plugin-node": "~6.0.0", + "eslint-plugin-promise": "~3.7.0", + "eslint-plugin-react": "~7.7.0", + "eslint-plugin-standard": "~3.0.1", + "standard-engine": "~8.0.0" + } + }, + "standard-engine": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-8.0.1.tgz", + "integrity": "sha512-LA531C3+nljom/XRvdW/hGPXwmilRkaRkENhO3FAGF1Vtq/WtCXzgmnc5S6vUHHsgv534MRy02C1ikMwZXC+tw==", + "dev": true, + "requires": { + "deglob": "^2.1.0", + "get-stdin": "^6.0.0", + "minimist": "^1.1.0", + "pkg-conf": "^2.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tiny-secp256k1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.0.1.tgz", + "integrity": "sha512-Wz2kMPWtCI5XBftFeF3bUL8uz2+VlasniKwOkRPjvL7h1QVd9rbhrve/HWUu747kJKzVf1XHonzcdM4Ut8fvww==", + "requires": { + "bindings": "^1.3.0", + "bn.js": "^4.11.8", + "create-hmac": "^1.1.7", + "elliptic": "^6.4.0", + "nan": "^2.10.0" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "unorm": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.4.1.tgz", + "integrity": "sha1-NkIA1fE2RsqLzURJAnEzVhR5IwA=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "varuint-bitcoin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz", + "integrity": "sha512-jCEPG+COU/1Rp84neKTyDJQr478/hAfVp5xxYn09QEH0yBjbmPeMfuuQIrp+BUD83hybtYZKhr5elV3bvdV1bA==", + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wif": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", + "integrity": "sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=", + "requires": { + "bs58check": "<3.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } +} From 568e5c818988eeb01deba5e8bb52d6930ac6ac4c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 25 Jan 2019 14:55:56 +0900 Subject: [PATCH 220/568] 4.0.3 --- CHANGELOG.md | 10 ++++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 451a168..f31645b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 4.0.3 +__fixed__ +- Fixed `TransactionBuilder` to require that the Transaction has outputs before signing (#1151) +- Fixed `payments.p2sh`, which now takes the network from the redeem attribute if one is not given in the object argument (#1232) +- Fixed `Block.calculateTarget` to allow for exponents up to 29 (#1285) +- Fixed some low priority rarely occurring bugs with multisig payments and `TransactionBuilder` multisig processing (#1307) + +__added__ +- Regtest network object to `networks` (#1261) + # 4.0.2 __fixed__ - Fixed `TransactionBuilder` not throwing when payment type validation should fail (#1195) diff --git a/package-lock.json b/package-lock.json index e34ecd4..3009813 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "4.0.2", + "version": "4.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 755be28..0c3a31f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "4.0.2", + "version": "4.0.3", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "engines": { From 06868f520e6a75b8dd1acdb60f522a8a55f2f6d9 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Mon, 4 Mar 2019 00:05:11 +0900 Subject: [PATCH 221/568] build: move typescript to devDependencies --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a38c47..384b53d 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,6 @@ "randombytes": "^2.0.1", "tiny-secp256k1": "^1.0.0", "typeforce": "^1.11.3", - "typescript": "3.2.2", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" }, @@ -71,6 +70,7 @@ "nyc": "^11.8.0", "proxyquire": "^2.0.1", "standard": "^11.0.1", + "typescript": "3.2.2", "typescript-eslint-parser": "^21.0.2" }, "license": "MIT", From de361673cf99738231a8422fd8bf3c0fc57c27ff Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Mon, 4 Mar 2019 00:10:12 +0900 Subject: [PATCH 222/568] build: add compiler options to forbid unused local variables and parameters --- ts_src/payments/p2sh.ts | 2 +- ts_src/payments/p2wsh.ts | 2 +- tsconfig.json | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index 1aa1a94..daaa8cc 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -1,5 +1,5 @@ import { Payment, PaymentOpts } from './index' // eslint-disable-line -import { Network, bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line +import { bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line import * as bscript from '../script' import * as bcrypto from '../crypto' import * as lazy from './lazy' diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 452dcc3..edb0a69 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -1,5 +1,5 @@ import { Payment, PaymentOpts } from './index' // eslint-disable-line -import { Network, bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line +import { bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line import * as bscript from '../script' import * as bcrypto from '../crypto' import * as lazy from './lazy' diff --git a/tsconfig.json b/tsconfig.json index 0ab4612..f770a45 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,9 @@ "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true, - "esModuleInterop": false + "esModuleInterop": false, + "noUnusedLocals": true, + "noUnusedParameters": true }, "include": [ "ts_src/**/*.ts" From c74ab67ccf23b3796b17808e765ff580685b31ef Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Sun, 3 Mar 2019 23:07:34 +0900 Subject: [PATCH 223/568] style: add prettier --- .prettierignore | 0 .prettierrc.json | 4 ++++ package.json | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 .prettierignore create mode 100644 .prettierrc.json diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..e69de29 diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..a20502b --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} diff --git a/package.json b/package.json index 384b53d..e55b1a8 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", + "format": "prettier ts_src/*.ts ts_src/**/*.ts --ignore-path ./.prettierignore --write", "integration": "npm run build && npm run nobuild:integration", "nobuild:coverage-report": "nyc report --reporter=lcov", "nobuild:coverage-html": "nyc report --reporter=html", @@ -68,6 +69,7 @@ "minimaldata": "^1.0.2", "mocha": "^5.2.0", "nyc": "^11.8.0", + "prettier": "^1.16.4", "proxyquire": "^2.0.1", "standard": "^11.0.1", "typescript": "3.2.2", From 03632f15072470ffa3fd1b6f4e3cbf9d19c6fee9 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Sun, 3 Mar 2019 23:07:49 +0900 Subject: [PATCH 224/568] style: apply prettier --- ts_src/address.ts | 128 ++--- ts_src/block.ts | 365 ++++++++------- ts_src/bufferutils.ts | 57 ++- ts_src/classify.ts | 96 ++-- ts_src/crypto.ts | 28 +- ts_src/ecpair.ts | 168 +++---- ts_src/index.ts | 42 +- ts_src/networks.ts | 22 +- ts_src/payments/embed.ts | 79 ++-- ts_src/payments/index.ts | 50 +- ts_src/payments/lazy.ts | 34 +- ts_src/payments/p2ms.ts | 216 +++++---- ts_src/payments/p2pk.ts | 114 ++--- ts_src/payments/p2pkh.ts | 195 ++++---- ts_src/payments/p2sh.ts | 231 +++++----- ts_src/payments/p2wpkh.ts | 198 ++++---- ts_src/payments/p2wsh.ts | 219 +++++---- ts_src/script.ts | 248 +++++----- ts_src/script_number.ts | 77 ++-- ts_src/script_signature.ts | 88 ++-- ts_src/templates/nulldata.ts | 21 +- ts_src/transaction.ts | 692 ++++++++++++++------------- ts_src/transaction_builder.ts | 847 ++++++++++++++++++++-------------- ts_src/types.ts | 64 +-- 24 files changed, 2321 insertions(+), 1958 deletions(-) diff --git a/ts_src/address.ts b/ts_src/address.ts index 4299321..d547e78 100644 --- a/ts_src/address.ts +++ b/ts_src/address.ts @@ -1,101 +1,119 @@ -import { Network } from './networks' -import * as types from './types' -import * as bscript from './script' -import * as networks from './networks' -import * as payments from './payments' +import { Network } from './networks'; +import * as types from './types'; +import * as bscript from './script'; +import * as networks from './networks'; +import * as payments from './payments'; -const bech32 = require('bech32') -const bs58check = require('bs58check') -const typeforce = require('typeforce') +const bech32 = require('bech32'); +const bs58check = require('bs58check'); +const typeforce = require('typeforce'); export type Base58CheckResult = { hash: Buffer; version: number; -} +}; export type Bech32Result = { version: number; prefix: string; data: Buffer; -} +}; -export function fromBase58Check (address: string): Base58CheckResult { - const payload = bs58check.decode(address) +export function fromBase58Check(address: string): Base58CheckResult { + const payload = bs58check.decode(address); // TODO: 4.0.0, move to "toOutputScript" - if (payload.length < 21) throw new TypeError(address + ' is too short') - if (payload.length > 21) throw new TypeError(address + ' is too long') + if (payload.length < 21) throw new TypeError(address + ' is too short'); + if (payload.length > 21) throw new TypeError(address + ' is too long'); - const version = payload.readUInt8(0) - const hash = payload.slice(1) + const version = payload.readUInt8(0); + const hash = payload.slice(1); - return { version: version, hash: hash } + return { version: version, hash: hash }; } -export function fromBech32 (address: string): Bech32Result { - const result = bech32.decode(address) - const data = bech32.fromWords(result.words.slice(1)) +export function fromBech32(address: string): Bech32Result { + const result = bech32.decode(address); + const data = bech32.fromWords(result.words.slice(1)); return { version: result.words[0], prefix: result.prefix, - data: Buffer.from(data) - } + data: Buffer.from(data), + }; } -export function toBase58Check (hash: Buffer, version: number): string { - typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments) +export function toBase58Check(hash: Buffer, version: number): string { + typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); - const payload = Buffer.allocUnsafe(21) - payload.writeUInt8(version, 0) - hash.copy(payload, 1) + const payload = Buffer.allocUnsafe(21); + payload.writeUInt8(version, 0); + hash.copy(payload, 1); - return bs58check.encode(payload) + return bs58check.encode(payload); } -export function toBech32 (data: Buffer, version: number, prefix: string): string { - const words = bech32.toWords(data) - words.unshift(version) +export function toBech32( + data: Buffer, + version: number, + prefix: string, +): string { + const words = bech32.toWords(data); + words.unshift(version); - return bech32.encode(prefix, words) + return bech32.encode(prefix, words); } -export function fromOutputScript (output: Buffer, network: Network): string { //TODO: Network - network = network || networks.bitcoin +export function fromOutputScript(output: Buffer, network: Network): string { + //TODO: Network + network = network || networks.bitcoin; - try { return <string>payments.p2pkh({ output, network }).address } catch (e) {} - try { return <string>payments.p2sh({ output, network }).address } catch (e) {} - try { return <string>payments.p2wpkh({ output, network }).address } catch (e) {} - try { return <string>payments.p2wsh({ output, network }).address } catch (e) {} - - throw new Error(bscript.toASM(output) + ' has no matching Address') -} - -export function toOutputScript (address: string, network: Network): Buffer { - network = network || networks.bitcoin - - let decodeBase58: Base58CheckResult | undefined = undefined - let decodeBech32: Bech32Result | undefined = undefined try { - decodeBase58 = fromBase58Check(address) + return <string>payments.p2pkh({ output, network }).address; + } catch (e) {} + try { + return <string>payments.p2sh({ output, network }).address; + } catch (e) {} + try { + return <string>payments.p2wpkh({ output, network }).address; + } catch (e) {} + try { + return <string>payments.p2wsh({ output, network }).address; + } catch (e) {} + + throw new Error(bscript.toASM(output) + ' has no matching Address'); +} + +export function toOutputScript(address: string, network: Network): Buffer { + network = network || networks.bitcoin; + + let decodeBase58: Base58CheckResult | undefined = undefined; + let decodeBech32: Bech32Result | undefined = undefined; + try { + decodeBase58 = fromBase58Check(address); } catch (e) {} if (decodeBase58) { - if (decodeBase58.version === network.pubKeyHash) return <Buffer>payments.p2pkh({ hash: decodeBase58.hash }).output - if (decodeBase58.version === network.scriptHash) return <Buffer>payments.p2sh({ hash: decodeBase58.hash }).output + if (decodeBase58.version === network.pubKeyHash) + return <Buffer>payments.p2pkh({ hash: decodeBase58.hash }).output; + if (decodeBase58.version === network.scriptHash) + return <Buffer>payments.p2sh({ hash: decodeBase58.hash }).output; } else { try { - decodeBech32 = fromBech32(address) + decodeBech32 = fromBech32(address); } catch (e) {} if (decodeBech32) { - if (decodeBech32.prefix !== network.bech32) throw new Error(address + ' has an invalid prefix') + if (decodeBech32.prefix !== network.bech32) + throw new Error(address + ' has an invalid prefix'); if (decodeBech32.version === 0) { - if (decodeBech32.data.length === 20) return <Buffer>payments.p2wpkh({ hash: decodeBech32.data }).output - if (decodeBech32.data.length === 32) return <Buffer>payments.p2wsh({ hash: decodeBech32.data }).output + if (decodeBech32.data.length === 20) + return <Buffer>payments.p2wpkh({ hash: decodeBech32.data }).output; + if (decodeBech32.data.length === 32) + return <Buffer>payments.p2wsh({ hash: decodeBech32.data }).output; } } } - throw new Error(address + ' has no matching Script') + throw new Error(address + ' has no matching Script'); } diff --git a/ts_src/block.ts b/ts_src/block.ts index e3d1fc5..feb141e 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -1,17 +1,22 @@ -import { Transaction } from './transaction' -import * as types from './types' -import * as bcrypto from './crypto' -import { reverseBuffer } from './bufferutils' +import { Transaction } from './transaction'; +import * as types from './types'; +import * as bcrypto from './crypto'; +import { reverseBuffer } from './bufferutils'; -const fastMerkleRoot = require('merkle-lib/fastRoot') -const typeforce = require('typeforce') -const varuint = require('varuint-bitcoin') +const fastMerkleRoot = require('merkle-lib/fastRoot'); +const typeforce = require('typeforce'); +const varuint = require('varuint-bitcoin'); -const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions') -const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block') +const errorMerkleNoTxes = new TypeError( + 'Cannot compute merkle root for zero transactions', +); +const errorWitnessNotSegwit = new TypeError( + 'Cannot compute witness commit for non-segwit block', +); -function txesHaveWitnessCommit (transactions: Array<Transaction>): boolean { - return transactions instanceof Array && +function txesHaveWitnessCommit(transactions: Array<Transaction>): boolean { + return ( + transactions instanceof Array && transactions[0] && transactions[0].ins && transactions[0].ins instanceof Array && @@ -19,255 +24,281 @@ function txesHaveWitnessCommit (transactions: Array<Transaction>): boolean { transactions[0].ins[0].witness && transactions[0].ins[0].witness instanceof Array && transactions[0].ins[0].witness.length > 0 + ); } -function anyTxHasWitness (transactions: Array<Transaction>): boolean { - return transactions instanceof Array && - transactions.some(tx => - typeof tx === 'object' && - tx.ins instanceof Array && - tx.ins.some(input => - typeof input === 'object' && - input.witness instanceof Array && - input.witness.length > 0 - ) +function anyTxHasWitness(transactions: Array<Transaction>): boolean { + return ( + transactions instanceof Array && + transactions.some( + tx => + typeof tx === 'object' && + tx.ins instanceof Array && + tx.ins.some( + input => + typeof input === 'object' && + input.witness instanceof Array && + input.witness.length > 0, + ), ) + ); } export class Block { - version: number - prevHash?: Buffer - merkleRoot?: Buffer - timestamp: number - witnessCommit?: Buffer - bits: number - nonce: number - transactions?: Array<Transaction> + version: number; + prevHash?: Buffer; + merkleRoot?: Buffer; + timestamp: number; + witnessCommit?: Buffer; + bits: number; + nonce: number; + transactions?: Array<Transaction>; - constructor () { - this.version = 1 - this.timestamp = 0 - this.bits = 0 - this.nonce = 0 - this.prevHash = undefined - this.merkleRoot = undefined - this.witnessCommit = undefined - this.transactions = undefined + constructor() { + this.version = 1; + this.timestamp = 0; + this.bits = 0; + this.nonce = 0; + this.prevHash = undefined; + this.merkleRoot = undefined; + this.witnessCommit = undefined; + this.transactions = undefined; } - static fromBuffer (buffer: Buffer): Block { - if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)') + static fromBuffer(buffer: Buffer): Block { + if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); - let offset: number = 0 + let offset: number = 0; const readSlice = (n: number): Buffer => { - offset += n - return buffer.slice(offset - n, offset) - } + offset += n; + return buffer.slice(offset - n, offset); + }; const readUInt32 = (): number => { - const i = buffer.readUInt32LE(offset) - offset += 4 - return i - } + const i = buffer.readUInt32LE(offset); + offset += 4; + return i; + }; const readInt32 = (): number => { - const i = buffer.readInt32LE(offset) - offset += 4 - return i - } + const i = buffer.readInt32LE(offset); + offset += 4; + return i; + }; - const block = new Block() - block.version = readInt32() - block.prevHash = readSlice(32) - block.merkleRoot = readSlice(32) - block.timestamp = readUInt32() - block.bits = readUInt32() - block.nonce = readUInt32() + const block = new Block(); + block.version = readInt32(); + block.prevHash = readSlice(32); + block.merkleRoot = readSlice(32); + block.timestamp = readUInt32(); + block.bits = readUInt32(); + block.nonce = readUInt32(); - if (buffer.length === 80) return block + if (buffer.length === 80) return block; const readVarInt = (): number => { - const vi = varuint.decode(buffer, offset) - offset += varuint.decode.bytes - return vi - } + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + }; const readTransaction = (): any => { - const tx = Transaction.fromBuffer(buffer.slice(offset), true) - offset += tx.byteLength() - return tx - } + const tx = Transaction.fromBuffer(buffer.slice(offset), true); + offset += tx.byteLength(); + return tx; + }; - const nTransactions = readVarInt() - block.transactions = [] + const nTransactions = readVarInt(); + block.transactions = []; for (var i = 0; i < nTransactions; ++i) { - const tx = readTransaction() - block.transactions.push(tx) + const tx = readTransaction(); + block.transactions.push(tx); } - let witnessCommit = block.getWitnessCommit() + let witnessCommit = block.getWitnessCommit(); // This Block contains a witness commit - if (witnessCommit) block.witnessCommit = witnessCommit + if (witnessCommit) block.witnessCommit = witnessCommit; - return block + return block; } - static fromHex (hex: string): Block { - return Block.fromBuffer(Buffer.from(hex, 'hex')) + static fromHex(hex: string): Block { + return Block.fromBuffer(Buffer.from(hex, 'hex')); } - static calculateTarget (bits: number): Buffer { - const exponent = ((bits & 0xff000000) >> 24) - 3 - const mantissa = bits & 0x007fffff - const target = Buffer.alloc(32, 0) - target.writeUIntBE(mantissa, 29 - exponent, 3) - return target + static calculateTarget(bits: number): Buffer { + const exponent = ((bits & 0xff000000) >> 24) - 3; + const mantissa = bits & 0x007fffff; + const target = Buffer.alloc(32, 0); + target.writeUIntBE(mantissa, 29 - exponent, 3); + return target; } - static calculateMerkleRoot (transactions: Array<Transaction>, forWitness?: boolean): Buffer { - typeforce([{ getHash: types.Function }], transactions) - if (transactions.length === 0) throw errorMerkleNoTxes - if (forWitness && !txesHaveWitnessCommit(transactions)) throw errorWitnessNotSegwit + static calculateMerkleRoot( + transactions: Array<Transaction>, + forWitness?: boolean, + ): Buffer { + typeforce([{ getHash: types.Function }], transactions); + if (transactions.length === 0) throw errorMerkleNoTxes; + if (forWitness && !txesHaveWitnessCommit(transactions)) + throw errorWitnessNotSegwit; - const hashes = transactions.map(transaction => transaction.getHash(forWitness!)) + const hashes = transactions.map(transaction => + transaction.getHash(forWitness!), + ); - const rootHash = fastMerkleRoot(hashes, bcrypto.hash256) + const rootHash = fastMerkleRoot(hashes, bcrypto.hash256); return forWitness - ? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) - : rootHash + ? bcrypto.hash256( + Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]), + ) + : rootHash; } - getWitnessCommit (): Buffer | null { - if (!txesHaveWitnessCommit(this.transactions!)) return null + getWitnessCommit(): Buffer | null { + if (!txesHaveWitnessCommit(this.transactions!)) return null; // The merkle root for the witness data is in an OP_RETURN output. // There is no rule for the index of the output, so use filter to find it. // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed // If multiple commits are found, the output with highest index is assumed. - let witnessCommits = this.transactions![0].outs - .filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))) - .map(out => out.script.slice(6, 38)) - if (witnessCommits.length === 0) return null + let witnessCommits = this.transactions![0].outs.filter(out => + out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')), + ).map(out => out.script.slice(6, 38)); + if (witnessCommits.length === 0) return null; // Use the commit with the highest output (should only be one though) - let result = witnessCommits[witnessCommits.length - 1] + let result = witnessCommits[witnessCommits.length - 1]; - if (!(result instanceof Buffer && result.length === 32)) return null - return result + if (!(result instanceof Buffer && result.length === 32)) return null; + return result; } - hasWitnessCommit (): boolean { - if (this.witnessCommit instanceof Buffer && - this.witnessCommit.length === 32) return true - if (this.getWitnessCommit() !== null) return true - return false + hasWitnessCommit(): boolean { + if ( + this.witnessCommit instanceof Buffer && + this.witnessCommit.length === 32 + ) + return true; + if (this.getWitnessCommit() !== null) return true; + return false; } - hasWitness (): boolean { - return anyTxHasWitness(this.transactions!) + hasWitness(): boolean { + return anyTxHasWitness(this.transactions!); } - byteLength (headersOnly: boolean): number { - if (headersOnly || !this.transactions) return 80 + byteLength(headersOnly: boolean): number { + if (headersOnly || !this.transactions) return 80; - return 80 + varuint.encodingLength(this.transactions.length) + + return ( + 80 + + varuint.encodingLength(this.transactions.length) + this.transactions.reduce((a, x) => a + x.byteLength(), 0) + ); } - getHash (): Buffer { - return bcrypto.hash256(this.toBuffer(true)) + getHash(): Buffer { + return bcrypto.hash256(this.toBuffer(true)); } - getId (): string { - return reverseBuffer(this.getHash()).toString('hex') + getId(): string { + return reverseBuffer(this.getHash()).toString('hex'); } - getUTCDate (): Date { - const date = new Date(0) // epoch - date.setUTCSeconds(this.timestamp) + getUTCDate(): Date { + const date = new Date(0); // epoch + date.setUTCSeconds(this.timestamp); - return date + return date; } // TODO: buffer, offset compatibility - toBuffer (headersOnly: boolean): Buffer { - const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)) + toBuffer(headersOnly: boolean): Buffer { + const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); - let offset: number = 0 + let offset: number = 0; const writeSlice = (slice: Buffer): void => { - slice.copy(buffer, offset) - offset += slice.length - } + slice.copy(buffer, offset); + offset += slice.length; + }; const writeInt32 = (i: number): void => { - buffer.writeInt32LE(i, offset) - offset += 4 - } + buffer.writeInt32LE(i, offset); + offset += 4; + }; const writeUInt32 = (i: number): void => { - buffer.writeUInt32LE(i, offset) - offset += 4 - } + buffer.writeUInt32LE(i, offset); + offset += 4; + }; - writeInt32(this.version) - writeSlice(this.prevHash!) - writeSlice(this.merkleRoot!) - writeUInt32(this.timestamp) - writeUInt32(this.bits) - writeUInt32(this.nonce) + writeInt32(this.version); + writeSlice(this.prevHash!); + writeSlice(this.merkleRoot!); + writeUInt32(this.timestamp); + writeUInt32(this.bits); + writeUInt32(this.nonce); - if (headersOnly || !this.transactions) return buffer + if (headersOnly || !this.transactions) return buffer; - varuint.encode(this.transactions.length, buffer, offset) - offset += varuint.encode.bytes + varuint.encode(this.transactions.length, buffer, offset); + offset += varuint.encode.bytes; this.transactions.forEach(tx => { - const txSize = tx.byteLength() // TODO: extract from toBuffer? - tx.toBuffer(buffer, offset) - offset += txSize - }) + const txSize = tx.byteLength(); // TODO: extract from toBuffer? + tx.toBuffer(buffer, offset); + offset += txSize; + }); - return buffer + return buffer; } - toHex (headersOnly: boolean): string { - return this.toBuffer(headersOnly).toString('hex') + toHex(headersOnly: boolean): string { + return this.toBuffer(headersOnly).toString('hex'); } - checkTxRoots (): boolean { + checkTxRoots(): boolean { // If the Block has segwit transactions but no witness commit, // there's no way it can be valid, so fail the check. - let hasWitnessCommit = this.hasWitnessCommit() - if (!hasWitnessCommit && this.hasWitness()) return false - return this.__checkMerkleRoot() && + let hasWitnessCommit = this.hasWitnessCommit(); + if (!hasWitnessCommit && this.hasWitness()) return false; + return ( + this.__checkMerkleRoot() && (hasWitnessCommit ? this.__checkWitnessCommit() : true) + ); } - checkMerkleRoot (): boolean { - console.warn('Deprecation Warning: Block method checkMerkleRoot will be ' + - 'deprecated in v5. Please use checkTxRoots instead.') - return this.checkTxRoots() + checkMerkleRoot(): boolean { + console.warn( + 'Deprecation Warning: Block method checkMerkleRoot will be ' + + 'deprecated in v5. Please use checkTxRoots instead.', + ); + return this.checkTxRoots(); } - __checkMerkleRoot (): boolean { - if (!this.transactions) throw errorMerkleNoTxes + __checkMerkleRoot(): boolean { + if (!this.transactions) throw errorMerkleNoTxes; - const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions) - return this.merkleRoot!.compare(actualMerkleRoot) === 0 + const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions); + return this.merkleRoot!.compare(actualMerkleRoot) === 0; } - __checkWitnessCommit (): boolean { - if (!this.transactions) throw errorMerkleNoTxes - if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit + __checkWitnessCommit(): boolean { + if (!this.transactions) throw errorMerkleNoTxes; + if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit; - const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true) - return this.witnessCommit!.compare(actualWitnessCommit) === 0 + const actualWitnessCommit = Block.calculateMerkleRoot( + this.transactions, + true, + ); + return this.witnessCommit!.compare(actualWitnessCommit) === 0; } - checkProofOfWork (): boolean { - const hash: Buffer = reverseBuffer(this.getHash()) - const target = Block.calculateTarget(this.bits) + checkProofOfWork(): boolean { + const hash: Buffer = reverseBuffer(this.getHash()); + const target = Block.calculateTarget(this.bits); - return hash.compare(target) <= 0 + return hash.compare(target) <= 0; } } diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 3ef7492..adb6060 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -1,37 +1,44 @@ // https://github.com/feross/buffer/blob/master/index.js#L1127 -function verifuint (value: number, max: number): void { - if (typeof value !== 'number') throw new Error('cannot write a non-number as a number') - if (value < 0) throw new Error('specified a negative value for writing an unsigned value') - if (value > max) throw new Error('RangeError: value out of range') - if (Math.floor(value) !== value) throw new Error('value has a fractional component') +function verifuint(value: number, max: number): void { + if (typeof value !== 'number') + throw new Error('cannot write a non-number as a number'); + if (value < 0) + throw new Error('specified a negative value for writing an unsigned value'); + if (value > max) throw new Error('RangeError: value out of range'); + if (Math.floor(value) !== value) + throw new Error('value has a fractional component'); } -export function readUInt64LE (buffer: Buffer, offset: number): number { - const a = buffer.readUInt32LE(offset) - let b = buffer.readUInt32LE(offset + 4) - b *= 0x100000000 +export function readUInt64LE(buffer: Buffer, offset: number): number { + const a = buffer.readUInt32LE(offset); + let b = buffer.readUInt32LE(offset + 4); + b *= 0x100000000; - verifuint(b + a, 0x001fffffffffffff) - return b + a + verifuint(b + a, 0x001fffffffffffff); + return b + a; } -export function writeUInt64LE (buffer: Buffer, value: number, offset: number): number { - verifuint(value, 0x001fffffffffffff) +export function writeUInt64LE( + buffer: Buffer, + value: number, + offset: number, +): number { + verifuint(value, 0x001fffffffffffff); - buffer.writeInt32LE(value & -1, offset) - buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4) - return offset + 8 + buffer.writeInt32LE(value & -1, offset); + buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); + return offset + 8; } -export function reverseBuffer (buffer: Buffer): Buffer { - if (buffer.length < 1) return buffer - let j = buffer.length - 1 - let tmp = 0 +export function reverseBuffer(buffer: Buffer): Buffer { + if (buffer.length < 1) return buffer; + let j = buffer.length - 1; + let tmp = 0; for (let i = 0; i < buffer.length / 2; i++) { - tmp = buffer[i] - buffer[i] = buffer[j] - buffer[j] = tmp - j-- + tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + j--; } - return buffer + return buffer; } diff --git a/ts_src/classify.ts b/ts_src/classify.ts index 9a5c849..0de93f9 100644 --- a/ts_src/classify.ts +++ b/ts_src/classify.ts @@ -1,65 +1,69 @@ -import { decompile } from './script' -import * as multisig from './templates/multisig' -import * as nullData from './templates/nulldata' -import * as pubKey from './templates/pubkey' -import * as pubKeyHash from './templates/pubkeyhash' -import * as scriptHash from './templates/scripthash' -import * as witnessPubKeyHash from './templates/witnesspubkeyhash' -import * as witnessScriptHash from './templates/witnessscripthash' -import * as witnessCommitment from './templates/witnesscommitment' +import { decompile } from './script'; +import * as multisig from './templates/multisig'; +import * as nullData from './templates/nulldata'; +import * as pubKey from './templates/pubkey'; +import * as pubKeyHash from './templates/pubkeyhash'; +import * as scriptHash from './templates/scripthash'; +import * as witnessPubKeyHash from './templates/witnesspubkeyhash'; +import * as witnessScriptHash from './templates/witnessscripthash'; +import * as witnessCommitment from './templates/witnesscommitment'; const types = { - P2MS: <string> 'multisig', - NONSTANDARD: <string> 'nonstandard', - NULLDATA: <string> 'nulldata', - P2PK: <string> 'pubkey', - P2PKH: <string> 'pubkeyhash', - P2SH: <string> 'scripthash', - P2WPKH: <string> 'witnesspubkeyhash', - P2WSH: <string> 'witnessscripthash', - WITNESS_COMMITMENT: <string> 'witnesscommitment' -} + P2MS: <string>'multisig', + NONSTANDARD: <string>'nonstandard', + NULLDATA: <string>'nulldata', + P2PK: <string>'pubkey', + P2PKH: <string>'pubkeyhash', + P2SH: <string>'scripthash', + P2WPKH: <string>'witnesspubkeyhash', + P2WSH: <string>'witnessscripthash', + WITNESS_COMMITMENT: <string>'witnesscommitment', +}; -function classifyOutput (script: Buffer): string { - if (witnessPubKeyHash.output.check(script)) return types.P2WPKH - if (witnessScriptHash.output.check(script)) return types.P2WSH - if (pubKeyHash.output.check(script)) return types.P2PKH - if (scriptHash.output.check(script)) return types.P2SH +function classifyOutput(script: Buffer): string { + if (witnessPubKeyHash.output.check(script)) return types.P2WPKH; + if (witnessScriptHash.output.check(script)) return types.P2WSH; + if (pubKeyHash.output.check(script)) return types.P2PKH; + if (scriptHash.output.check(script)) return types.P2SH; // XXX: optimization, below functions .decompile before use - const chunks = decompile(script) - if (!chunks) throw new TypeError('Invalid script') + const chunks = decompile(script); + if (!chunks) throw new TypeError('Invalid script'); - if (multisig.output.check(chunks)) return types.P2MS - if (pubKey.output.check(chunks)) return types.P2PK - if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT - if (nullData.output.check(chunks)) return types.NULLDATA + if (multisig.output.check(chunks)) return types.P2MS; + if (pubKey.output.check(chunks)) return types.P2PK; + if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT; + if (nullData.output.check(chunks)) return types.NULLDATA; - return types.NONSTANDARD + return types.NONSTANDARD; } -function classifyInput (script: Buffer, allowIncomplete: boolean): string { +function classifyInput(script: Buffer, allowIncomplete: boolean): string { // XXX: optimization, below functions .decompile before use - const chunks = decompile(script) - if (!chunks) throw new TypeError('Invalid script') + const chunks = decompile(script); + if (!chunks) throw new TypeError('Invalid script'); - if (pubKeyHash.input.check(chunks)) return types.P2PKH - if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH - if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS - if (pubKey.input.check(chunks)) return types.P2PK + if (pubKeyHash.input.check(chunks)) return types.P2PKH; + if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH; + if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS; + if (pubKey.input.check(chunks)) return types.P2PK; - return types.NONSTANDARD + return types.NONSTANDARD; } -function classifyWitness (script: Array<Buffer>, allowIncomplete: boolean): string { +function classifyWitness( + script: Array<Buffer>, + allowIncomplete: boolean, +): string { // XXX: optimization, below functions .decompile before use - const chunks = decompile(script) - if (!chunks) throw new TypeError('Invalid script') + const chunks = decompile(script); + if (!chunks) throw new TypeError('Invalid script'); - if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH - if (witnessScriptHash.input.check(<Array<Buffer>>chunks, allowIncomplete)) return types.P2WSH + if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH; + if (witnessScriptHash.input.check(<Array<Buffer>>chunks, allowIncomplete)) + return types.P2WSH; - return types.NONSTANDARD + return types.NONSTANDARD; } export { @@ -67,4 +71,4 @@ export { classifyOutput as output, classifyWitness as witness, types, -} +}; diff --git a/ts_src/crypto.ts b/ts_src/crypto.ts index 6aabb5b..6d92e60 100644 --- a/ts_src/crypto.ts +++ b/ts_src/crypto.ts @@ -1,21 +1,27 @@ -const createHash = require('create-hash') +const createHash = require('create-hash'); -export function ripemd160 (buffer: Buffer): Buffer { - return createHash('rmd160').update(buffer).digest() +export function ripemd160(buffer: Buffer): Buffer { + return createHash('rmd160') + .update(buffer) + .digest(); } -export function sha1 (buffer: Buffer): Buffer { - return createHash('sha1').update(buffer).digest() +export function sha1(buffer: Buffer): Buffer { + return createHash('sha1') + .update(buffer) + .digest(); } -export function sha256 (buffer: Buffer): Buffer { - return createHash('sha256').update(buffer).digest() +export function sha256(buffer: Buffer): Buffer { + return createHash('sha256') + .update(buffer) + .digest(); } -export function hash160 (buffer: Buffer): Buffer { - return ripemd160(sha256(buffer)) +export function hash160(buffer: Buffer): Buffer { + return ripemd160(sha256(buffer)); } -export function hash256 (buffer: Buffer): Buffer { - return sha256(sha256(buffer)) +export function hash256(buffer: Buffer): Buffer { + return sha256(sha256(buffer)); } diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index ad28adf..15ec00a 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -1,130 +1,132 @@ -import { Network } from './networks' -import * as NETWORKS from './networks' -import * as types from './types' -const ecc = require('tiny-secp256k1') -const randomBytes = require('randombytes') -const typeforce = require('typeforce') -const wif = require('wif') +import { Network } from './networks'; +import * as NETWORKS from './networks'; +import * as types from './types'; +const ecc = require('tiny-secp256k1'); +const randomBytes = require('randombytes'); +const typeforce = require('typeforce'); +const wif = require('wif'); -const isOptions = typeforce.maybe(typeforce.compile({ - compressed: types.maybe(types.Boolean), - network: types.maybe(types.Network) -})) +const isOptions = typeforce.maybe( + typeforce.compile({ + compressed: types.maybe(types.Boolean), + network: types.maybe(types.Network), + }), +); interface ECPairOptions { - compressed?: boolean - network?: Network - rng?(arg0: Buffer): Buffer + compressed?: boolean; + network?: Network; + rng?(arg0: Buffer): Buffer; } export interface ECPairInterface { - compressed: boolean - network: Network - privateKey?: Buffer - publicKey?: Buffer - toWIF(): string - sign(hash: Buffer): Buffer - verify(hash: Buffer, signature: Buffer): Buffer - getPublicKey?(): Buffer + compressed: boolean; + network: Network; + privateKey?: Buffer; + publicKey?: Buffer; + toWIF(): string; + sign(hash: Buffer): Buffer; + verify(hash: Buffer, signature: Buffer): Buffer; + getPublicKey?(): Buffer; } class ECPair implements ECPairInterface { - compressed: boolean - network: Network - private __d?: Buffer - private __Q?: Buffer + compressed: boolean; + network: Network; + private __d?: Buffer; + private __Q?: Buffer; - constructor (d?: Buffer, Q?: Buffer, options?: ECPairOptions) { - if (options === undefined) options = {} - this.compressed = options.compressed === undefined ? true : options.compressed - this.network = options.network || NETWORKS.bitcoin + constructor(d?: Buffer, Q?: Buffer, options?: ECPairOptions) { + if (options === undefined) options = {}; + this.compressed = + options.compressed === undefined ? true : options.compressed; + this.network = options.network || NETWORKS.bitcoin; - this.__d = undefined - this.__Q = undefined - if (d !== undefined) this.__d = d - if (Q !== undefined) this.__Q = ecc.pointCompress(Q, this.compressed) + this.__d = undefined; + this.__Q = undefined; + if (d !== undefined) this.__d = d; + if (Q !== undefined) this.__Q = ecc.pointCompress(Q, this.compressed); } - get privateKey (): Buffer | undefined { - return this.__d + get privateKey(): Buffer | undefined { + return this.__d; } - get publicKey (): Buffer | undefined { - if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed) - return this.__Q + get publicKey(): Buffer | undefined { + if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed); + return this.__Q; } - toWIF (): string { - if (!this.__d) throw new Error('Missing private key') - return wif.encode(this.network.wif, this.__d, this.compressed) + toWIF(): string { + if (!this.__d) throw new Error('Missing private key'); + return wif.encode(this.network.wif, this.__d, this.compressed); } - sign (hash: Buffer): Buffer { - if (!this.__d) throw new Error('Missing private key') - return ecc.sign(hash, this.__d) + sign(hash: Buffer): Buffer { + if (!this.__d) throw new Error('Missing private key'); + return ecc.sign(hash, this.__d); } - verify (hash: Buffer, signature: Buffer): Buffer { - return ecc.verify(hash, this.publicKey, signature) + verify(hash: Buffer, signature: Buffer): Buffer { + return ecc.verify(hash, this.publicKey, signature); } } -function fromPrivateKey (buffer: Buffer, options?: ECPairOptions): ECPair { - typeforce(types.Buffer256bit, buffer) - if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)') - typeforce(isOptions, options) +function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair { + typeforce(types.Buffer256bit, buffer); + if (!ecc.isPrivate(buffer)) + throw new TypeError('Private key not in range [1, n)'); + typeforce(isOptions, options); - return new ECPair(buffer, undefined, <ECPairOptions>options) + return new ECPair(buffer, undefined, <ECPairOptions>options); } -function fromPublicKey (buffer: Buffer, options?: ECPairOptions): ECPair { - typeforce(ecc.isPoint, buffer) - typeforce(isOptions, options) - return new ECPair(undefined, buffer, <ECPairOptions>options) +function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair { + typeforce(ecc.isPoint, buffer); + typeforce(isOptions, options); + return new ECPair(undefined, buffer, <ECPairOptions>options); } -function fromWIF (string: string, network?: Network | Array<Network>): ECPair { - const decoded = wif.decode(string) - const version = decoded.version +function fromWIF(string: string, network?: Network | Array<Network>): ECPair { + const decoded = wif.decode(string); + const version = decoded.version; // list of networks? if (types.Array(network)) { - network = <Network>(<Array<Network>>network).filter(function (x: Network) { - return version === x.wif - }).pop() + network = <Network>(<Array<Network>>network) + .filter(function(x: Network) { + return version === x.wif; + }) + .pop(); - if (!network) throw new Error('Unknown network version') + if (!network) throw new Error('Unknown network version'); - // otherwise, assume a network object (or default to bitcoin) + // otherwise, assume a network object (or default to bitcoin) } else { - network = network || NETWORKS.bitcoin + network = network || NETWORKS.bitcoin; - if (version !== (<Network>network).wif) throw new Error('Invalid network version') + if (version !== (<Network>network).wif) + throw new Error('Invalid network version'); } return fromPrivateKey(decoded.privateKey, { compressed: decoded.compressed, - network: <Network>network - }) + network: <Network>network, + }); } -function makeRandom (options?: ECPairOptions): ECPair { - typeforce(isOptions, options) - if (options === undefined) options = {} - const rng = options.rng || randomBytes +function makeRandom(options?: ECPairOptions): ECPair { + typeforce(isOptions, options); + if (options === undefined) options = {}; + const rng = options.rng || randomBytes; - let d + let d; do { - d = rng(32) - typeforce(types.Buffer256bit, d) - } while (!ecc.isPrivate(d)) + d = rng(32); + typeforce(types.Buffer256bit, d); + } while (!ecc.isPrivate(d)); - return fromPrivateKey(d, options) + return fromPrivateKey(d, options); } -export { - makeRandom, - fromPrivateKey, - fromPublicKey, - fromWIF -} +export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF }; diff --git a/ts_src/index.ts b/ts_src/index.ts index 7574048..fce7304 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -1,28 +1,20 @@ -import * as bip32 from 'bip32' -import * as ECPair from './ecpair' -import * as address from './address' -import * as crypto from './crypto' -import * as networks from './networks' -import * as payments from './payments' -import * as script from './script' +import * as bip32 from 'bip32'; +import * as ECPair from './ecpair'; +import * as address from './address'; +import * as crypto from './crypto'; +import * as networks from './networks'; +import * as payments from './payments'; +import * as script from './script'; -export { - ECPair, - address, - bip32, - crypto, - networks, - payments, - script, -} +export { ECPair, address, bip32, crypto, networks, payments, script }; -export { Block } from './block' -export { Transaction } from './transaction' -export { TransactionBuilder } from './transaction_builder' -export { OPS as opcodes } from './script' +export { Block } from './block'; +export { Transaction } from './transaction'; +export { TransactionBuilder } from './transaction_builder'; +export { OPS as opcodes } from './script'; -export { Payment, PaymentOpts } from './payments' -export { Input as TxInput, Output as TxOutput } from './transaction' -export { Network } from './networks' -export { OpCode } from './script' -export { BIP32Interface } from 'bip32' +export { Payment, PaymentOpts } from './payments'; +export { Input as TxInput, Output as TxOutput } from './transaction'; +export { Network } from './networks'; +export { OpCode } from './script'; +export { BIP32Interface } from 'bip32'; diff --git a/ts_src/networks.ts b/ts_src/networks.ts index 11147af..0212a44 100644 --- a/ts_src/networks.ts +++ b/ts_src/networks.ts @@ -7,43 +7,43 @@ export type Network = { pubKeyHash: number; scriptHash: number; wif: number; -} +}; type bip32 = { public: number; private: number; -} +}; export const bitcoin: Network = { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'bc', bip32: { public: 0x0488b21e, - private: 0x0488ade4 + private: 0x0488ade4, }, pubKeyHash: 0x00, scriptHash: 0x05, - wif: 0x80 -} + wif: 0x80, +}; export const regtest: Network = { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'bcrt', bip32: { public: 0x043587cf, - private: 0x04358394 + private: 0x04358394, }, pubKeyHash: 0x6f, scriptHash: 0xc4, - wif: 0xef -} + wif: 0xef, +}; export const testnet: Network = { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'tb', bip32: { public: 0x043587cf, - private: 0x04358394 + private: 0x04358394, }, pubKeyHash: 0x6f, scriptHash: 0xc4, - wif: 0xef -} + wif: 0xef, +}; diff --git a/ts_src/payments/embed.ts b/ts_src/payments/embed.ts index bfaeb8b..f7225a7 100644 --- a/ts_src/payments/embed.ts +++ b/ts_src/payments/embed.ts @@ -1,54 +1,59 @@ -import { Payment, PaymentOpts } from './index' // eslint-disable-line -import * as bscript from '../script' -import * as lazy from './lazy' -import { bitcoin as BITCOIN_NETWORK } from '../networks' -const typef = require('typeforce') -const OPS = bscript.OPS +import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import * as bscript from '../script'; +import * as lazy from './lazy'; +import { bitcoin as BITCOIN_NETWORK } from '../networks'; +const typef = require('typeforce'); +const OPS = bscript.OPS; -function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { - if (a.length !== b.length) return false +function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean { + if (a.length !== b.length) return false; - return a.every(function (x, i) { - return x.equals(b[i]) - }) + return a.every(function(x, i) { + return x.equals(b[i]); + }); } // output: OP_RETURN ... -export function p2data (a: Payment, opts?: PaymentOpts): Payment { - if ( - !a.data && - !a.output - ) throw new TypeError('Not enough data') - opts = Object.assign({ validate: true }, opts || {}) +export function p2data(a: Payment, opts?: PaymentOpts): Payment { + if (!a.data && !a.output) throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); - typef({ - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - data: typef.maybe(typef.arrayOf(typef.Buffer)) - }, a) + typef( + { + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + data: typef.maybe(typef.arrayOf(typef.Buffer)), + }, + a, + ); - const network = a.network || BITCOIN_NETWORK - const o = <Payment>{ network } + const network = a.network || BITCOIN_NETWORK; + const o = <Payment>{ network }; - lazy.prop(o, 'output', function () { - if (!a.data) return - return bscript.compile((<Array<Buffer | number>>[OPS.OP_RETURN]).concat(a.data)) - }) - lazy.prop(o, 'data', function () { - if (!a.output) return - return bscript.decompile(a.output)!.slice(1) - }) + lazy.prop(o, 'output', function() { + if (!a.data) return; + return bscript.compile( + (<Array<Buffer | number>>[OPS.OP_RETURN]).concat(a.data), + ); + }); + lazy.prop(o, 'data', function() { + if (!a.output) return; + return bscript.decompile(a.output)!.slice(1); + }); // extended validation if (opts.validate) { if (a.output) { - const chunks = bscript.decompile(a.output) - if (chunks![0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid') - if (!chunks!.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid') + const chunks = bscript.decompile(a.output); + if (chunks![0] !== OPS.OP_RETURN) + throw new TypeError('Output is invalid'); + if (!chunks!.slice(1).every(typef.Buffer)) + throw new TypeError('Output is invalid'); - if (a.data && !stacksEqual(a.data, <Array<Buffer>>o.data)) throw new TypeError('Data mismatch') + if (a.data && !stacksEqual(a.data, <Array<Buffer>>o.data)) + throw new TypeError('Data mismatch'); } } - return Object.assign(o, a) + return Object.assign(o, a); } diff --git a/ts_src/payments/index.ts b/ts_src/payments/index.ts index 7fc72d8..9791661 100644 --- a/ts_src/payments/index.ts +++ b/ts_src/payments/index.ts @@ -1,35 +1,35 @@ -import { Network } from '../networks' // eslint-disable-line -import { p2data as embed } from './embed' -import { p2ms } from './p2ms' -import { p2pk } from './p2pk' -import { p2pkh } from './p2pkh' -import { p2sh } from './p2sh' -import { p2wpkh } from './p2wpkh' -import { p2wsh } from './p2wsh' +import { Network } from '../networks'; // eslint-disable-line +import { p2data as embed } from './embed'; +import { p2ms } from './p2ms'; +import { p2pk } from './p2pk'; +import { p2pkh } from './p2pkh'; +import { p2sh } from './p2sh'; +import { p2wpkh } from './p2wpkh'; +import { p2wsh } from './p2wsh'; export interface Payment { - network?: Network, - output?: Buffer, - data?: Array<Buffer>, - m?: number, - n?: number, - pubkeys?: Array<Buffer>, - input?: Buffer, - signatures?: Array<Buffer>, - pubkey?: Buffer, - signature?: Buffer, - address?: string, - hash?: Buffer, - redeem?: Payment, - witness?: Array<Buffer>, + network?: Network; + output?: Buffer; + data?: Array<Buffer>; + m?: number; + n?: number; + pubkeys?: Array<Buffer>; + input?: Buffer; + signatures?: Array<Buffer>; + pubkey?: Buffer; + signature?: Buffer; + address?: string; + hash?: Buffer; + redeem?: Payment; + witness?: Array<Buffer>; } export interface PaymentOpts { - validate?: boolean, - allowIncomplete?: boolean, + validate?: boolean; + allowIncomplete?: boolean; } -export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh } +export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }; // TODO // witness commitment diff --git a/ts_src/payments/lazy.ts b/ts_src/payments/lazy.ts index a6f0951..474c8e9 100644 --- a/ts_src/payments/lazy.ts +++ b/ts_src/payments/lazy.ts @@ -1,28 +1,28 @@ -export function prop (object: Object, name: string, f: ()=>any): void { +export function prop(object: Object, name: string, f: () => any): void { Object.defineProperty(object, name, { configurable: true, enumerable: true, - get: function () { - let value = f.call(this) - this[name] = value - return value + get: function() { + let value = f.call(this); + this[name] = value; + return value; }, - set: function (value) { + set: function(value) { Object.defineProperty(this, name, { configurable: true, enumerable: true, value: value, - writable: true - }) - } - }) + writable: true, + }); + }, + }); } -export function value <T> (f: ()=>T): ()=>T { - let value: T - return function (): T { - if (value !== undefined) return value - value = f() - return value - } +export function value<T>(f: () => T): () => T { + let value: T; + return function(): T { + if (value !== undefined) return value; + value = f(); + return value; + }; } diff --git a/ts_src/payments/p2ms.ts b/ts_src/payments/p2ms.ts index 34e2d31..fce7616 100644 --- a/ts_src/payments/p2ms.ts +++ b/ts_src/payments/p2ms.ts @@ -1,141 +1,165 @@ -import { Payment, PaymentOpts } from './index' // eslint-disable-line -import * as bscript from '../script' -import * as lazy from './lazy' -import { bitcoin as BITCOIN_NETWORK } from '../networks' -const OPS = bscript.OPS -const typef = require('typeforce') -const ecc = require('tiny-secp256k1') +import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import * as bscript from '../script'; +import * as lazy from './lazy'; +import { bitcoin as BITCOIN_NETWORK } from '../networks'; +const OPS = bscript.OPS; +const typef = require('typeforce'); +const ecc = require('tiny-secp256k1'); -const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 +const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 -function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { - if (a.length !== b.length) return false +function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean { + if (a.length !== b.length) return false; - return a.every(function (x, i) { - return x.equals(b[i]) - }) + return a.every(function(x, i) { + return x.equals(b[i]); + }); } // input: OP_0 [signatures ...] // output: m [pubKeys ...] n OP_CHECKMULTISIG -export function p2ms (a: Payment, opts?: PaymentOpts): Payment { +export function p2ms(a: Payment, opts?: PaymentOpts): Payment { if ( !a.input && !a.output && !(a.pubkeys && a.m !== undefined) && !a.signatures - ) throw new TypeError('Not enough data') - opts = Object.assign({ validate: true }, opts || {}) + ) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); - function isAcceptableSignature (x: Buffer | number) { - return bscript.isCanonicalScriptSignature(<Buffer>x) || - (opts!.allowIncomplete && - (<number> x === OPS.OP_0)) !== undefined // eslint-disable-line + function isAcceptableSignature(x: Buffer | number) { + return ( + bscript.isCanonicalScriptSignature(<Buffer>x) || + (opts!.allowIncomplete && <number>x === OPS.OP_0) !== undefined + ); // eslint-disable-line } - typef({ - network: typef.maybe(typef.Object), - m: typef.maybe(typef.Number), - n: typef.maybe(typef.Number), - output: typef.maybe(typef.Buffer), - pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), + typef( + { + network: typef.maybe(typef.Object), + m: typef.maybe(typef.Number), + n: typef.maybe(typef.Number), + output: typef.maybe(typef.Buffer), + pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), - signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), - input: typef.maybe(typef.Buffer) - }, a) + signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), + input: typef.maybe(typef.Buffer), + }, + a, + ); - const network = a.network || BITCOIN_NETWORK - const o: Payment = { network } + const network = a.network || BITCOIN_NETWORK; + const o: Payment = { network }; - let chunks: Array<Buffer | number> = [] - let decoded = false - function decode (output: Buffer | Array<Buffer | number>): void { - if (decoded) return - decoded = true - chunks = <Array<Buffer | number>>bscript.decompile(output) - o.m = <number> chunks[0] - OP_INT_BASE // eslint-disable-line - o.n = <number> chunks[chunks.length - 2] - OP_INT_BASE // eslint-disable-line - o.pubkeys = <Array<Buffer>>chunks.slice(1, -2) + let chunks: Array<Buffer | number> = []; + let decoded = false; + function decode(output: Buffer | Array<Buffer | number>): void { + if (decoded) return; + decoded = true; + chunks = <Array<Buffer | number>>bscript.decompile(output); + o.m = <number>chunks[0] - OP_INT_BASE; // eslint-disable-line + o.n = <number>chunks[chunks.length - 2] - OP_INT_BASE; // eslint-disable-line + o.pubkeys = <Array<Buffer>>chunks.slice(1, -2); } - lazy.prop(o, 'output', function () { - if (!a.m) return - if (!o.n) return - if (!a.pubkeys) return - return bscript.compile((<Array<Buffer | number>>[]).concat( - OP_INT_BASE + a.m, - a.pubkeys, - OP_INT_BASE + o.n, - OPS.OP_CHECKMULTISIG - )) - }) - lazy.prop(o, 'm', function () { - if (!o.output) return - decode(o.output) - return o.m - }) - lazy.prop(o, 'n', function () { - if (!o.pubkeys) return - return o.pubkeys.length - }) - lazy.prop(o, 'pubkeys', function () { - if (!a.output) return - decode(a.output) - return o.pubkeys - }) - lazy.prop(o, 'signatures', function () { - if (!a.input) return - return bscript.decompile(a.input)!.slice(1) - }) - lazy.prop(o, 'input', function () { - if (!a.signatures) return - return bscript.compile((<Array<Buffer | number>>[OPS.OP_0]).concat(a.signatures)) - }) - lazy.prop(o, 'witness', function () { - if (!o.input) return - return [] - }) + lazy.prop(o, 'output', function() { + if (!a.m) return; + if (!o.n) return; + if (!a.pubkeys) return; + return bscript.compile( + (<Array<Buffer | number>>[]).concat( + OP_INT_BASE + a.m, + a.pubkeys, + OP_INT_BASE + o.n, + OPS.OP_CHECKMULTISIG, + ), + ); + }); + lazy.prop(o, 'm', function() { + if (!o.output) return; + decode(o.output); + return o.m; + }); + lazy.prop(o, 'n', function() { + if (!o.pubkeys) return; + return o.pubkeys.length; + }); + lazy.prop(o, 'pubkeys', function() { + if (!a.output) return; + decode(a.output); + return o.pubkeys; + }); + lazy.prop(o, 'signatures', function() { + if (!a.input) return; + return bscript.decompile(a.input)!.slice(1); + }); + lazy.prop(o, 'input', function() { + if (!a.signatures) return; + return bscript.compile( + (<Array<Buffer | number>>[OPS.OP_0]).concat(a.signatures), + ); + }); + lazy.prop(o, 'witness', function() { + if (!o.input) return; + return []; + }); // extended validation if (opts.validate) { if (a.output) { - decode(a.output) - if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid') - if (!typef.Number(chunks[chunks.length - 2])) throw new TypeError('Output is invalid') - if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid') + decode(a.output); + if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid'); + if (!typef.Number(chunks[chunks.length - 2])) + throw new TypeError('Output is invalid'); + if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) + throw new TypeError('Output is invalid'); if ( o.m! <= 0 || // eslint-disable-line o.n! > 16 || // eslint-disable-line o.m! > o.n! || // eslint-disable-line - o.n !== chunks.length - 3) throw new TypeError('Output is invalid') - if (!o.pubkeys!.every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid') + o.n !== chunks.length - 3 + ) + throw new TypeError('Output is invalid'); + if (!o.pubkeys!.every(x => ecc.isPoint(x))) + throw new TypeError('Output is invalid'); - if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch') - if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch') - if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys!)) throw new TypeError('Pubkeys mismatch') + if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch'); + if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch'); + if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys!)) + throw new TypeError('Pubkeys mismatch'); } if (a.pubkeys) { - if (a.n !== undefined && a.n !== a.pubkeys.length) throw new TypeError('Pubkey count mismatch') - o.n = a.pubkeys.length + if (a.n !== undefined && a.n !== a.pubkeys.length) + throw new TypeError('Pubkey count mismatch'); + o.n = a.pubkeys.length; - if (o.n < o.m!) throw new TypeError('Pubkey count cannot be less than m') + if (o.n < o.m!) throw new TypeError('Pubkey count cannot be less than m'); } if (a.signatures) { - if (a.signatures.length < o.m!) throw new TypeError('Not enough signatures provided') - if (a.signatures.length > o.m!) throw new TypeError('Too many signatures provided') + if (a.signatures.length < o.m!) + throw new TypeError('Not enough signatures provided'); + if (a.signatures.length > o.m!) + throw new TypeError('Too many signatures provided'); } if (a.input) { - if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid') - if (o.signatures!.length === 0 || !o.signatures!.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)') + if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid'); + if ( + o.signatures!.length === 0 || + !o.signatures!.every(isAcceptableSignature) + ) + throw new TypeError('Input has invalid signature(s)'); - if (a.signatures && !stacksEqual(a.signatures, o.signatures!)) throw new TypeError('Signature mismatch') - if (a.m !== undefined && a.m !== a.signatures!.length) throw new TypeError('Signature count mismatch') + if (a.signatures && !stacksEqual(a.signatures, o.signatures!)) + throw new TypeError('Signature mismatch'); + if (a.m !== undefined && a.m !== a.signatures!.length) + throw new TypeError('Signature count mismatch'); } } - return Object.assign(o, a) + return Object.assign(o, a); } diff --git a/ts_src/payments/p2pk.ts b/ts_src/payments/p2pk.ts index 3385c56..e17a8f2 100644 --- a/ts_src/payments/p2pk.ts +++ b/ts_src/payments/p2pk.ts @@ -1,78 +1,80 @@ -import { Payment, PaymentOpts } from './index' // eslint-disable-line -import * as bscript from '../script' -import * as lazy from './lazy' -import { bitcoin as BITCOIN_NETWORK } from '../networks' -const typef = require('typeforce') -const OPS = bscript.OPS -const ecc = require('tiny-secp256k1') +import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import * as bscript from '../script'; +import * as lazy from './lazy'; +import { bitcoin as BITCOIN_NETWORK } from '../networks'; +const typef = require('typeforce'); +const OPS = bscript.OPS; +const ecc = require('tiny-secp256k1'); // input: {signature} // output: {pubKey} OP_CHECKSIG -export function p2pk (a: Payment, opts?: PaymentOpts): Payment { - if ( - !a.input && - !a.output && - !a.pubkey && - !a.input && - !a.signature - ) throw new TypeError('Not enough data') - opts = Object.assign({ validate: true }, opts || {}) +export function p2pk(a: Payment, opts?: PaymentOpts): Payment { + if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); - typef({ - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - pubkey: typef.maybe(ecc.isPoint), + typef( + { + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + pubkey: typef.maybe(ecc.isPoint), - signature: typef.maybe(bscript.isCanonicalScriptSignature), - input: typef.maybe(typef.Buffer) - }, a) + signature: typef.maybe(bscript.isCanonicalScriptSignature), + input: typef.maybe(typef.Buffer), + }, + a, + ); - const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.input!) }) + const _chunks = <() => Array<Buffer | number>>lazy.value(function() { + return bscript.decompile(a.input!); + }); - const network = a.network || BITCOIN_NETWORK - const o: Payment = { network } + const network = a.network || BITCOIN_NETWORK; + const o: Payment = { network }; - lazy.prop(o, 'output', function () { - if (!a.pubkey) return - return bscript.compile([ - a.pubkey, - OPS.OP_CHECKSIG - ]) - }) - lazy.prop(o, 'pubkey', function () { - if (!a.output) return - return a.output.slice(1, -1) - }) - lazy.prop(o, 'signature', function () { - if (!a.input) return - return <Buffer>_chunks()[0] - }) - lazy.prop(o, 'input', function () { - if (!a.signature) return - return bscript.compile([a.signature]) - }) - lazy.prop(o, 'witness', function () { - if (!o.input) return - return [] - }) + lazy.prop(o, 'output', function() { + if (!a.pubkey) return; + return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); + }); + lazy.prop(o, 'pubkey', function() { + if (!a.output) return; + return a.output.slice(1, -1); + }); + lazy.prop(o, 'signature', function() { + if (!a.input) return; + return <Buffer>_chunks()[0]; + }); + lazy.prop(o, 'input', function() { + if (!a.signature) return; + return bscript.compile([a.signature]); + }); + lazy.prop(o, 'witness', function() { + if (!o.input) return; + return []; + }); // extended validation if (opts.validate) { if (a.output) { - if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') - if (!ecc.isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid') - if (a.pubkey && !a.pubkey.equals(o.pubkey!)) throw new TypeError('Pubkey mismatch') + if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) + throw new TypeError('Output is invalid'); + if (!ecc.isPoint(o.pubkey)) + throw new TypeError('Output pubkey is invalid'); + if (a.pubkey && !a.pubkey.equals(o.pubkey!)) + throw new TypeError('Pubkey mismatch'); } if (a.signature) { - if (a.input && !a.input.equals(o.input!)) throw new TypeError('Signature mismatch') + if (a.input && !a.input.equals(o.input!)) + throw new TypeError('Signature mismatch'); } if (a.input) { - if (_chunks().length !== 1) throw new TypeError('Input is invalid') - if (!bscript.isCanonicalScriptSignature(o.signature!)) throw new TypeError('Input has invalid signature') + if (_chunks().length !== 1) throw new TypeError('Input is invalid'); + if (!bscript.isCanonicalScriptSignature(o.signature!)) + throw new TypeError('Input has invalid signature'); } } - return Object.assign(o, a) + return Object.assign(o, a); } diff --git a/ts_src/payments/p2pkh.ts b/ts_src/payments/p2pkh.ts index ddb9cc3..ccac25d 100644 --- a/ts_src/payments/p2pkh.ts +++ b/ts_src/payments/p2pkh.ts @@ -1,101 +1,103 @@ -import { Payment, PaymentOpts } from './index' // eslint-disable-line -import * as bscript from '../script' -import * as bcrypto from '../crypto' -import * as lazy from './lazy' -import { bitcoin as BITCOIN_NETWORK } from '../networks' -const typef = require('typeforce') -const OPS = bscript.OPS -const ecc = require('tiny-secp256k1') +import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import * as bscript from '../script'; +import * as bcrypto from '../crypto'; +import * as lazy from './lazy'; +import { bitcoin as BITCOIN_NETWORK } from '../networks'; +const typef = require('typeforce'); +const OPS = bscript.OPS; +const ecc = require('tiny-secp256k1'); -const bs58check = require('bs58check') +const bs58check = require('bs58check'); // input: {signature} {pubkey} // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG -export function p2pkh (a: Payment, opts?: PaymentOpts): Payment { - if ( - !a.address && - !a.hash && - !a.output && - !a.pubkey && - !a.input - ) throw new TypeError('Not enough data') - opts = Object.assign({ validate: true }, opts || {}) +export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { + if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); - typef({ - network: typef.maybe(typef.Object), - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(20)), - output: typef.maybe(typef.BufferN(25)), + typef( + { + network: typef.maybe(typef.Object), + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + output: typef.maybe(typef.BufferN(25)), - pubkey: typef.maybe(ecc.isPoint), - signature: typef.maybe(bscript.isCanonicalScriptSignature), - input: typef.maybe(typef.Buffer) - }, a) + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + input: typef.maybe(typef.Buffer), + }, + a, + ); - const _address = lazy.value(function () { - const payload = bs58check.decode(a.address) - const version = payload.readUInt8(0) - const hash = payload.slice(1) - return { version, hash } - }) - const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.input!) }) + const _address = lazy.value(function() { + const payload = bs58check.decode(a.address); + const version = payload.readUInt8(0); + const hash = payload.slice(1); + return { version, hash }; + }); + const _chunks = <() => Array<Buffer | number>>lazy.value(function() { + return bscript.decompile(a.input!); + }); - const network = a.network || BITCOIN_NETWORK - const o: Payment = { network } + const network = a.network || BITCOIN_NETWORK; + const o: Payment = { network }; - lazy.prop(o, 'address', function () { - if (!o.hash) return + lazy.prop(o, 'address', function() { + if (!o.hash) return; - const payload = Buffer.allocUnsafe(21) - payload.writeUInt8(network.pubKeyHash, 0) - o.hash.copy(payload, 1) - return bs58check.encode(payload) - }) - lazy.prop(o, 'hash', function () { - if (a.output) return a.output.slice(3, 23) - if (a.address) return _address().hash - if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!) - }) - lazy.prop(o, 'output', function () { - if (!o.hash) return + const payload = Buffer.allocUnsafe(21); + payload.writeUInt8(network.pubKeyHash, 0); + o.hash.copy(payload, 1); + return bs58check.encode(payload); + }); + lazy.prop(o, 'hash', function() { + if (a.output) return a.output.slice(3, 23); + if (a.address) return _address().hash; + if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!); + }); + lazy.prop(o, 'output', function() { + if (!o.hash) return; return bscript.compile([ OPS.OP_DUP, OPS.OP_HASH160, o.hash, OPS.OP_EQUALVERIFY, - OPS.OP_CHECKSIG - ]) - }) - lazy.prop(o, 'pubkey', function () { - if (!a.input) return - return <Buffer>_chunks()[1] - }) - lazy.prop(o, 'signature', function () { - if (!a.input) return - return <Buffer>_chunks()[0] - }) - lazy.prop(o, 'input', function () { - if (!a.pubkey) return - if (!a.signature) return - return bscript.compile([a.signature, a.pubkey]) - }) - lazy.prop(o, 'witness', function () { - if (!o.input) return - return [] - }) + OPS.OP_CHECKSIG, + ]); + }); + lazy.prop(o, 'pubkey', function() { + if (!a.input) return; + return <Buffer>_chunks()[1]; + }); + lazy.prop(o, 'signature', function() { + if (!a.input) return; + return <Buffer>_chunks()[0]; + }); + lazy.prop(o, 'input', function() { + if (!a.pubkey) return; + if (!a.signature) return; + return bscript.compile([a.signature, a.pubkey]); + }); + lazy.prop(o, 'witness', function() { + if (!o.input) return; + return []; + }); // extended validation if (opts.validate) { - let hash: Buffer = Buffer.from([]) + let hash: Buffer = Buffer.from([]); if (a.address) { - if (_address().version !== network.pubKeyHash) throw new TypeError('Invalid version or Network mismatch') - if (_address().hash.length !== 20) throw new TypeError('Invalid address') - hash = _address().hash + if (_address().version !== network.pubKeyHash) + throw new TypeError('Invalid version or Network mismatch'); + if (_address().hash.length !== 20) throw new TypeError('Invalid address'); + hash = _address().hash; } if (a.hash) { - if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') - else hash = a.hash + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else hash = a.hash; } if (a.output) { @@ -105,32 +107,41 @@ export function p2pkh (a: Payment, opts?: PaymentOpts): Payment { a.output[1] !== OPS.OP_HASH160 || a.output[2] !== 0x14 || a.output[23] !== OPS.OP_EQUALVERIFY || - a.output[24] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid') + a.output[24] !== OPS.OP_CHECKSIG + ) + throw new TypeError('Output is invalid'); - const hash2 = a.output.slice(3, 23) - if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') - else hash = hash2 + const hash2 = a.output.slice(3, 23); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; } if (a.pubkey) { - const pkh = bcrypto.hash160(a.pubkey) - if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch') - else hash = pkh + const pkh = bcrypto.hash160(a.pubkey); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + else hash = pkh; } if (a.input) { - const chunks = _chunks() - if (chunks.length !== 2) throw new TypeError('Input is invalid') - if (!bscript.isCanonicalScriptSignature(<Buffer>chunks[0])) throw new TypeError('Input has invalid signature') - if (!ecc.isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey') + const chunks = _chunks(); + if (chunks.length !== 2) throw new TypeError('Input is invalid'); + if (!bscript.isCanonicalScriptSignature(<Buffer>chunks[0])) + throw new TypeError('Input has invalid signature'); + if (!ecc.isPoint(chunks[1])) + throw new TypeError('Input has invalid pubkey'); - if (a.signature && !a.signature.equals(<Buffer>chunks[0])) throw new TypeError('Signature mismatch') - if (a.pubkey && !a.pubkey.equals(<Buffer>chunks[1])) throw new TypeError('Pubkey mismatch') + if (a.signature && !a.signature.equals(<Buffer>chunks[0])) + throw new TypeError('Signature mismatch'); + if (a.pubkey && !a.pubkey.equals(<Buffer>chunks[1])) + throw new TypeError('Pubkey mismatch'); - const pkh = bcrypto.hash160(<Buffer>chunks[1]) - if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + const pkh = bcrypto.hash160(<Buffer>chunks[1]); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); } } - return Object.assign(o, a) + return Object.assign(o, a); } diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index daaa8cc..281e3df 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -19,109 +19,109 @@ function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { // input: [redeemScriptSig ...] {redeemScript} // witness: <?> // output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL -export function p2sh (a: Payment, opts?: PaymentOpts): Payment { - if ( - !a.address && - !a.hash && - !a.output && - !a.redeem && - !a.input - ) throw new TypeError('Not enough data') - opts = Object.assign({ validate: true }, opts || {}) +export function p2sh(a: Payment, opts?: PaymentOpts): Payment { + if (!a.address && !a.hash && !a.output && !a.redeem && !a.input) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); - typef({ - network: typef.maybe(typef.Object), - - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(20)), - output: typef.maybe(typef.BufferN(23)), - - redeem: typef.maybe({ + typef( + { network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - input: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) - }), - input: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) - }, a) - let network = a.network + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + output: typef.maybe(typef.BufferN(23)), + + redeem: typef.maybe({ + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + input: typef.maybe(typef.Buffer), + witness: typef.maybe(typef.arrayOf(typef.Buffer)), + }), + input: typef.maybe(typef.Buffer), + witness: typef.maybe(typef.arrayOf(typef.Buffer)), + }, + a, + ); + + let network = a.network; if (!network) { - network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK + network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK; } - const o: Payment = { network } + const o: Payment = { network }; - const _address = lazy.value(function () { - const payload = bs58check.decode(a.address) - const version = payload.readUInt8(0) - const hash = payload.slice(1) - return { version, hash } - }) - const _chunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.input!) }) - const _redeem = lazy.value(function (): Payment { - const chunks = _chunks() + const _address = lazy.value(function() { + const payload = bs58check.decode(a.address); + const version = payload.readUInt8(0); + const hash = payload.slice(1); + return { version, hash }; + }); + const _chunks = <() => Array<Buffer | number>>lazy.value(function() { + return bscript.decompile(a.input!); + }); + const _redeem = lazy.value(function(): Payment { + const chunks = _chunks(); return { network, output: <Buffer>chunks[chunks.length - 1], input: bscript.compile(chunks.slice(0, -1)), - witness: a.witness || [] - } - }) + witness: a.witness || [], + }; + }); // output dependents - lazy.prop(o, 'address', function () { - if (!o.hash) return + lazy.prop(o, 'address', function() { + if (!o.hash) return; - const payload = Buffer.allocUnsafe(21) - payload.writeUInt8(o.network!.scriptHash, 0) - o.hash.copy(payload, 1) - return bs58check.encode(payload) - }) - lazy.prop(o, 'hash', function () { + const payload = Buffer.allocUnsafe(21); + payload.writeUInt8(o.network!.scriptHash, 0); + o.hash.copy(payload, 1); + return bs58check.encode(payload); + }); + lazy.prop(o, 'hash', function() { // in order of least effort - if (a.output) return a.output.slice(2, 22) - if (a.address) return _address().hash - if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output) - }) - lazy.prop(o, 'output', function () { - if (!o.hash) return - return bscript.compile([ - OPS.OP_HASH160, - o.hash, - OPS.OP_EQUAL - ]) - }) + if (a.output) return a.output.slice(2, 22); + if (a.address) return _address().hash; + if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output); + }); + lazy.prop(o, 'output', function() { + if (!o.hash) return; + return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); + }); // input dependents - lazy.prop(o, 'redeem', function () { - if (!a.input) return - return _redeem() - }) - lazy.prop(o, 'input', function () { - if (!a.redeem || !a.redeem.input || !a.redeem.output) return - return bscript.compile((<Array<Buffer | number>>[]).concat( - <Array<Buffer | number>>bscript.decompile(a.redeem.input), - a.redeem.output - )) - }) - lazy.prop(o, 'witness', function () { - if (o.redeem && o.redeem.witness) return o.redeem.witness - if (o.input) return [] - }) + lazy.prop(o, 'redeem', function() { + if (!a.input) return; + return _redeem(); + }); + lazy.prop(o, 'input', function() { + if (!a.redeem || !a.redeem.input || !a.redeem.output) return; + return bscript.compile( + (<Array<Buffer | number>>[]).concat( + <Array<Buffer | number>>bscript.decompile(a.redeem.input), + a.redeem.output, + ), + ); + }); + lazy.prop(o, 'witness', function() { + if (o.redeem && o.redeem.witness) return o.redeem.witness; + if (o.input) return []; + }); if (opts.validate) { - let hash: Buffer = Buffer.from([]) + let hash: Buffer = Buffer.from([]); if (a.address) { - if (_address().version !== network.scriptHash) throw new TypeError('Invalid version or Network mismatch') - if (_address().hash.length !== 20) throw new TypeError('Invalid address') - hash = _address().hash + if (_address().version !== network.scriptHash) + throw new TypeError('Invalid version or Network mismatch'); + if (_address().hash.length !== 20) throw new TypeError('Invalid address'); + hash = _address().hash; } if (a.hash) { - if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') - else hash = a.hash + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else hash = a.hash; } if (a.output) { @@ -129,64 +129,79 @@ export function p2sh (a: Payment, opts?: PaymentOpts): Payment { a.output.length !== 23 || a.output[0] !== OPS.OP_HASH160 || a.output[1] !== 0x14 || - a.output[22] !== OPS.OP_EQUAL) throw new TypeError('Output is invalid') + a.output[22] !== OPS.OP_EQUAL + ) + throw new TypeError('Output is invalid'); - const hash2 = a.output.slice(2, 22) - if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') - else hash = hash2 + const hash2 = a.output.slice(2, 22); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; } // inlined to prevent 'no-inner-declarations' failing - const checkRedeem = function (redeem: Payment): void { + const checkRedeem = function(redeem: Payment): void { // is the redeem output empty/invalid? if (redeem.output) { - const decompile = bscript.decompile(redeem.output) - if (!decompile || decompile.length < 1) throw new TypeError('Redeem.output too short') + const decompile = bscript.decompile(redeem.output); + if (!decompile || decompile.length < 1) + throw new TypeError('Redeem.output too short'); // match hash against other sources - const hash2 = bcrypto.hash160(redeem.output) - if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') - else hash = hash2 + const hash2 = bcrypto.hash160(redeem.output); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; } if (redeem.input) { - const hasInput = redeem.input.length > 0 - const hasWitness = redeem.witness && redeem.witness.length > 0 - if (!hasInput && !hasWitness) throw new TypeError('Empty input') - if (hasInput && hasWitness) throw new TypeError('Input and witness provided') + const hasInput = redeem.input.length > 0; + const hasWitness = redeem.witness && redeem.witness.length > 0; + if (!hasInput && !hasWitness) throw new TypeError('Empty input'); + if (hasInput && hasWitness) + throw new TypeError('Input and witness provided'); if (hasInput) { - const richunks = <Array<Buffer | number>>bscript.decompile(redeem.input) - if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig') + const richunks = <Array<Buffer | number>>( + bscript.decompile(redeem.input) + ); + if (!bscript.isPushOnly(richunks)) + throw new TypeError('Non push-only scriptSig'); } } - } + }; if (a.input) { - const chunks = _chunks() - if (!chunks || chunks.length < 1) throw new TypeError('Input too short') - if (!Buffer.isBuffer(_redeem().output)) throw new TypeError('Input is invalid') + const chunks = _chunks(); + if (!chunks || chunks.length < 1) throw new TypeError('Input too short'); + if (!Buffer.isBuffer(_redeem().output)) + throw new TypeError('Input is invalid'); - checkRedeem(_redeem()) + checkRedeem(_redeem()); } if (a.redeem) { - if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') + if (a.redeem.network && a.redeem.network !== network) + throw new TypeError('Network mismatch'); if (a.input) { - const redeem = _redeem() - if (a.redeem.output && !a.redeem.output.equals(redeem.output!)) throw new TypeError('Redeem.output mismatch') - if (a.redeem.input && !a.redeem.input.equals(redeem.input!)) throw new TypeError('Redeem.input mismatch') + const redeem = _redeem(); + if (a.redeem.output && !a.redeem.output.equals(redeem.output!)) + throw new TypeError('Redeem.output mismatch'); + if (a.redeem.input && !a.redeem.input.equals(redeem.input!)) + throw new TypeError('Redeem.input mismatch'); } - checkRedeem(a.redeem) + checkRedeem(a.redeem); } if (a.witness) { if ( a.redeem && a.redeem.witness && - !stacksEqual(a.redeem.witness, a.witness)) throw new TypeError('Witness and redeem.witness mismatch') + !stacksEqual(a.redeem.witness, a.witness) + ) + throw new TypeError('Witness and redeem.witness mismatch'); } } - return Object.assign(o, a) + return Object.assign(o, a); } diff --git a/ts_src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts index 6cfecc4..ec7ed40 100644 --- a/ts_src/payments/p2wpkh.ts +++ b/ts_src/payments/p2wpkh.ts @@ -1,134 +1,142 @@ -import { Payment, PaymentOpts } from './index' // eslint-disable-line -import * as bscript from '../script' -import * as bcrypto from '../crypto' -import * as lazy from './lazy' -import { bitcoin as BITCOIN_NETWORK } from '../networks' -const typef = require('typeforce') -const OPS = bscript.OPS -const ecc = require('tiny-secp256k1') +import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import * as bscript from '../script'; +import * as bcrypto from '../crypto'; +import * as lazy from './lazy'; +import { bitcoin as BITCOIN_NETWORK } from '../networks'; +const typef = require('typeforce'); +const OPS = bscript.OPS; +const ecc = require('tiny-secp256k1'); -const bech32 = require('bech32') +const bech32 = require('bech32'); -const EMPTY_BUFFER = Buffer.alloc(0) +const EMPTY_BUFFER = Buffer.alloc(0); // witness: {signature} {pubKey} // input: <> // output: OP_0 {pubKeyHash} -export function p2wpkh (a: Payment, opts?: PaymentOpts): Payment { - if ( - !a.address && - !a.hash && - !a.output && - !a.pubkey && - !a.witness - ) throw new TypeError('Not enough data') - opts = Object.assign({ validate: true }, opts || {}) +export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { + if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); - typef({ - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(20)), - input: typef.maybe(typef.BufferN(0)), - network: typef.maybe(typef.Object), - output: typef.maybe(typef.BufferN(22)), - pubkey: typef.maybe(ecc.isPoint), - signature: typef.maybe(bscript.isCanonicalScriptSignature), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) - }, a) + typef( + { + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + input: typef.maybe(typef.BufferN(0)), + network: typef.maybe(typef.Object), + output: typef.maybe(typef.BufferN(22)), + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + witness: typef.maybe(typef.arrayOf(typef.Buffer)), + }, + a, + ); - const _address = lazy.value(function () { - const result = bech32.decode(a.address) - const version = result.words.shift() - const data = bech32.fromWords(result.words) + const _address = lazy.value(function() { + const result = bech32.decode(a.address); + const version = result.words.shift(); + const data = bech32.fromWords(result.words); return { version, prefix: result.prefix, - data: Buffer.from(data) - } - }) + data: Buffer.from(data), + }; + }); - const network = a.network || BITCOIN_NETWORK - const o: Payment = { network } + const network = a.network || BITCOIN_NETWORK; + const o: Payment = { network }; - lazy.prop(o, 'address', function () { - if (!o.hash) return + lazy.prop(o, 'address', function() { + if (!o.hash) return; - const words = bech32.toWords(o.hash) - words.unshift(0x00) - return bech32.encode(network.bech32, words) - }) - lazy.prop(o, 'hash', function () { - if (a.output) return a.output.slice(2, 22) - if (a.address) return _address().data - if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!) - }) - lazy.prop(o, 'output', function () { - if (!o.hash) return - return bscript.compile([ - OPS.OP_0, - o.hash - ]) - }) - lazy.prop(o, 'pubkey', function () { - if (a.pubkey) return a.pubkey - if (!a.witness) return - return a.witness[1] - }) - lazy.prop(o, 'signature', function () { - if (!a.witness) return - return a.witness[0] - }) - lazy.prop(o, 'input', function () { - if (!o.witness) return - return EMPTY_BUFFER - }) - lazy.prop(o, 'witness', function () { - if (!a.pubkey) return - if (!a.signature) return - return [a.signature, a.pubkey] - }) + const words = bech32.toWords(o.hash); + words.unshift(0x00); + return bech32.encode(network.bech32, words); + }); + lazy.prop(o, 'hash', function() { + if (a.output) return a.output.slice(2, 22); + if (a.address) return _address().data; + if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!); + }); + lazy.prop(o, 'output', function() { + if (!o.hash) return; + return bscript.compile([OPS.OP_0, o.hash]); + }); + lazy.prop(o, 'pubkey', function() { + if (a.pubkey) return a.pubkey; + if (!a.witness) return; + return a.witness[1]; + }); + lazy.prop(o, 'signature', function() { + if (!a.witness) return; + return a.witness[0]; + }); + lazy.prop(o, 'input', function() { + if (!o.witness) return; + return EMPTY_BUFFER; + }); + lazy.prop(o, 'witness', function() { + if (!a.pubkey) return; + if (!a.signature) return; + return [a.signature, a.pubkey]; + }); // extended validation if (opts.validate) { - let hash: Buffer = Buffer.from([]) + let hash: Buffer = Buffer.from([]); if (a.address) { - if (network && network.bech32 !== _address().prefix) throw new TypeError('Invalid prefix or Network mismatch') - if (_address().version !== 0x00) throw new TypeError('Invalid address version') - if (_address().data.length !== 20) throw new TypeError('Invalid address data') - hash = _address().data + if (network && network.bech32 !== _address().prefix) + throw new TypeError('Invalid prefix or Network mismatch'); + if (_address().version !== 0x00) + throw new TypeError('Invalid address version'); + if (_address().data.length !== 20) + throw new TypeError('Invalid address data'); + hash = _address().data; } if (a.hash) { - if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') - else hash = a.hash + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else hash = a.hash; } if (a.output) { if ( a.output.length !== 22 || a.output[0] !== OPS.OP_0 || - a.output[1] !== 0x14) throw new TypeError('Output is invalid') - if (hash.length > 0 && !hash.equals(a.output.slice(2))) throw new TypeError('Hash mismatch') - else hash = a.output.slice(2) + a.output[1] !== 0x14 + ) + throw new TypeError('Output is invalid'); + if (hash.length > 0 && !hash.equals(a.output.slice(2))) + throw new TypeError('Hash mismatch'); + else hash = a.output.slice(2); } if (a.pubkey) { - const pkh = bcrypto.hash160(a.pubkey) - if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch') - else hash = pkh + const pkh = bcrypto.hash160(a.pubkey); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + else hash = pkh; } if (a.witness) { - if (a.witness.length !== 2) throw new TypeError('Witness is invalid') - if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Witness has invalid signature') - if (!ecc.isPoint(a.witness[1])) throw new TypeError('Witness has invalid pubkey') + if (a.witness.length !== 2) throw new TypeError('Witness is invalid'); + if (!bscript.isCanonicalScriptSignature(a.witness[0])) + throw new TypeError('Witness has invalid signature'); + if (!ecc.isPoint(a.witness[1])) + throw new TypeError('Witness has invalid pubkey'); - if (a.signature && !a.signature.equals(a.witness[0])) throw new TypeError('Signature mismatch') - if (a.pubkey && !a.pubkey.equals(a.witness[1])) throw new TypeError('Pubkey mismatch') + if (a.signature && !a.signature.equals(a.witness[0])) + throw new TypeError('Signature mismatch'); + if (a.pubkey && !a.pubkey.equals(a.witness[1])) + throw new TypeError('Pubkey mismatch'); - const pkh = bcrypto.hash160(a.witness[1]) - if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch') + const pkh = bcrypto.hash160(a.witness[1]); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); } } - return Object.assign(o, a) + return Object.assign(o, a); } diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index edb0a69..5f9b02d 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -6,98 +6,95 @@ import * as lazy from './lazy' const typef = require('typeforce') const OPS = bscript.OPS -const bech32 = require('bech32') +const bech32 = require('bech32'); -const EMPTY_BUFFER = Buffer.alloc(0) +const EMPTY_BUFFER = Buffer.alloc(0); -function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { - if (a.length !== b.length) return false +function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean { + if (a.length !== b.length) return false; - return a.every(function (x, i) { - return x.equals(b[i]) - }) + return a.every(function(x, i) { + return x.equals(b[i]); + }); } // input: <> // witness: [redeemScriptSig ...] {redeemScript} // output: OP_0 {sha256(redeemScript)} -export function p2wsh (a: Payment, opts?: PaymentOpts): Payment { - if ( - !a.address && - !a.hash && - !a.output && - !a.redeem && - !a.witness - ) throw new TypeError('Not enough data') - opts = Object.assign({ validate: true }, opts || {}) +export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { + if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); - typef({ - network: typef.maybe(typef.Object), - - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(32)), - output: typef.maybe(typef.BufferN(34)), - - redeem: typef.maybe({ - input: typef.maybe(typef.Buffer), + typef( + { network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) - }), - input: typef.maybe(typef.BufferN(0)), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) - }, a) - const _address = lazy.value(function () { - const result = bech32.decode(a.address) - const version = result.words.shift() - const data = bech32.fromWords(result.words) + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(32)), + output: typef.maybe(typef.BufferN(34)), + + redeem: typef.maybe({ + input: typef.maybe(typef.Buffer), + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + witness: typef.maybe(typef.arrayOf(typef.Buffer)), + }), + input: typef.maybe(typef.BufferN(0)), + witness: typef.maybe(typef.arrayOf(typef.Buffer)), + }, + a, + ); + + const _address = lazy.value(function() { + const result = bech32.decode(a.address); + const version = result.words.shift(); + const data = bech32.fromWords(result.words); return { version, prefix: result.prefix, - data: Buffer.from(data) - } - }) - const _rchunks = <()=>Array<Buffer | number>>lazy.value(function () { return bscript.decompile(a.redeem!.input!) }) + data: Buffer.from(data), + }; + }); + const _rchunks = <() => Array<Buffer | number>>lazy.value(function() { + return bscript.decompile(a.redeem!.input!); + }); - let network = a.network + let network = a.network; if (!network) { - network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK + network = (a.redeem && a.redeem.network) || BITCOIN_NETWORK; } - const o: Payment = { network } + const o: Payment = { network }; - lazy.prop(o, 'address', function () { - if (!o.hash) return - const words = bech32.toWords(o.hash) - words.unshift(0x00) - return bech32.encode(network!.bech32, words) - }) - lazy.prop(o, 'hash', function () { - if (a.output) return a.output.slice(2) - if (a.address) return _address().data - if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output) - }) - lazy.prop(o, 'output', function () { - if (!o.hash) return - return bscript.compile([ - OPS.OP_0, - o.hash - ]) - }) - lazy.prop(o, 'redeem', function () { - if (!a.witness) return + lazy.prop(o, 'address', function() { + if (!o.hash) return; + const words = bech32.toWords(o.hash); + words.unshift(0x00); + return bech32.encode(network!.bech32, words); + }); + lazy.prop(o, 'hash', function() { + if (a.output) return a.output.slice(2); + if (a.address) return _address().data; + if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output); + }); + lazy.prop(o, 'output', function() { + if (!o.hash) return; + return bscript.compile([OPS.OP_0, o.hash]); + }); + lazy.prop(o, 'redeem', function() { + if (!a.witness) return; return { output: a.witness[a.witness.length - 1], input: EMPTY_BUFFER, - witness: a.witness.slice(0, -1) - } - }) - lazy.prop(o, 'input', function () { - if (!o.witness) return - return EMPTY_BUFFER - }) - lazy.prop(o, 'witness', function () { + witness: a.witness.slice(0, -1), + }; + }); + lazy.prop(o, 'input', function() { + if (!o.witness) return; + return EMPTY_BUFFER; + }); + lazy.prop(o, 'witness', function() { // transform redeem input to witness stack? if ( a.redeem && @@ -106,47 +103,55 @@ export function p2wsh (a: Payment, opts?: PaymentOpts): Payment { a.redeem.output && a.redeem.output.length > 0 ) { - const stack = bscript.toStack(_rchunks()) + const stack = bscript.toStack(_rchunks()); // assign, and blank the existing input - o.redeem = Object.assign({ witness: stack }, a.redeem) - o.redeem.input = EMPTY_BUFFER - return (<Array<Buffer>>[]).concat(stack, a.redeem.output) + o.redeem = Object.assign({ witness: stack }, a.redeem); + o.redeem.input = EMPTY_BUFFER; + return (<Array<Buffer>>[]).concat(stack, a.redeem.output); } - if (!a.redeem) return - if (!a.redeem.output) return - if (!a.redeem.witness) return - return (<Array<Buffer>>[]).concat(a.redeem.witness, a.redeem.output) - }) + if (!a.redeem) return; + if (!a.redeem.output) return; + if (!a.redeem.witness) return; + return (<Array<Buffer>>[]).concat(a.redeem.witness, a.redeem.output); + }); // extended validation if (opts.validate) { - let hash: Buffer = Buffer.from([]) + let hash: Buffer = Buffer.from([]); if (a.address) { - if (_address().prefix !== network.bech32) throw new TypeError('Invalid prefix or Network mismatch') - if (_address().version !== 0x00) throw new TypeError('Invalid address version') - if (_address().data.length !== 32) throw new TypeError('Invalid address data') - hash = _address().data + if (_address().prefix !== network.bech32) + throw new TypeError('Invalid prefix or Network mismatch'); + if (_address().version !== 0x00) + throw new TypeError('Invalid address version'); + if (_address().data.length !== 32) + throw new TypeError('Invalid address data'); + hash = _address().data; } if (a.hash) { - if (hash.length > 0 && !hash.equals(a.hash)) throw new TypeError('Hash mismatch') - else hash = a.hash + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else hash = a.hash; } if (a.output) { if ( a.output.length !== 34 || a.output[0] !== OPS.OP_0 || - a.output[1] !== 0x20) throw new TypeError('Output is invalid') - const hash2 = a.output.slice(2) - if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') - else hash = hash2 + a.output[1] !== 0x20 + ) + throw new TypeError('Output is invalid'); + const hash2 = a.output.slice(2); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; } if (a.redeem) { - if (a.redeem.network && a.redeem.network !== network) throw new TypeError('Network mismatch') + if (a.redeem.network && a.redeem.network !== network) + throw new TypeError('Network mismatch'); // is there two redeem sources? if ( @@ -154,26 +159,40 @@ export function p2wsh (a: Payment, opts?: PaymentOpts): Payment { a.redeem.input.length > 0 && a.redeem.witness && a.redeem.witness.length > 0 - ) throw new TypeError('Ambiguous witness source') + ) + throw new TypeError('Ambiguous witness source'); // is the redeem output non-empty? if (a.redeem.output) { - if (bscript.decompile(a.redeem.output)!.length === 0) throw new TypeError('Redeem.output is invalid') + if (bscript.decompile(a.redeem.output)!.length === 0) + throw new TypeError('Redeem.output is invalid'); // match hash against other sources - const hash2 = bcrypto.sha256(a.redeem.output) - if (hash.length > 0 && !hash.equals(hash2)) throw new TypeError('Hash mismatch') - else hash = hash2 + const hash2 = bcrypto.sha256(a.redeem.output); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; } - if (a.redeem.input && !bscript.isPushOnly(_rchunks())) throw new TypeError('Non push-only scriptSig') - if (a.witness && a.redeem.witness && !stacksEqual(a.witness, a.redeem.witness)) throw new TypeError('Witness and redeem.witness mismatch') + if (a.redeem.input && !bscript.isPushOnly(_rchunks())) + throw new TypeError('Non push-only scriptSig'); + if ( + a.witness && + a.redeem.witness && + !stacksEqual(a.witness, a.redeem.witness) + ) + throw new TypeError('Witness and redeem.witness mismatch'); } if (a.witness) { - if (a.redeem && a.redeem.output && !a.redeem.output.equals(a.witness[a.witness.length - 1])) throw new TypeError('Witness and redeem.output mismatch') + if ( + a.redeem && + a.redeem.output && + !a.redeem.output.equals(a.witness[a.witness.length - 1]) + ) + throw new TypeError('Witness and redeem.output mismatch'); } } - return Object.assign(o, a) + return Object.assign(o, a); } diff --git a/ts_src/script.ts b/ts_src/script.ts index 64b74ad..36fef95 100644 --- a/ts_src/script.ts +++ b/ts_src/script.ts @@ -1,206 +1,218 @@ -import * as types from './types' -import * as scriptNumber from './script_number' -import * as scriptSignature from './script_signature' -const bip66 = require('bip66') -const ecc = require('tiny-secp256k1') -const pushdata = require('pushdata-bitcoin') -const typeforce = require('typeforce') +import * as types from './types'; +import * as scriptNumber from './script_number'; +import * as scriptSignature from './script_signature'; +const bip66 = require('bip66'); +const ecc = require('tiny-secp256k1'); +const pushdata = require('pushdata-bitcoin'); +const typeforce = require('typeforce'); -export type OpCode = number -export const OPS = <{[index:string]: OpCode}> require('bitcoin-ops') +export type OpCode = number; +export const OPS = <{ [index: string]: OpCode }>require('bitcoin-ops'); -const REVERSE_OPS = <{[index:number]: string}> require('bitcoin-ops/map') -const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 +const REVERSE_OPS = <{ [index: number]: string }>require('bitcoin-ops/map'); +const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 -function isOPInt (value:number): boolean { - return types.Number(value) && - ((value === OPS.OP_0) || - (value >= OPS.OP_1 && value <= OPS.OP_16) || - (value === OPS.OP_1NEGATE)) +function isOPInt(value: number): boolean { + return ( + types.Number(value) && + (value === OPS.OP_0 || + (value >= OPS.OP_1 && value <= OPS.OP_16) || + value === OPS.OP_1NEGATE) + ); } -function isPushOnlyChunk (value: number | Buffer): boolean { - return types.Buffer(value) || isOPInt(<number>value) +function isPushOnlyChunk(value: number | Buffer): boolean { + return types.Buffer(value) || isOPInt(<number>value); } -export function isPushOnly (value: Array<number | Buffer>) { - return types.Array(value) && value.every(isPushOnlyChunk) +export function isPushOnly(value: Array<number | Buffer>) { + return types.Array(value) && value.every(isPushOnlyChunk); } -function asMinimalOP (buffer: Buffer): number | void { - if (buffer.length === 0) return OPS.OP_0 - if (buffer.length !== 1) return - if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0] - if (buffer[0] === 0x81) return OPS.OP_1NEGATE +function asMinimalOP(buffer: Buffer): number | void { + if (buffer.length === 0) return OPS.OP_0; + if (buffer.length !== 1) return; + if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0]; + if (buffer[0] === 0x81) return OPS.OP_1NEGATE; } function chunksIsBuffer(buf: Buffer | Array<number | Buffer>): buf is Buffer { - return Buffer.isBuffer(buf) + return Buffer.isBuffer(buf); } -function chunksIsArray(buf: Buffer | Array<number | Buffer>): buf is Array<number | Buffer> { - return types.Array(buf) +function chunksIsArray( + buf: Buffer | Array<number | Buffer>, +): buf is Array<number | Buffer> { + return types.Array(buf); } function singleChunkIsBuffer(buf: number | Buffer): buf is Buffer { - return Buffer.isBuffer(buf) + return Buffer.isBuffer(buf); } -export function compile (chunks: Buffer | Array<number | Buffer>): Buffer { +export function compile(chunks: Buffer | Array<number | Buffer>): Buffer { // TODO: remove me - if (chunksIsBuffer(chunks)) return chunks + if (chunksIsBuffer(chunks)) return chunks; - typeforce(types.Array, chunks) + typeforce(types.Array, chunks); - const bufferSize = chunks.reduce(function (accum: number, chunk) { + const bufferSize = chunks.reduce(function(accum: number, chunk) { // data chunk if (singleChunkIsBuffer(chunk)) { // adhere to BIP62.3, minimal push policy if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { - return accum + 1 + return accum + 1; } - return accum + pushdata.encodingLength(chunk.length) + chunk.length + return accum + pushdata.encodingLength(chunk.length) + chunk.length; } // opcode - return accum + 1 - }, 0.0) + return accum + 1; + }, 0.0); - const buffer = Buffer.allocUnsafe(bufferSize) - let offset = 0 + const buffer = Buffer.allocUnsafe(bufferSize); + let offset = 0; - chunks.forEach(function (chunk) { + chunks.forEach(function(chunk) { // data chunk if (singleChunkIsBuffer(chunk)) { // adhere to BIP62.3, minimal push policy - const opcode = asMinimalOP(chunk) + const opcode = asMinimalOP(chunk); if (opcode !== undefined) { - buffer.writeUInt8(opcode, offset) - offset += 1 - return + buffer.writeUInt8(opcode, offset); + offset += 1; + return; } - offset += pushdata.encode(buffer, chunk.length, offset) - chunk.copy(buffer, offset) - offset += chunk.length + offset += pushdata.encode(buffer, chunk.length, offset); + chunk.copy(buffer, offset); + offset += chunk.length; - // opcode + // opcode } else { - buffer.writeUInt8(chunk, offset) - offset += 1 + buffer.writeUInt8(chunk, offset); + offset += 1; } - }) + }); - if (offset !== buffer.length) throw new Error('Could not decode chunks') - return buffer + if (offset !== buffer.length) throw new Error('Could not decode chunks'); + return buffer; } -export function decompile (buffer: Buffer | Array<number | Buffer>): Array<number | Buffer> | null { +export function decompile( + buffer: Buffer | Array<number | Buffer>, +): Array<number | Buffer> | null { // TODO: remove me - if (chunksIsArray(buffer)) return buffer + if (chunksIsArray(buffer)) return buffer; - typeforce(types.Buffer, buffer) + typeforce(types.Buffer, buffer); - const chunks: Array<number | Buffer> = [] - let i = 0 + const chunks: Array<number | Buffer> = []; + let i = 0; while (i < buffer.length) { - const opcode = buffer[i] + const opcode = buffer[i]; // data chunk - if ((opcode > OPS.OP_0) && (opcode <= OPS.OP_PUSHDATA4)) { - const d = pushdata.decode(buffer, i) + if (opcode > OPS.OP_0 && opcode <= OPS.OP_PUSHDATA4) { + const d = pushdata.decode(buffer, i); // did reading a pushDataInt fail? - if (d === null) return null - i += d.size + if (d === null) return null; + i += d.size; // attempt to read too much data? - if (i + d.number > buffer.length) return null + if (i + d.number > buffer.length) return null; - const data = buffer.slice(i, i + d.number) - i += d.number + const data = buffer.slice(i, i + d.number); + i += d.number; // decompile minimally - const op = asMinimalOP(data) + const op = asMinimalOP(data); if (op !== undefined) { - chunks.push(op) + chunks.push(op); } else { - chunks.push(data) + chunks.push(data); } - // opcode + // opcode } else { - chunks.push(opcode) + chunks.push(opcode); - i += 1 + i += 1; } } + return chunks; +} + +export function toASM(chunks: Buffer | Array<number | Buffer>): string { + if (chunksIsBuffer(chunks)) { + chunks = <Array<number | Buffer>>decompile(chunks); + } + return chunks + .map(function(chunk) { + // data? + if (singleChunkIsBuffer(chunk)) { + const op = asMinimalOP(chunk); + if (op === undefined) return chunk.toString('hex'); + chunk = <number>op; + } + + // opcode! + return REVERSE_OPS[chunk]; + }) + .join(' '); } -export function toASM (chunks: Buffer | Array<number | Buffer>): string { - if (chunksIsBuffer(chunks)) { - chunks = <Array<number | Buffer>>decompile(chunks) - } +export function fromASM(asm: string): Buffer { + typeforce(types.String, asm); - return chunks.map(function (chunk) { - // data? - if (singleChunkIsBuffer(chunk)) { - const op = asMinimalOP(chunk) - if (op === undefined) return chunk.toString('hex') - chunk = <number>op - } + return compile( + asm.split(' ').map(function(chunkStr) { + // opcode? + if (OPS[chunkStr] !== undefined) return OPS[chunkStr]; + typeforce(types.Hex, chunkStr); - // opcode! - return REVERSE_OPS[chunk] - }).join(' ') + // data! + return Buffer.from(chunkStr, 'hex'); + }), + ); } -export function fromASM (asm: string): Buffer { - typeforce(types.String, asm) +export function toStack( + chunks: Buffer | Array<number | Buffer>, +): Array<Buffer> { + chunks = <Array<number | Buffer>>decompile(chunks); + typeforce(isPushOnly, chunks); - return compile(asm.split(' ').map(function (chunkStr) { - // opcode? - if (OPS[chunkStr] !== undefined) return OPS[chunkStr] - typeforce(types.Hex, chunkStr) + return chunks.map(function(op) { + if (singleChunkIsBuffer(op)) return op; + if (op === OPS.OP_0) return Buffer.allocUnsafe(0); - // data! - return Buffer.from(chunkStr, 'hex') - })) + return scriptNumber.encode(op - OP_INT_BASE); + }); } -export function toStack (chunks: Buffer | Array<number | Buffer>): Array<Buffer> { - chunks = <Array<number | Buffer>>decompile(chunks) - typeforce(isPushOnly, chunks) - - return chunks.map(function (op) { - if (singleChunkIsBuffer(op)) return op - if (op === OPS.OP_0) return Buffer.allocUnsafe(0) - - return scriptNumber.encode(op - OP_INT_BASE) - }) +export function isCanonicalPubKey(buffer: Buffer): boolean { + return ecc.isPoint(buffer); } -export function isCanonicalPubKey (buffer: Buffer): boolean { - return ecc.isPoint(buffer) -} - -export function isDefinedHashType (hashType: number): boolean { - const hashTypeMod = hashType & ~0x80 +export function isDefinedHashType(hashType: number): boolean { + const hashTypeMod = hashType & ~0x80; // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE - return hashTypeMod > 0x00 && hashTypeMod < 0x04 + return hashTypeMod > 0x00 && hashTypeMod < 0x04; } -export function isCanonicalScriptSignature (buffer: Buffer): boolean { - if (!Buffer.isBuffer(buffer)) return false - if (!isDefinedHashType(buffer[buffer.length - 1])) return false +export function isCanonicalScriptSignature(buffer: Buffer): boolean { + if (!Buffer.isBuffer(buffer)) return false; + if (!isDefinedHashType(buffer[buffer.length - 1])) return false; - return bip66.check(buffer.slice(0, -1)) + return bip66.check(buffer.slice(0, -1)); } -export const number = scriptNumber -export const signature = scriptSignature +export const number = scriptNumber; +export const signature = scriptSignature; diff --git a/ts_src/script_number.ts b/ts_src/script_number.ts index d88c463..cd48373 100644 --- a/ts_src/script_number.ts +++ b/ts_src/script_number.ts @@ -1,62 +1,71 @@ +export function decode( + buffer: Buffer, + maxLength?: number, + minimal?: boolean, +): number { + maxLength = maxLength || 4; + minimal = minimal === undefined ? true : minimal; - -export function decode (buffer: Buffer, maxLength?: number, minimal?: boolean): number { - maxLength = maxLength || 4 - minimal = minimal === undefined ? true : minimal - - const length = buffer.length - if (length === 0) return 0 - if (length > maxLength) throw new TypeError('Script number overflow') + const length = buffer.length; + if (length === 0) return 0; + if (length > maxLength) throw new TypeError('Script number overflow'); if (minimal) { if ((buffer[length - 1] & 0x7f) === 0) { - if (length <= 1 || (buffer[length - 2] & 0x80) === 0) throw new Error('Non-minimally encoded script number') + if (length <= 1 || (buffer[length - 2] & 0x80) === 0) + throw new Error('Non-minimally encoded script number'); } } // 40-bit if (length === 5) { - const a = buffer.readUInt32LE(0) - const b = buffer.readUInt8(4) + const a = buffer.readUInt32LE(0); + const b = buffer.readUInt8(4); - if (b & 0x80) return -(((b & ~0x80) * 0x100000000) + a) - return (b * 0x100000000) + a + if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a); + return b * 0x100000000 + a; } // 32-bit / 24-bit / 16-bit / 8-bit - let result = 0 + let result = 0; for (var i = 0; i < length; ++i) { - result |= buffer[i] << (8 * i) + result |= buffer[i] << (8 * i); } - if (buffer[length - 1] & 0x80) return -(result & ~(0x80 << (8 * (length - 1)))) - return result + if (buffer[length - 1] & 0x80) + return -(result & ~(0x80 << (8 * (length - 1)))); + return result; } -function scriptNumSize (i: number): number { - return i > 0x7fffffff ? 5 - : i > 0x7fffff ? 4 - : i > 0x7fff ? 3 - : i > 0x7f ? 2 - : i > 0x00 ? 1 - : 0 +function scriptNumSize(i: number): number { + return i > 0x7fffffff + ? 5 + : i > 0x7fffff + ? 4 + : i > 0x7fff + ? 3 + : i > 0x7f + ? 2 + : i > 0x00 + ? 1 + : 0; } -export function encode (number: number): Buffer { - let value = Math.abs(number) - const size = scriptNumSize(value) - const buffer = Buffer.allocUnsafe(size) - const negative = number < 0 +export function encode(number: number): Buffer { + let value = Math.abs(number); + const size = scriptNumSize(value); + const buffer = Buffer.allocUnsafe(size); + const negative = number < 0; for (var i = 0; i < size; ++i) { - buffer.writeUInt8(value & 0xff, i) - value >>= 8 + buffer.writeUInt8(value & 0xff, i); + value >>= 8; } if (buffer[size - 1] & 0x80) { - buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1) + buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1); } else if (negative) { - buffer[size - 1] |= 0x80 + buffer[size - 1] |= 0x80; } - return buffer + return buffer; } diff --git a/ts_src/script_signature.ts b/ts_src/script_signature.ts index 3b5bc6e..1f11d00 100644 --- a/ts_src/script_signature.ts +++ b/ts_src/script_signature.ts @@ -1,64 +1,66 @@ -import * as types from './types' -const bip66 = require('bip66') +import * as types from './types'; +const bip66 = require('bip66'); -const typeforce = require('typeforce') +const typeforce = require('typeforce'); -const ZERO = Buffer.alloc(1, 0) -function toDER (x: Buffer): Buffer { - let i = 0 - while (x[i] === 0) ++i - if (i === x.length) return ZERO - x = x.slice(i) - if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length) - return x +const ZERO = Buffer.alloc(1, 0); +function toDER(x: Buffer): Buffer { + let i = 0; + while (x[i] === 0) ++i; + if (i === x.length) return ZERO; + x = x.slice(i); + if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length); + return x; } -function fromDER (x: Buffer): Buffer { - if (x[0] === 0x00) x = x.slice(1) - const buffer = Buffer.alloc(32, 0) - const bstart = Math.max(0, 32 - x.length) - x.copy(buffer, bstart) - return buffer +function fromDER(x: Buffer): Buffer { + if (x[0] === 0x00) x = x.slice(1); + const buffer = Buffer.alloc(32, 0); + const bstart = Math.max(0, 32 - x.length); + x.copy(buffer, bstart); + return buffer; } interface ScriptSignature { - signature: Buffer - hashType: number + signature: Buffer; + hashType: number; } // BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) -export function decode (buffer: Buffer): ScriptSignature { - const hashType = buffer.readUInt8(buffer.length - 1) - const hashTypeMod = hashType & ~0x80 - if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) +export function decode(buffer: Buffer): ScriptSignature { + const hashType = buffer.readUInt8(buffer.length - 1); + const hashTypeMod = hashType & ~0x80; + if (hashTypeMod <= 0 || hashTypeMod >= 4) + throw new Error('Invalid hashType ' + hashType); - const decode = bip66.decode(buffer.slice(0, -1)) - const r = fromDER(decode.r) - const s = fromDER(decode.s) + const decode = bip66.decode(buffer.slice(0, -1)); + const r = fromDER(decode.r); + const s = fromDER(decode.s); return { signature: Buffer.concat([r, s], 64), - hashType: hashType - } + hashType: hashType, + }; } -export function encode (signature: Buffer, hashType: number): Buffer { - typeforce({ - signature: types.BufferN(64), - hashType: types.UInt8 - }, { signature, hashType }) +export function encode(signature: Buffer, hashType: number): Buffer { + typeforce( + { + signature: types.BufferN(64), + hashType: types.UInt8, + }, + { signature, hashType }, + ); - const hashTypeMod = hashType & ~0x80 - if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType) + const hashTypeMod = hashType & ~0x80; + if (hashTypeMod <= 0 || hashTypeMod >= 4) + throw new Error('Invalid hashType ' + hashType); - const hashTypeBuffer = Buffer.allocUnsafe(1) - hashTypeBuffer.writeUInt8(hashType, 0) + const hashTypeBuffer = Buffer.allocUnsafe(1); + hashTypeBuffer.writeUInt8(hashType, 0); - const r = toDER(signature.slice(0, 32)) - const s = toDER(signature.slice(32, 64)) + const r = toDER(signature.slice(0, 32)); + const s = toDER(signature.slice(32, 64)); - return Buffer.concat([ - bip66.encode(r, s), - hashTypeBuffer - ]) + return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]); } diff --git a/ts_src/templates/nulldata.ts b/ts_src/templates/nulldata.ts index 0833eac..f0694a3 100644 --- a/ts_src/templates/nulldata.ts +++ b/ts_src/templates/nulldata.ts @@ -1,17 +1,16 @@ // OP_RETURN {data} -import * as bscript from '../script' -const OPS = bscript.OPS +import * as bscript from '../script'; +const OPS = bscript.OPS; -export function check (script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const buffer = bscript.compile(script); - return buffer.length > 1 && - buffer[0] === OPS.OP_RETURN + return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; } -check.toJSON = function () { return 'null data output' } +check.toJSON = function() { + return 'null data output'; +}; -const output = { check } +const output = { check }; -export { - output -} +export { output }; diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index e6e1d24..a456a32 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -1,282 +1,311 @@ -import * as bcrypto from './crypto' -import * as bscript from './script' -import * as types from './types' -import * as bufferutils from './bufferutils' -import { reverseBuffer } from './bufferutils' -import { OPS as opcodes } from './script' +import * as bcrypto from './crypto'; +import * as bscript from './script'; +import * as types from './types'; +import * as bufferutils from './bufferutils'; +import { reverseBuffer } from './bufferutils'; +import { OPS as opcodes } from './script'; -const typeforce = require('typeforce') -const varuint = require('varuint-bitcoin') +const typeforce = require('typeforce'); +const varuint = require('varuint-bitcoin'); -function varSliceSize (someScript: Buffer): number { - const length = someScript.length +function varSliceSize(someScript: Buffer): number { + const length = someScript.length; - return varuint.encodingLength(length) + length + return varuint.encodingLength(length) + length; } -function vectorSize (someVector: Array<Buffer>): number { - const length = someVector.length +function vectorSize(someVector: Array<Buffer>): number { + const length = someVector.length; - return varuint.encodingLength(length) + someVector.reduce((sum, witness) => { - return sum + varSliceSize(witness) - }, 0) + return ( + varuint.encodingLength(length) + + someVector.reduce((sum, witness) => { + return sum + varSliceSize(witness); + }, 0) + ); } -const EMPTY_SCRIPT: Buffer = Buffer.allocUnsafe(0) -const EMPTY_WITNESS: Array<Buffer> = [] -const ZERO: Buffer = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') -const ONE: Buffer = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') -const VALUE_UINT64_MAX: Buffer = Buffer.from('ffffffffffffffff', 'hex') +const EMPTY_SCRIPT: Buffer = Buffer.allocUnsafe(0); +const EMPTY_WITNESS: Array<Buffer> = []; +const ZERO: Buffer = Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000000', + 'hex', +); +const ONE: Buffer = Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000001', + 'hex', +); +const VALUE_UINT64_MAX: Buffer = Buffer.from('ffffffffffffffff', 'hex'); const BLANK_OUTPUT: BlankOutput = { script: EMPTY_SCRIPT, - valueBuffer: VALUE_UINT64_MAX -} + valueBuffer: VALUE_UINT64_MAX, +}; function isOutput(out: Output | BlankOutput): out is Output { - return (<Output>out).value !== undefined + return (<Output>out).value !== undefined; } export type BlankOutput = { - script: Buffer - valueBuffer: Buffer -} + script: Buffer; + valueBuffer: Buffer; +}; export type Output = { - script: Buffer - value: number -} + script: Buffer; + value: number; +}; export type Input = { - hash: Buffer - index: number - script: Buffer - sequence: number - witness: Array<Buffer> -} + hash: Buffer; + index: number; + script: Buffer; + sequence: number; + witness: Array<Buffer>; +}; export class Transaction { - version: number - locktime: number - ins: Array<Input> - outs: Array<Output | BlankOutput> + version: number; + locktime: number; + ins: Array<Input>; + outs: Array<Output | BlankOutput>; - static readonly DEFAULT_SEQUENCE = 0xffffffff - static readonly SIGHASH_ALL = 0x01 - static readonly SIGHASH_NONE = 0x02 - static readonly SIGHASH_SINGLE = 0x03 - static readonly SIGHASH_ANYONECANPAY = 0x80 - static readonly ADVANCED_TRANSACTION_MARKER = 0x00 - static readonly ADVANCED_TRANSACTION_FLAG = 0x01 + static readonly DEFAULT_SEQUENCE = 0xffffffff; + static readonly SIGHASH_ALL = 0x01; + static readonly SIGHASH_NONE = 0x02; + static readonly SIGHASH_SINGLE = 0x03; + static readonly SIGHASH_ANYONECANPAY = 0x80; + static readonly ADVANCED_TRANSACTION_MARKER = 0x00; + static readonly ADVANCED_TRANSACTION_FLAG = 0x01; - constructor () { - this.version = 1 - this.locktime = 0 - this.ins = [] - this.outs = [] + constructor() { + this.version = 1; + this.locktime = 0; + this.ins = []; + this.outs = []; } - static fromBuffer (buffer: Buffer, __noStrict?: boolean): Transaction { - let offset: number = 0 + static fromBuffer(buffer: Buffer, __noStrict?: boolean): Transaction { + let offset: number = 0; - function readSlice (n: number): Buffer { - offset += n - return buffer.slice(offset - n, offset) + function readSlice(n: number): Buffer { + offset += n; + return buffer.slice(offset - n, offset); } - function readUInt32 (): number { - const i = buffer.readUInt32LE(offset) - offset += 4 - return i + function readUInt32(): number { + const i = buffer.readUInt32LE(offset); + offset += 4; + return i; } - function readInt32 (): number { - const i = buffer.readInt32LE(offset) - offset += 4 - return i + function readInt32(): number { + const i = buffer.readInt32LE(offset); + offset += 4; + return i; } - function readUInt64 (): number { - const i = bufferutils.readUInt64LE(buffer, offset) - offset += 8 - return i + function readUInt64(): number { + const i = bufferutils.readUInt64LE(buffer, offset); + offset += 8; + return i; } - function readVarInt (): number { - const vi = varuint.decode(buffer, offset) - offset += varuint.decode.bytes - return vi + function readVarInt(): number { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; } - function readVarSlice (): Buffer { - return readSlice(readVarInt()) + function readVarSlice(): Buffer { + return readSlice(readVarInt()); } - function readVector (): Array<Buffer> { - const count = readVarInt() - const vector: Array<Buffer> = [] - for (var i = 0; i < count; i++) vector.push(readVarSlice()) - return vector + function readVector(): Array<Buffer> { + const count = readVarInt(); + const vector: Array<Buffer> = []; + for (var i = 0; i < count; i++) vector.push(readVarSlice()); + return vector; } - const tx = new Transaction() - tx.version = readInt32() + const tx = new Transaction(); + tx.version = readInt32(); - const marker = buffer.readUInt8(offset) - const flag = buffer.readUInt8(offset + 1) + const marker = buffer.readUInt8(offset); + const flag = buffer.readUInt8(offset + 1); - let hasWitnesses = false - if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && - flag === Transaction.ADVANCED_TRANSACTION_FLAG) { - offset += 2 - hasWitnesses = true + let hasWitnesses = false; + if ( + marker === Transaction.ADVANCED_TRANSACTION_MARKER && + flag === Transaction.ADVANCED_TRANSACTION_FLAG + ) { + offset += 2; + hasWitnesses = true; } - const vinLen = readVarInt() + const vinLen = readVarInt(); for (var i = 0; i < vinLen; ++i) { tx.ins.push({ hash: readSlice(32), index: readUInt32(), script: readVarSlice(), sequence: readUInt32(), - witness: EMPTY_WITNESS - }) + witness: EMPTY_WITNESS, + }); } - const voutLen = readVarInt() + const voutLen = readVarInt(); for (i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), - script: readVarSlice() - }) + script: readVarSlice(), + }); } if (hasWitnesses) { for (i = 0; i < vinLen; ++i) { - tx.ins[i].witness = readVector() + tx.ins[i].witness = readVector(); } // was this pointless? - if (!tx.hasWitnesses()) throw new Error('Transaction has superfluous witness data') + if (!tx.hasWitnesses()) + throw new Error('Transaction has superfluous witness data'); } - tx.locktime = readUInt32() + tx.locktime = readUInt32(); - if (__noStrict) return tx - if (offset !== buffer.length) throw new Error('Transaction has unexpected data') + if (__noStrict) return tx; + if (offset !== buffer.length) + throw new Error('Transaction has unexpected data'); - return tx + return tx; } - static fromHex (hex: string): Transaction { - return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false) + static fromHex(hex: string): Transaction { + return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false); } - static isCoinbaseHash (buffer: Buffer): boolean { - typeforce(types.Hash256bit, buffer) + static isCoinbaseHash(buffer: Buffer): boolean { + typeforce(types.Hash256bit, buffer); for (var i = 0; i < 32; ++i) { - if (buffer[i] !== 0) return false + if (buffer[i] !== 0) return false; } - return true + return true; } - isCoinbase (): boolean { - return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) + isCoinbase(): boolean { + return ( + this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) + ); } - addInput (hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number { - typeforce(types.tuple( - types.Hash256bit, - types.UInt32, - types.maybe(types.UInt32), - types.maybe(types.Buffer) - ), arguments) + addInput( + hash: Buffer, + index: number, + sequence?: number, + scriptSig?: Buffer, + ): number { + typeforce( + types.tuple( + types.Hash256bit, + types.UInt32, + types.maybe(types.UInt32), + types.maybe(types.Buffer), + ), + arguments, + ); if (types.Null(sequence)) { - sequence = Transaction.DEFAULT_SEQUENCE + sequence = Transaction.DEFAULT_SEQUENCE; } // Add the input and return the input's index - return (this.ins.push({ - hash: hash, - index: index, - script: scriptSig || EMPTY_SCRIPT, - sequence: <number>sequence, - witness: EMPTY_WITNESS - }) - 1) + return ( + this.ins.push({ + hash: hash, + index: index, + script: scriptSig || EMPTY_SCRIPT, + sequence: <number>sequence, + witness: EMPTY_WITNESS, + }) - 1 + ); } - addOutput (scriptPubKey: Buffer, value: number): number { - typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) + addOutput(scriptPubKey: Buffer, value: number): number { + typeforce(types.tuple(types.Buffer, types.Satoshi), arguments); // Add the output and return the output's index - return (this.outs.push({ - script: scriptPubKey, - value: value - }) - 1) + return ( + this.outs.push({ + script: scriptPubKey, + value: value, + }) - 1 + ); } - hasWitnesses (): boolean { - return this.ins.some((x) => { - return x.witness.length !== 0 - }) + hasWitnesses(): boolean { + return this.ins.some(x => { + return x.witness.length !== 0; + }); } - weight (): number { - const base = this.__byteLength(false) - const total = this.__byteLength(true) - return base * 3 + total + weight(): number { + const base = this.__byteLength(false); + const total = this.__byteLength(true); + return base * 3 + total; } - virtualSize (): number { - return Math.ceil(this.weight() / 4) + virtualSize(): number { + return Math.ceil(this.weight() / 4); } - byteLength (): number { - return this.__byteLength(true) + byteLength(): number { + return this.__byteLength(true); } - private __byteLength (__allowWitness: boolean): number { - const hasWitnesses = __allowWitness && this.hasWitnesses() + private __byteLength(__allowWitness: boolean): number { + const hasWitnesses = __allowWitness && this.hasWitnesses(); return ( (hasWitnesses ? 10 : 8) + varuint.encodingLength(this.ins.length) + varuint.encodingLength(this.outs.length) + this.ins.reduce((sum, input) => { - return sum + 40 + varSliceSize(input.script) + return sum + 40 + varSliceSize(input.script); }, 0) + this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script) + return sum + 8 + varSliceSize(output.script); }, 0) + - (hasWitnesses ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness) - }, 0) : 0) - ) + (hasWitnesses + ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) + : 0) + ); } - clone (): Transaction { - const newTx = new Transaction() - newTx.version = this.version - newTx.locktime = this.locktime + clone(): Transaction { + const newTx = new Transaction(); + newTx.version = this.version; + newTx.locktime = this.locktime; - newTx.ins = this.ins.map((txIn) => { + newTx.ins = this.ins.map(txIn => { return { hash: txIn.hash, index: txIn.index, script: txIn.script, sequence: txIn.sequence, - witness: txIn.witness - } - }) + witness: txIn.witness, + }; + }); - newTx.outs = this.outs.map((txOut) => { + newTx.outs = this.outs.map(txOut => { return { script: txOut.script, - value: (<Output>txOut).value - } - }) + value: (<Output>txOut).value, + }; + }); - return newTx + return newTx; } /** @@ -287,284 +316,313 @@ export class Transaction { * hashType, and then hashes the result. * This hash can then be used to sign the provided transaction input. */ - hashForSignature (inIndex: number, prevOutScript: Buffer, hashType: number): Buffer { - typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) + hashForSignature( + inIndex: number, + prevOutScript: Buffer, + hashType: number, + ): Buffer { + typeforce( + types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), + arguments, + ); // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 - if (inIndex >= this.ins.length) return ONE + if (inIndex >= this.ins.length) return ONE; // ignore OP_CODESEPARATOR - const ourScript = bscript.compile(bscript.decompile(prevOutScript)!.filter((x) => { - return x !== opcodes.OP_CODESEPARATOR - })) + const ourScript = bscript.compile( + bscript.decompile(prevOutScript)!.filter(x => { + return x !== opcodes.OP_CODESEPARATOR; + }), + ); - const txTmp = this.clone() + const txTmp = this.clone(); // SIGHASH_NONE: ignore all outputs? (wildcard payee) if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { - txTmp.outs = [] + txTmp.outs = []; // ignore sequence numbers (except at inIndex) txTmp.ins.forEach((input, i) => { - if (i === inIndex) return + if (i === inIndex) return; - input.sequence = 0 - }) + input.sequence = 0; + }); // SIGHASH_SINGLE: ignore all outputs, except at the same index? } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 - if (inIndex >= this.outs.length) return ONE + if (inIndex >= this.outs.length) return ONE; // truncate outputs after - txTmp.outs.length = inIndex + 1 + txTmp.outs.length = inIndex + 1; // "blank" outputs before for (var i = 0; i < inIndex; i++) { - txTmp.outs[i] = BLANK_OUTPUT + txTmp.outs[i] = BLANK_OUTPUT; } // ignore sequence numbers (except at inIndex) txTmp.ins.forEach((input, y) => { - if (y === inIndex) return + if (y === inIndex) return; - input.sequence = 0 - }) + input.sequence = 0; + }); } // SIGHASH_ANYONECANPAY: ignore inputs entirely? if (hashType & Transaction.SIGHASH_ANYONECANPAY) { - txTmp.ins = [txTmp.ins[inIndex]] - txTmp.ins[0].script = ourScript + txTmp.ins = [txTmp.ins[inIndex]]; + txTmp.ins[0].script = ourScript; // SIGHASH_ALL: only ignore input scripts } else { // "blank" others input scripts - txTmp.ins.forEach((input) => { - input.script = EMPTY_SCRIPT - }) - txTmp.ins[inIndex].script = ourScript + txTmp.ins.forEach(input => { + input.script = EMPTY_SCRIPT; + }); + txTmp.ins[inIndex].script = ourScript; } // serialize and hash - const buffer: Buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) - buffer.writeInt32LE(hashType, buffer.length - 4) - txTmp.__toBuffer(buffer, 0, false) + const buffer: Buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4); + buffer.writeInt32LE(hashType, buffer.length - 4); + txTmp.__toBuffer(buffer, 0, false); - return bcrypto.hash256(buffer) + return bcrypto.hash256(buffer); } - hashForWitnessV0 (inIndex: number, prevOutScript: Buffer, value: number, hashType: number): Buffer { - typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) + hashForWitnessV0( + inIndex: number, + prevOutScript: Buffer, + value: number, + hashType: number, + ): Buffer { + typeforce( + types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), + arguments, + ); - let tbuffer: Buffer = Buffer.from([]) - let toffset: number = 0 + let tbuffer: Buffer = Buffer.from([]); + let toffset: number = 0; - function writeSlice (slice: Buffer): void { - toffset += slice.copy(tbuffer, toffset) + function writeSlice(slice: Buffer): void { + toffset += slice.copy(tbuffer, toffset); } - function writeUInt32 (i: number): void { - toffset = tbuffer.writeUInt32LE(i, toffset) + function writeUInt32(i: number): void { + toffset = tbuffer.writeUInt32LE(i, toffset); } - function writeUInt64 (i: number): void { - toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) + function writeUInt64(i: number): void { + toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset); } - function writeVarInt (i: number): void { - varuint.encode(i, tbuffer, toffset) - toffset += varuint.encode.bytes + function writeVarInt(i: number): void { + varuint.encode(i, tbuffer, toffset); + toffset += varuint.encode.bytes; } - function writeVarSlice (slice: Buffer): void { - writeVarInt(slice.length) - writeSlice(slice) + function writeVarSlice(slice: Buffer): void { + writeVarInt(slice.length); + writeSlice(slice); } - let hashOutputs = ZERO - let hashPrevouts = ZERO - let hashSequence = ZERO + let hashOutputs = ZERO; + let hashPrevouts = ZERO; + let hashSequence = ZERO; if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { - tbuffer = Buffer.allocUnsafe(36 * this.ins.length) - toffset = 0 + tbuffer = Buffer.allocUnsafe(36 * this.ins.length); + toffset = 0; - this.ins.forEach((txIn) => { - writeSlice(txIn.hash) - writeUInt32(txIn.index) - }) + this.ins.forEach(txIn => { + writeSlice(txIn.hash); + writeUInt32(txIn.index); + }); - hashPrevouts = bcrypto.hash256(tbuffer) + hashPrevouts = bcrypto.hash256(tbuffer); } - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && + if ( + !(hashType & Transaction.SIGHASH_ANYONECANPAY) && (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - tbuffer = Buffer.allocUnsafe(4 * this.ins.length) - toffset = 0 + (hashType & 0x1f) !== Transaction.SIGHASH_NONE + ) { + tbuffer = Buffer.allocUnsafe(4 * this.ins.length); + toffset = 0; - this.ins.forEach((txIn) => { - writeUInt32(txIn.sequence) - }) + this.ins.forEach(txIn => { + writeUInt32(txIn.sequence); + }); - hashSequence = bcrypto.hash256(tbuffer) + hashSequence = bcrypto.hash256(tbuffer); } - if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + if ( + (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE + ) { const txOutsSize = this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script) - }, 0) + return sum + 8 + varSliceSize(output.script); + }, 0); - tbuffer = Buffer.allocUnsafe(txOutsSize) - toffset = 0 + tbuffer = Buffer.allocUnsafe(txOutsSize); + toffset = 0; - this.outs.forEach((out) => { - writeUInt64((<Output>out).value) - writeVarSlice(out.script) - }) + this.outs.forEach(out => { + writeUInt64((<Output>out).value); + writeVarSlice(out.script); + }); - hashOutputs = bcrypto.hash256(tbuffer) - } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { - const output = this.outs[inIndex] + hashOutputs = bcrypto.hash256(tbuffer); + } else if ( + (hashType & 0x1f) === Transaction.SIGHASH_SINGLE && + inIndex < this.outs.length + ) { + const output = this.outs[inIndex]; - tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) - toffset = 0 - writeUInt64((<Output>output).value) - writeVarSlice(output.script) + tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); + toffset = 0; + writeUInt64((<Output>output).value); + writeVarSlice(output.script); - hashOutputs = bcrypto.hash256(tbuffer) + hashOutputs = bcrypto.hash256(tbuffer); } - tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) - toffset = 0 + tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)); + toffset = 0; - const input = this.ins[inIndex] - writeUInt32(this.version) - writeSlice(hashPrevouts) - writeSlice(hashSequence) - writeSlice(input.hash) - writeUInt32(input.index) - writeVarSlice(prevOutScript) - writeUInt64(value) - writeUInt32(input.sequence) - writeSlice(hashOutputs) - writeUInt32(this.locktime) - writeUInt32(hashType) - return bcrypto.hash256(tbuffer) + const input = this.ins[inIndex]; + writeUInt32(this.version); + writeSlice(hashPrevouts); + writeSlice(hashSequence); + writeSlice(input.hash); + writeUInt32(input.index); + writeVarSlice(prevOutScript); + writeUInt64(value); + writeUInt32(input.sequence); + writeSlice(hashOutputs); + writeUInt32(this.locktime); + writeUInt32(hashType); + return bcrypto.hash256(tbuffer); } - getHash (forWitness?: boolean): Buffer { + getHash(forWitness?: boolean): Buffer { // wtxid for coinbase is always 32 bytes of 0x00 - if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0) - return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness)) + if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0); + return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness)); } - getId (): string { + getId(): string { // transaction hash's are displayed in reverse order - return reverseBuffer(this.getHash(false)).toString('hex') + return reverseBuffer(this.getHash(false)).toString('hex'); } - toBuffer (buffer?: Buffer, initialOffset?: number): Buffer { - return this.__toBuffer(buffer, initialOffset, true) + toBuffer(buffer?: Buffer, initialOffset?: number): Buffer { + return this.__toBuffer(buffer, initialOffset, true); } - private __toBuffer (buffer?: Buffer, initialOffset?: number, __allowWitness?: boolean): Buffer { - if (!buffer) buffer = <Buffer> Buffer.allocUnsafe(this.__byteLength(__allowWitness!)) + private __toBuffer( + buffer?: Buffer, + initialOffset?: number, + __allowWitness?: boolean, + ): Buffer { + if (!buffer) + buffer = <Buffer>Buffer.allocUnsafe(this.__byteLength(__allowWitness!)); - let offset = initialOffset || 0 + let offset = initialOffset || 0; - function writeSlice (slice: Buffer): void { - offset += slice.copy(buffer!, offset) + function writeSlice(slice: Buffer): void { + offset += slice.copy(buffer!, offset); } - function writeUInt8 (i: number) { - offset = (buffer!).writeUInt8(i, offset) + function writeUInt8(i: number) { + offset = buffer!.writeUInt8(i, offset); } - function writeUInt32 (i: number) { - offset = (buffer!).writeUInt32LE(i, offset) + function writeUInt32(i: number) { + offset = buffer!.writeUInt32LE(i, offset); } - function writeInt32 (i: number) { - offset = (buffer!).writeInt32LE(i, offset) + function writeInt32(i: number) { + offset = buffer!.writeInt32LE(i, offset); } - function writeUInt64 (i: number) { - offset = bufferutils.writeUInt64LE(buffer!, i, offset) + function writeUInt64(i: number) { + offset = bufferutils.writeUInt64LE(buffer!, i, offset); } - function writeVarInt (i: number) { - varuint.encode(i, buffer, offset) - offset += varuint.encode.bytes + function writeVarInt(i: number) { + varuint.encode(i, buffer, offset); + offset += varuint.encode.bytes; } - function writeVarSlice (slice: Buffer) { - writeVarInt(slice.length) - writeSlice(slice) + function writeVarSlice(slice: Buffer) { + writeVarInt(slice.length); + writeSlice(slice); } - function writeVector (vector: Array<Buffer>) { - writeVarInt(vector.length) - vector.forEach(writeVarSlice) + function writeVector(vector: Array<Buffer>) { + writeVarInt(vector.length); + vector.forEach(writeVarSlice); } - writeInt32(this.version) + writeInt32(this.version); - const hasWitnesses = __allowWitness && this.hasWitnesses() + const hasWitnesses = __allowWitness && this.hasWitnesses(); if (hasWitnesses) { - writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) - writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) + writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); + writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); } - writeVarInt(this.ins.length) + writeVarInt(this.ins.length); - this.ins.forEach((txIn) => { - writeSlice(txIn.hash) - writeUInt32(txIn.index) - writeVarSlice(txIn.script) - writeUInt32(txIn.sequence) - }) + this.ins.forEach(txIn => { + writeSlice(txIn.hash); + writeUInt32(txIn.index); + writeVarSlice(txIn.script); + writeUInt32(txIn.sequence); + }); - writeVarInt(this.outs.length) - this.outs.forEach((txOut) => { + writeVarInt(this.outs.length); + this.outs.forEach(txOut => { if (isOutput(txOut)) { - writeUInt64(txOut.value) + writeUInt64(txOut.value); } else { - writeSlice(txOut.valueBuffer) + writeSlice(txOut.valueBuffer); } - writeVarSlice(txOut.script) - }) + writeVarSlice(txOut.script); + }); if (hasWitnesses) { - this.ins.forEach((input) => { - writeVector(input.witness) - }) + this.ins.forEach(input => { + writeVector(input.witness); + }); } - writeUInt32(this.locktime) + writeUInt32(this.locktime); // avoid slicing unless necessary - if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) - return buffer + if (initialOffset !== undefined) return buffer.slice(initialOffset, offset); + return buffer; } - toHex () { - return this.toBuffer(undefined, undefined).toString('hex') + toHex() { + return this.toBuffer(undefined, undefined).toString('hex'); } - setInputScript (index: number, scriptSig: Buffer) { - typeforce(types.tuple(types.Number, types.Buffer), arguments) + setInputScript(index: number, scriptSig: Buffer) { + typeforce(types.tuple(types.Number, types.Buffer), arguments); - this.ins[index].script = scriptSig + this.ins[index].script = scriptSig; } - setWitness (index: number, witness: Array<Buffer>) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments) + setWitness(index: number, witness: Array<Buffer>) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments); - this.ins[index].witness = witness + this.ins[index].witness = witness; } } diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 969462c..a3bd9e6 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -1,458 +1,528 @@ -import { Network } from './networks' -import * as networks from './networks' -import { reverseBuffer } from './bufferutils' -import { Transaction, Output } from './transaction' -import { ECPairInterface } from './ecpair' -import * as ECPair from './ecpair' -import * as types from './types' -import * as baddress from './address' -import * as bcrypto from './crypto' -import * as bscript from './script' -import { Payment } from './payments' -import * as payments from './payments' -import * as classify from './classify' -import { OPS as ops } from './script' -const typeforce = require('typeforce') +import { Network } from './networks'; +import * as networks from './networks'; +import { reverseBuffer } from './bufferutils'; +import { Transaction, Output } from './transaction'; +import { ECPairInterface } from './ecpair'; +import * as ECPair from './ecpair'; +import * as types from './types'; +import * as baddress from './address'; +import * as bcrypto from './crypto'; +import * as bscript from './script'; +import { Payment } from './payments'; +import * as payments from './payments'; +import * as classify from './classify'; +import { OPS as ops } from './script'; +const typeforce = require('typeforce'); -const SCRIPT_TYPES = classify.types +const SCRIPT_TYPES = classify.types; -type TxbSignatures = Array<Buffer> | Array<Buffer | undefined> -type TxbPubkeys = Array<Buffer | undefined> -type TxbWitness = Array<Buffer> -type TxbScriptType = string -type TxbScript = Buffer +type TxbSignatures = Array<Buffer> | Array<Buffer | undefined>; +type TxbPubkeys = Array<Buffer | undefined>; +type TxbWitness = Array<Buffer>; +type TxbScriptType = string; +type TxbScript = Buffer; interface TxbInput { - value?: number - hasWitness?: boolean - signScript?: TxbScript - signType?: TxbScriptType - prevOutScript?: TxbScript - redeemScript?: TxbScript - redeemScriptType?: TxbScriptType - prevOutType?: TxbScriptType - pubkeys?: TxbPubkeys - signatures?: TxbSignatures - witness?: TxbWitness - witnessScript?: TxbScript - witnessScriptType?: TxbScriptType - script?: TxbScript - sequence?: number - scriptSig?: TxbScript - maxSignatures?: number + value?: number; + hasWitness?: boolean; + signScript?: TxbScript; + signType?: TxbScriptType; + prevOutScript?: TxbScript; + redeemScript?: TxbScript; + redeemScriptType?: TxbScriptType; + prevOutType?: TxbScriptType; + pubkeys?: TxbPubkeys; + signatures?: TxbSignatures; + witness?: TxbWitness; + witnessScript?: TxbScript; + witnessScriptType?: TxbScriptType; + script?: TxbScript; + sequence?: number; + scriptSig?: TxbScript; + maxSignatures?: number; } interface TxbOutput { - type: string - pubkeys?: TxbPubkeys - signatures?: TxbSignatures - maxSignatures?: number + type: string; + pubkeys?: TxbPubkeys; + signatures?: TxbSignatures; + maxSignatures?: number; } function txIsString(tx: Buffer | string | Transaction): tx is string { - return typeof tx === 'string' || tx instanceof String + return typeof tx === 'string' || tx instanceof String; } function txIsTransaction(tx: Buffer | string | Transaction): tx is Transaction { - return tx instanceof Transaction + return tx instanceof Transaction; } export class TransactionBuilder { - network: Network - maximumFeeRate: number - private __prevTxSet: { [index: string]: boolean } - private __inputs: Array<TxbInput> - private __tx: Transaction + network: Network; + maximumFeeRate: number; + private __prevTxSet: { [index: string]: boolean }; + private __inputs: Array<TxbInput>; + private __tx: Transaction; - constructor (network?: Network, maximumFeeRate?: number) { - this.__prevTxSet = {} - this.network = network || networks.bitcoin + constructor(network?: Network, maximumFeeRate?: number) { + this.__prevTxSet = {}; + this.network = network || networks.bitcoin; // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) - this.maximumFeeRate = maximumFeeRate || 2500 + this.maximumFeeRate = maximumFeeRate || 2500; - this.__inputs = [] - this.__tx = new Transaction() - this.__tx.version = 2 + this.__inputs = []; + this.__tx = new Transaction(); + this.__tx.version = 2; } - static fromTransaction (transaction: Transaction, network?: Network): TransactionBuilder { - const txb = new TransactionBuilder(network) + static fromTransaction( + transaction: Transaction, + network?: Network, + ): TransactionBuilder { + const txb = new TransactionBuilder(network); // Copy transaction fields - txb.setVersion(transaction.version) - txb.setLockTime(transaction.locktime) + txb.setVersion(transaction.version); + txb.setLockTime(transaction.locktime); // Copy outputs (done first to avoid signature invalidation) transaction.outs.forEach(txOut => { - txb.addOutput(txOut.script, (<Output>txOut).value) - }) + txb.addOutput(txOut.script, (<Output>txOut).value); + }); // Copy inputs transaction.ins.forEach(txIn => { txb.__addInputUnsafe(txIn.hash, txIn.index, { sequence: txIn.sequence, script: txIn.script, - witness: txIn.witness - }) - }) + witness: txIn.witness, + }); + }); // fix some things not possible through the public API txb.__inputs.forEach((input, i) => { - fixMultisigOrder(input, transaction, i) - }) + fixMultisigOrder(input, transaction, i); + }); - return txb + return txb; } - setLockTime (locktime: number): void { - typeforce(types.UInt32, locktime) + setLockTime(locktime: number): void { + typeforce(types.UInt32, locktime); // if any signatures exist, throw - if (this.__inputs.some(input => { - if (!input.signatures) return false + if ( + this.__inputs.some(input => { + if (!input.signatures) return false; - return input.signatures.some(s => s !== undefined) - })) { - throw new Error('No, this would invalidate signatures') + return input.signatures.some(s => s !== undefined); + }) + ) { + throw new Error('No, this would invalidate signatures'); } - this.__tx.locktime = locktime + this.__tx.locktime = locktime; } - setVersion (version: number): void { - typeforce(types.UInt32, version) + setVersion(version: number): void { + typeforce(types.UInt32, version); // XXX: this might eventually become more complex depending on what the versions represent - this.__tx.version = version + this.__tx.version = version; } - addInput (txHash: Buffer | string | Transaction, vout: number, sequence: number, prevOutScript: Buffer): number { + addInput( + txHash: Buffer | string | Transaction, + vout: number, + sequence: number, + prevOutScript: Buffer, + ): number { if (!this.__canModifyInputs()) { - throw new Error('No, this would invalidate signatures') + throw new Error('No, this would invalidate signatures'); } - let value: number | undefined = undefined + let value: number | undefined = undefined; // is it a hex string? if (txIsString(txHash)) { // transaction hashs's are displayed in reverse order, un-reverse it - txHash = reverseBuffer(Buffer.from(txHash, 'hex')) + txHash = reverseBuffer(Buffer.from(txHash, 'hex')); - // is it a Transaction object? + // is it a Transaction object? } else if (txIsTransaction(txHash)) { - const txOut = txHash.outs[vout] - prevOutScript = txOut.script - value = (<Output>txOut).value + const txOut = txHash.outs[vout]; + prevOutScript = txOut.script; + value = (<Output>txOut).value; - txHash = <Buffer> txHash.getHash(false) + txHash = <Buffer>txHash.getHash(false); } return this.__addInputUnsafe(txHash, vout, { sequence: sequence, prevOutScript: prevOutScript, - value: value - }) + value: value, + }); } - private __addInputUnsafe (txHash: Buffer, vout: number, options: TxbInput): number { + private __addInputUnsafe( + txHash: Buffer, + vout: number, + options: TxbInput, + ): number { if (Transaction.isCoinbaseHash(txHash)) { - throw new Error('coinbase inputs not supported') + throw new Error('coinbase inputs not supported'); } - const prevTxOut = txHash.toString('hex') + ':' + vout - if (this.__prevTxSet[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut) + const prevTxOut = txHash.toString('hex') + ':' + vout; + if (this.__prevTxSet[prevTxOut] !== undefined) + throw new Error('Duplicate TxOut: ' + prevTxOut); - let input = <TxbInput>{} + let input = <TxbInput>{}; // derive what we can from the scriptSig if (options.script !== undefined) { - input = expandInput(options.script, options.witness || []) + input = expandInput(options.script, options.witness || []); } // if an input value was given, retain it if (options.value !== undefined) { - input.value = options.value + input.value = options.value; } // derive what we can from the previous transactions output script if (!input.prevOutScript && options.prevOutScript) { - let prevOutType + let prevOutType; if (!input.pubkeys && !input.signatures) { - const expanded = expandOutput(options.prevOutScript) + const expanded = expandOutput(options.prevOutScript); if (expanded.pubkeys) { - input.pubkeys = expanded.pubkeys - input.signatures = expanded.signatures + input.pubkeys = expanded.pubkeys; + input.signatures = expanded.signatures; } - prevOutType = expanded.type + prevOutType = expanded.type; } - input.prevOutScript = options.prevOutScript - input.prevOutType = prevOutType || classify.output(options.prevOutScript) + input.prevOutScript = options.prevOutScript; + input.prevOutType = prevOutType || classify.output(options.prevOutScript); } - const vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig) - this.__inputs[vin] = input - this.__prevTxSet[prevTxOut] = true - return vin + const vin = this.__tx.addInput( + txHash, + vout, + options.sequence, + options.scriptSig, + ); + this.__inputs[vin] = input; + this.__prevTxSet[prevTxOut] = true; + return vin; } - addOutput (scriptPubKey: string | Buffer, value: number): number { + addOutput(scriptPubKey: string | Buffer, value: number): number { if (!this.__canModifyOutputs()) { - throw new Error('No, this would invalidate signatures') + throw new Error('No, this would invalidate signatures'); } // Attempt to get a script if it's a base58 or bech32 address string if (typeof scriptPubKey === 'string') { - scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network) + scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); } - return this.__tx.addOutput(scriptPubKey, value) + return this.__tx.addOutput(scriptPubKey, value); } - build (): Transaction { - return this.__build(false) + build(): Transaction { + return this.__build(false); } - buildIncomplete (): Transaction { - return this.__build(true) + buildIncomplete(): Transaction { + return this.__build(true); } - private __build (allowIncomplete?: boolean): Transaction { + private __build(allowIncomplete?: boolean): Transaction { if (!allowIncomplete) { - if (!this.__tx.ins.length) throw new Error('Transaction has no inputs') - if (!this.__tx.outs.length) throw new Error('Transaction has no outputs') + if (!this.__tx.ins.length) throw new Error('Transaction has no inputs'); + if (!this.__tx.outs.length) throw new Error('Transaction has no outputs'); } - const tx = this.__tx.clone() + const tx = this.__tx.clone(); // create script signatures from inputs this.__inputs.forEach((input, i) => { - if (!input.prevOutType && !allowIncomplete) throw new Error('Transaction is not complete') + if (!input.prevOutType && !allowIncomplete) + throw new Error('Transaction is not complete'); - const result = build(input.prevOutType!, input, allowIncomplete) + const result = build(input.prevOutType!, input, allowIncomplete); if (!result) { - if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) throw new Error('Unknown input type') - if (!allowIncomplete) throw new Error('Not enough information') - return + if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) + throw new Error('Unknown input type'); + if (!allowIncomplete) throw new Error('Not enough information'); + return; } - tx.setInputScript(i, result.input!) - tx.setWitness(i, result.witness!) - }) + tx.setInputScript(i, result.input!); + tx.setWitness(i, result.witness!); + }); if (!allowIncomplete) { // do not rely on this, its merely a last resort if (this.__overMaximumFees(tx.virtualSize())) { - throw new Error('Transaction has absurd fees') + throw new Error('Transaction has absurd fees'); } } - return tx + return tx; } - sign (vin: number, keyPair: ECPairInterface, redeemScript: Buffer, hashType: number, witnessValue: number, witnessScript: Buffer) { + sign( + vin: number, + keyPair: ECPairInterface, + redeemScript: Buffer, + hashType: number, + witnessValue: number, + witnessScript: Buffer, + ) { // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network') - if (!this.__inputs[vin]) throw new Error('No input at index: ' + vin) + if (keyPair.network && keyPair.network !== this.network) + throw new TypeError('Inconsistent network'); + if (!this.__inputs[vin]) throw new Error('No input at index: ' + vin); - hashType = hashType || Transaction.SIGHASH_ALL - if (this.__needsOutputs(hashType)) throw new Error('Transaction needs outputs') + hashType = hashType || Transaction.SIGHASH_ALL; + if (this.__needsOutputs(hashType)) + throw new Error('Transaction needs outputs'); - const input = this.__inputs[vin] + const input = this.__inputs[vin]; // if redeemScript was previously provided, enforce consistency - if (input.redeemScript !== undefined && - redeemScript && - !input.redeemScript.equals(redeemScript)) { - throw new Error('Inconsistent redeemScript') + if ( + input.redeemScript !== undefined && + redeemScript && + !input.redeemScript.equals(redeemScript) + ) { + throw new Error('Inconsistent redeemScript'); } - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!() + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!(); if (!canSign(input)) { if (witnessValue !== undefined) { - if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue') - typeforce(types.Satoshi, witnessValue) - input.value = witnessValue + if (input.value !== undefined && input.value !== witnessValue) + throw new Error("Input didn't match witnessValue"); + typeforce(types.Satoshi, witnessValue); + input.value = witnessValue; } if (!canSign(input)) { - const prepared = prepareInput(input, ourPubKey, redeemScript, witnessScript) + const prepared = prepareInput( + input, + ourPubKey, + redeemScript, + witnessScript, + ); // updates inline - Object.assign(input, prepared) + Object.assign(input, prepared); } - if (!canSign(input)) throw Error(input.prevOutType + ' not supported') + if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); } // ready to sign - let signatureHash: Buffer + let signatureHash: Buffer; if (input.hasWitness) { - signatureHash = this.__tx.hashForWitnessV0(vin, <Buffer>input.signScript, <number>input.value, hashType) + signatureHash = this.__tx.hashForWitnessV0( + vin, + <Buffer>input.signScript, + <number>input.value, + hashType, + ); } else { - signatureHash = this.__tx.hashForSignature(vin, <Buffer>input.signScript, hashType) + signatureHash = this.__tx.hashForSignature( + vin, + <Buffer>input.signScript, + hashType, + ); } // enforce in order signing of public keys const signed = input.pubkeys!.some((pubKey, i) => { - if (!ourPubKey.equals(pubKey!)) return false - if (input.signatures![i]) throw new Error('Signature already exists') + if (!ourPubKey.equals(pubKey!)) return false; + if (input.signatures![i]) throw new Error('Signature already exists'); // TODO: add tests if (ourPubKey.length !== 33 && input.hasWitness) { - throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH') + throw new Error( + 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', + ); } - const signature = keyPair.sign(signatureHash) - input.signatures![i] = bscript.signature.encode(signature, hashType) - return true - }) + const signature = keyPair.sign(signatureHash); + input.signatures![i] = bscript.signature.encode(signature, hashType); + return true; + }); - if (!signed) throw new Error('Key pair cannot sign for this input') + if (!signed) throw new Error('Key pair cannot sign for this input'); } - private __canModifyInputs (): boolean { + private __canModifyInputs(): boolean { return this.__inputs.every(input => { - if (!input.signatures) return true + if (!input.signatures) return true; return input.signatures.every(signature => { - if (!signature) return true - const hashType = signatureHashType(signature) + if (!signature) return true; + const hashType = signatureHashType(signature); // if SIGHASH_ANYONECANPAY is set, signatures would not // be invalidated by more inputs - return (hashType & Transaction.SIGHASH_ANYONECANPAY) !== 0 - }) - }) + return (hashType & Transaction.SIGHASH_ANYONECANPAY) !== 0; + }); + }); } - private __needsOutputs (signingHashType: number): boolean { + private __needsOutputs(signingHashType: number): boolean { if (signingHashType === Transaction.SIGHASH_ALL) { - return this.__tx.outs.length === 0 + return this.__tx.outs.length === 0; } // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs // .build() will fail, but .buildIncomplete() is OK - return (this.__tx.outs.length === 0) && this.__inputs.some((input) => { - if (!input.signatures) return false + return ( + this.__tx.outs.length === 0 && + this.__inputs.some(input => { + if (!input.signatures) return false; - return input.signatures.some((signature) => { - if (!signature) return false // no signature, no issue - const hashType = signatureHashType(signature) - if (hashType & Transaction.SIGHASH_NONE) return false // SIGHASH_NONE doesn't care about outputs - return true // SIGHASH_* does care + return input.signatures.some(signature => { + if (!signature) return false; // no signature, no issue + const hashType = signatureHashType(signature); + if (hashType & Transaction.SIGHASH_NONE) return false; // SIGHASH_NONE doesn't care about outputs + return true; // SIGHASH_* does care + }); }) - }) + ); } - private __canModifyOutputs (): boolean { - const nInputs = this.__tx.ins.length - const nOutputs = this.__tx.outs.length + private __canModifyOutputs(): boolean { + const nInputs = this.__tx.ins.length; + const nOutputs = this.__tx.outs.length; return this.__inputs.every(input => { - if (input.signatures === undefined) return true + if (input.signatures === undefined) return true; return input.signatures.every(signature => { - if (!signature) return true - const hashType = signatureHashType(signature) + if (!signature) return true; + const hashType = signatureHashType(signature); - const hashTypeMod = hashType & 0x1f - if (hashTypeMod === Transaction.SIGHASH_NONE) return true + const hashTypeMod = hashType & 0x1f; + if (hashTypeMod === Transaction.SIGHASH_NONE) return true; if (hashTypeMod === Transaction.SIGHASH_SINGLE) { // if SIGHASH_SINGLE is set, and nInputs > nOutputs // some signatures would be invalidated by the addition // of more outputs - return nInputs <= nOutputs + return nInputs <= nOutputs; } - return false - }) - }) + return false; + }); + }); } - private __overMaximumFees (bytes: number): boolean { + private __overMaximumFees(bytes: number): boolean { // not all inputs will have .value defined - const incoming = this.__inputs.reduce((a, x) => a + (x.value! >>> 0), 0) + const incoming = this.__inputs.reduce((a, x) => a + (x.value! >>> 0), 0); // but all outputs do, and if we have any input value // we can immediately determine if the outputs are too small - const outgoing = this.__tx.outs.reduce((a, x) => a + (<Output>x).value, 0) - const fee = incoming - outgoing - const feeRate = fee / bytes + const outgoing = this.__tx.outs.reduce((a, x) => a + (<Output>x).value, 0); + const fee = incoming - outgoing; + const feeRate = fee / bytes; - return feeRate > this.maximumFeeRate + return feeRate > this.maximumFeeRate; } } -function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: string, scriptPubKey?: Buffer): TxbInput { - if (scriptSig.length === 0 && witnessStack.length === 0) return {} +function expandInput( + scriptSig: Buffer, + witnessStack: Array<Buffer>, + type?: string, + scriptPubKey?: Buffer, +): TxbInput { + if (scriptSig.length === 0 && witnessStack.length === 0) return {}; if (!type) { - let ssType: string | undefined = classify.input(scriptSig, true) - let wsType: string | undefined = classify.witness(witnessStack, true) - if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined - if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined - type = ssType || wsType + let ssType: string | undefined = classify.input(scriptSig, true); + let wsType: string | undefined = classify.witness(witnessStack, true); + if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined; + if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined; + type = ssType || wsType; } switch (type) { case SCRIPT_TYPES.P2WPKH: { - const { output, pubkey, signature } = payments.p2wpkh({ witness: witnessStack }) + const { output, pubkey, signature } = payments.p2wpkh({ + witness: witnessStack, + }); return { prevOutScript: output, prevOutType: SCRIPT_TYPES.P2WPKH, pubkeys: [pubkey], - signatures: [signature] - } + signatures: [signature], + }; } case SCRIPT_TYPES.P2PKH: { - const { output, pubkey, signature } = payments.p2pkh({ input: scriptSig }) + const { output, pubkey, signature } = payments.p2pkh({ + input: scriptSig, + }); return { prevOutScript: output, prevOutType: SCRIPT_TYPES.P2PKH, pubkeys: [pubkey], - signatures: [signature] - } + signatures: [signature], + }; } case SCRIPT_TYPES.P2PK: { - const { signature } = payments.p2pk({ input: scriptSig }) + const { signature } = payments.p2pk({ input: scriptSig }); return { prevOutType: SCRIPT_TYPES.P2PK, pubkeys: [undefined], - signatures: [signature] - } + signatures: [signature], + }; } case SCRIPT_TYPES.P2MS: { - const { m, pubkeys, signatures } = payments.p2ms({ - input: scriptSig, - output: scriptPubKey - }, { allowIncomplete: true }) + const { m, pubkeys, signatures } = payments.p2ms( + { + input: scriptSig, + output: scriptPubKey, + }, + { allowIncomplete: true }, + ); return { prevOutType: SCRIPT_TYPES.P2MS, pubkeys: pubkeys, signatures: signatures, - maxSignatures: m - } + maxSignatures: m, + }; } } if (type === SCRIPT_TYPES.P2SH) { const { output, redeem } = payments.p2sh({ input: scriptSig, - witness: witnessStack - }) + witness: witnessStack, + }); - const outputType = classify.output(redeem!.output!) - const expanded = expandInput(redeem!.input!, redeem!.witness!, outputType, redeem!.output) - if (!expanded.prevOutType) return {} + const outputType = classify.output(redeem!.output!); + const expanded = expandInput( + redeem!.input!, + redeem!.witness!, + outputType, + redeem!.output, + ); + if (!expanded.prevOutType) return {}; return { prevOutScript: output, @@ -463,23 +533,28 @@ function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: str witnessScriptType: expanded.witnessScriptType, pubkeys: expanded.pubkeys, - signatures: expanded.signatures - } + signatures: expanded.signatures, + }; } if (type === SCRIPT_TYPES.P2WSH) { const { output, redeem } = payments.p2wsh({ input: scriptSig, - witness: witnessStack - }) - const outputType = classify.output(redeem!.output!) - let expanded + witness: witnessStack, + }); + const outputType = classify.output(redeem!.output!); + let expanded; if (outputType === SCRIPT_TYPES.P2WPKH) { - expanded = expandInput(redeem!.input!, redeem!.witness!, outputType) + expanded = expandInput(redeem!.input!, redeem!.witness!, outputType); } else { - expanded = expandInput(bscript.compile(redeem!.witness!), [], outputType, redeem!.output) + expanded = expandInput( + bscript.compile(redeem!.witness!), + [], + outputType, + redeem!.output, + ); } - if (!expanded.prevOutType) return {} + if (!expanded.prevOutType) return {}; return { prevOutScript: output, @@ -488,127 +563,152 @@ function expandInput (scriptSig: Buffer, witnessStack: Array<Buffer>, type?: str witnessScriptType: expanded.prevOutType, pubkeys: expanded.pubkeys, - signatures: expanded.signatures - } + signatures: expanded.signatures, + }; } return { prevOutType: SCRIPT_TYPES.NONSTANDARD, - prevOutScript: scriptSig - } + prevOutScript: scriptSig, + }; } // could be done in expandInput, but requires the original Transaction for hashForSignature -function fixMultisigOrder (input: TxbInput, transaction: Transaction, vin: number): void { - if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) return - if (input.pubkeys!.length === input.signatures!.length) return +function fixMultisigOrder( + input: TxbInput, + transaction: Transaction, + vin: number, +): void { + if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) + return; + if (input.pubkeys!.length === input.signatures!.length) return; - const unmatched = input.signatures!.concat() + const unmatched = input.signatures!.concat(); input.signatures = input.pubkeys!.map(pubKey => { - const keyPair = ECPair.fromPublicKey(pubKey!) - let match: Buffer | undefined + const keyPair = ECPair.fromPublicKey(pubKey!); + let match: Buffer | undefined; // check for a signature unmatched.some((signature, i) => { // skip if undefined || OP_0 - if (!signature) return false + if (!signature) return false; // TODO: avoid O(n) hashForSignature - const parsed = bscript.signature.decode(signature) - const hash = transaction.hashForSignature(vin, input.redeemScript!, parsed.hashType) + const parsed = bscript.signature.decode(signature); + const hash = transaction.hashForSignature( + vin, + input.redeemScript!, + parsed.hashType, + ); // skip if signature does not match pubKey - if (!keyPair.verify(hash, parsed.signature)) return false + if (!keyPair.verify(hash, parsed.signature)) return false; // remove matched signature from unmatched - unmatched[i] = undefined - match = signature + unmatched[i] = undefined; + match = signature; - return true - }) + return true; + }); - return match - }) + return match; + }); } -function expandOutput (script: Buffer, ourPubKey?: Buffer): TxbOutput { - typeforce(types.Buffer, script) - const type = classify.output(script) +function expandOutput(script: Buffer, ourPubKey?: Buffer): TxbOutput { + typeforce(types.Buffer, script); + const type = classify.output(script); switch (type) { case SCRIPT_TYPES.P2PKH: { - if (!ourPubKey) return { type } + if (!ourPubKey) return { type }; // does our hash160(pubKey) match the output scripts? - const pkh1 = payments.p2pkh({ output: script }).hash - const pkh2 = bcrypto.hash160(ourPubKey) - if (!pkh1!.equals(pkh2)) return { type } + const pkh1 = payments.p2pkh({ output: script }).hash; + const pkh2 = bcrypto.hash160(ourPubKey); + if (!pkh1!.equals(pkh2)) return { type }; return { type, pubkeys: [ourPubKey], - signatures: [undefined] - } + signatures: [undefined], + }; } case SCRIPT_TYPES.P2WPKH: { - if (!ourPubKey) return { type } + if (!ourPubKey) return { type }; // does our hash160(pubKey) match the output scripts? - const wpkh1 = payments.p2wpkh({ output: script }).hash - const wpkh2 = bcrypto.hash160(ourPubKey) - if (!wpkh1!.equals(wpkh2)) return { type } + const wpkh1 = payments.p2wpkh({ output: script }).hash; + const wpkh2 = bcrypto.hash160(ourPubKey); + if (!wpkh1!.equals(wpkh2)) return { type }; return { type, pubkeys: [ourPubKey], - signatures: [undefined] - } + signatures: [undefined], + }; } case SCRIPT_TYPES.P2PK: { - const p2pk = payments.p2pk({ output: script }) + const p2pk = payments.p2pk({ output: script }); return { type, pubkeys: [p2pk.pubkey], - signatures: [undefined] - } + signatures: [undefined], + }; } case SCRIPT_TYPES.P2MS: { - const p2ms = payments.p2ms({ output: script }) + const p2ms = payments.p2ms({ output: script }); return { type, pubkeys: p2ms.pubkeys, signatures: p2ms.pubkeys!.map((): undefined => undefined), - maxSignatures: p2ms.m - } + maxSignatures: p2ms.m, + }; } } - return { type } + return { type }; } -function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, witnessScript: Buffer): TxbInput { +function prepareInput( + input: TxbInput, + ourPubKey: Buffer, + redeemScript: Buffer, + witnessScript: Buffer, +): TxbInput { if (redeemScript && witnessScript) { - const p2wsh = <Payment> payments.p2wsh({ redeem: { output: witnessScript } }) - const p2wshAlt = <Payment> payments.p2wsh({ output: redeemScript }) - const p2sh = <Payment> payments.p2sh({ redeem: { output: redeemScript } }) - const p2shAlt = <Payment> payments.p2sh({ redeem: p2wsh }) + const p2wsh = <Payment>( + payments.p2wsh({ redeem: { output: witnessScript } }) + ); + const p2wshAlt = <Payment>payments.p2wsh({ output: redeemScript }); + const p2sh = <Payment>payments.p2sh({ redeem: { output: redeemScript } }); + const p2shAlt = <Payment>payments.p2sh({ redeem: p2wsh }); // enforces P2SH(P2WSH(...)) - if (!p2wsh.hash!.equals(p2wshAlt.hash!)) throw new Error('Witness script inconsistent with prevOutScript') - if (!p2sh.hash!.equals(p2shAlt.hash!)) throw new Error('Redeem script inconsistent with prevOutScript') + if (!p2wsh.hash!.equals(p2wshAlt.hash!)) + throw new Error('Witness script inconsistent with prevOutScript'); + if (!p2sh.hash!.equals(p2shAlt.hash!)) + throw new Error('Redeem script inconsistent with prevOutScript'); - const expanded = expandOutput(p2wsh.redeem!.output!, ourPubKey) - if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') + const expanded = expandOutput(p2wsh.redeem!.output!, ourPubKey); + if (!expanded.pubkeys) + throw new Error( + expanded.type + + ' not supported as witnessScript (' + + bscript.toASM(witnessScript) + + ')', + ); if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures + expanded.signatures = input.signatures; } - let signScript = witnessScript - if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure') + let signScript = witnessScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) + throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure'); return { redeemScript, @@ -626,30 +726,39 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, pubkeys: expanded.pubkeys, signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures - } + maxSignatures: expanded.maxSignatures, + }; } if (redeemScript) { - const p2sh = <Payment> payments.p2sh({ redeem: { output: redeemScript } }) + const p2sh = <Payment>payments.p2sh({ redeem: { output: redeemScript } }); if (input.prevOutScript) { - let p2shAlt + let p2shAlt; try { - p2shAlt = <Payment> payments.p2sh({ output: input.prevOutScript }) - } catch (e) { throw new Error('PrevOutScript must be P2SH') } - if (!p2sh.hash!.equals(p2shAlt.hash!)) throw new Error('Redeem script inconsistent with prevOutScript') + p2shAlt = <Payment>payments.p2sh({ output: input.prevOutScript }); + } catch (e) { + throw new Error('PrevOutScript must be P2SH'); + } + if (!p2sh.hash!.equals(p2shAlt.hash!)) + throw new Error('Redeem script inconsistent with prevOutScript'); } - const expanded = expandOutput(p2sh.redeem!.output!, ourPubKey) - if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')') + const expanded = expandOutput(p2sh.redeem!.output!, ourPubKey); + if (!expanded.pubkeys) + throw new Error( + expanded.type + + ' not supported as redeemScript (' + + bscript.toASM(redeemScript) + + ')', + ); if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures + expanded.signatures = input.signatures; } - let signScript = redeemScript + let signScript = redeemScript; if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output! + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output!; } return { @@ -665,26 +774,34 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, pubkeys: expanded.pubkeys, signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures - } + maxSignatures: expanded.maxSignatures, + }; } if (witnessScript) { - const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }) + const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }); if (input.prevOutScript) { - const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }) - if (!p2wsh.hash!.equals(p2wshAlt.hash!)) throw new Error('Witness script inconsistent with prevOutScript') + const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }); + if (!p2wsh.hash!.equals(p2wshAlt.hash!)) + throw new Error('Witness script inconsistent with prevOutScript'); } - const expanded = expandOutput(p2wsh.redeem!.output!, ourPubKey) - if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')') + const expanded = expandOutput(p2wsh.redeem!.output!, ourPubKey); + if (!expanded.pubkeys) + throw new Error( + expanded.type + + ' not supported as witnessScript (' + + bscript.toASM(witnessScript) + + ')', + ); if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures + expanded.signatures = input.signatures; } - let signScript = witnessScript - if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2WSH(P2WPKH) is a consensus failure') + let signScript = witnessScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) + throw new Error('P2WSH(P2WPKH) is a consensus failure'); return { witnessScript, @@ -699,25 +816,39 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, pubkeys: expanded.pubkeys, signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures - } + maxSignatures: expanded.maxSignatures, + }; } if (input.prevOutType && input.prevOutScript) { // embedded scripts are not possible without extra information - if (input.prevOutType === SCRIPT_TYPES.P2SH) throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript') - if (input.prevOutType === SCRIPT_TYPES.P2WSH) throw new Error('PrevOutScript is ' + input.prevOutType + ', requires witnessScript') - if (!input.prevOutScript) throw new Error('PrevOutScript is missing') + if (input.prevOutType === SCRIPT_TYPES.P2SH) + throw new Error( + 'PrevOutScript is ' + input.prevOutType + ', requires redeemScript', + ); + if (input.prevOutType === SCRIPT_TYPES.P2WSH) + throw new Error( + 'PrevOutScript is ' + input.prevOutType + ', requires witnessScript', + ); + if (!input.prevOutScript) throw new Error('PrevOutScript is missing'); - const expanded = expandOutput(input.prevOutScript, ourPubKey) - if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported (' + bscript.toASM(input.prevOutScript) + ')') + const expanded = expandOutput(input.prevOutScript, ourPubKey); + if (!expanded.pubkeys) + throw new Error( + expanded.type + + ' not supported (' + + bscript.toASM(input.prevOutScript) + + ')', + ); if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures + expanded.signatures = input.signatures; } - let signScript = input.prevOutScript + let signScript = input.prevOutScript; if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = <Buffer> payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output + signScript = <Buffer>( + payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output + ); } return { @@ -730,11 +861,11 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, pubkeys: expanded.pubkeys, signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures - } + maxSignatures: expanded.maxSignatures, + }; } - const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output + const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output; return { prevOutType: SCRIPT_TYPES.P2PKH, prevOutScript: prevOutScript, @@ -744,86 +875,92 @@ function prepareInput (input: TxbInput, ourPubKey: Buffer, redeemScript: Buffer, signType: SCRIPT_TYPES.P2PKH, pubkeys: [ourPubKey], - signatures: [undefined] - } + signatures: [undefined], + }; } -function build (type: string, input: TxbInput, allowIncomplete?: boolean): Payment | undefined { - const pubkeys = <Array<Buffer>>(input.pubkeys || []) - let signatures = <Array<Buffer>>(input.signatures || []) +function build( + type: string, + input: TxbInput, + allowIncomplete?: boolean, +): Payment | undefined { + const pubkeys = <Array<Buffer>>(input.pubkeys || []); + let signatures = <Array<Buffer>>(input.signatures || []); switch (type) { case SCRIPT_TYPES.P2PKH: { - if (pubkeys.length === 0) break - if (signatures.length === 0) break + if (pubkeys.length === 0) break; + if (signatures.length === 0) break; - return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] }) + return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] }); } case SCRIPT_TYPES.P2WPKH: { - if (pubkeys.length === 0) break - if (signatures.length === 0) break + if (pubkeys.length === 0) break; + if (signatures.length === 0) break; - return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] }) + return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] }); } case SCRIPT_TYPES.P2PK: { - if (pubkeys.length === 0) break - if (signatures.length === 0) break + if (pubkeys.length === 0) break; + if (signatures.length === 0) break; - return payments.p2pk({ signature: signatures[0] }) + return payments.p2pk({ signature: signatures[0] }); } case SCRIPT_TYPES.P2MS: { - const m = input.maxSignatures + const m = input.maxSignatures; if (allowIncomplete) { - signatures = signatures.map(x => x || ops.OP_0) + signatures = signatures.map(x => x || ops.OP_0); } else { - signatures = signatures.filter(x => x) + signatures = signatures.filter(x => x); } // if the transaction is not not complete (complete), or if signatures.length === m, validate // otherwise, the number of OP_0's may be >= m, so don't validate (boo) - const validate = !allowIncomplete || (m === signatures.length) - return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate }) + const validate = !allowIncomplete || m === signatures.length; + return payments.p2ms( + { m, pubkeys, signatures }, + { allowIncomplete, validate }, + ); } case SCRIPT_TYPES.P2SH: { - const redeem = build(input.redeemScriptType!, input, allowIncomplete) - if (!redeem) return + const redeem = build(input.redeemScriptType!, input, allowIncomplete); + if (!redeem) return; return payments.p2sh({ redeem: { output: redeem.output || input.redeemScript, input: redeem.input, - witness: redeem.witness - } - }) + witness: redeem.witness, + }, + }); } case SCRIPT_TYPES.P2WSH: { - const redeem = build(input.witnessScriptType!, input, allowIncomplete) - if (!redeem) return + const redeem = build(input.witnessScriptType!, input, allowIncomplete); + if (!redeem) return; return payments.p2wsh({ redeem: { output: input.witnessScript, input: redeem.input, - witness: redeem.witness - } - }) + witness: redeem.witness, + }, + }); } } } -function canSign (input: TxbInput): boolean { - return input.signScript !== undefined && +function canSign(input: TxbInput): boolean { + return ( + input.signScript !== undefined && input.signType !== undefined && input.pubkeys !== undefined && input.signatures !== undefined && input.signatures.length === input.pubkeys.length && input.pubkeys.length > 0 && - ( - input.hasWitness === false || - input.value !== undefined - ) + (input.hasWitness === false || input.value !== undefined) + ); } -function signatureHashType (buffer: Buffer): number { - return buffer.readUInt8(buffer.length - 1) +function signatureHashType(buffer: Buffer): number { + return buffer.readUInt8(buffer.length - 1); } diff --git a/ts_src/types.ts b/ts_src/types.ts index 8eacbdc..42ddb40 100644 --- a/ts_src/types.ts +++ b/ts_src/types.ts @@ -1,49 +1,51 @@ -const typeforce = require('typeforce') +const typeforce = require('typeforce'); -const UINT31_MAX: number = Math.pow(2, 31) - 1 -export function UInt31 (value: number): boolean { - return typeforce.UInt32(value) && value <= UINT31_MAX +const UINT31_MAX: number = Math.pow(2, 31) - 1; +export function UInt31(value: number): boolean { + return typeforce.UInt32(value) && value <= UINT31_MAX; } -export function BIP32Path (value: string): boolean { - return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) +export function BIP32Path(value: string): boolean { + return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); } -BIP32Path.toJSON = function () { return 'BIP32 derivation path' } +BIP32Path.toJSON = function() { + return 'BIP32 derivation path'; +}; -const SATOSHI_MAX: number = 21 * 1e14 -export function Satoshi (value: number): boolean { - return typeforce.UInt53(value) && value <= SATOSHI_MAX +const SATOSHI_MAX: number = 21 * 1e14; +export function Satoshi(value: number): boolean { + return typeforce.UInt53(value) && value <= SATOSHI_MAX; } // external dependent types -export const ECPoint = typeforce.quacksLike('Point') +export const ECPoint = typeforce.quacksLike('Point'); // exposed, external API export const Network = typeforce.compile({ messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), bip32: { public: typeforce.UInt32, - private: typeforce.UInt32 + private: typeforce.UInt32, }, pubKeyHash: typeforce.UInt8, scriptHash: typeforce.UInt8, - wif: typeforce.UInt8 -}) + wif: typeforce.UInt8, +}); -export const Buffer256bit = typeforce.BufferN(32) -export const Hash160bit = typeforce.BufferN(20) -export const Hash256bit = typeforce.BufferN(32) -export const Number = typeforce.Number -export const Array = typeforce.Array -export const Boolean = typeforce.Boolean -export const String = typeforce.String -export const Buffer = typeforce.Buffer -export const Hex = typeforce.Hex -export const maybe = typeforce.maybe -export const tuple = typeforce.tuple -export const UInt8 = typeforce.UInt8 -export const UInt32 = typeforce.UInt32 -export const Function = typeforce.Function -export const BufferN = typeforce.BufferN -export const Null = typeforce.Null -export const oneOf = typeforce.oneOf +export const Buffer256bit = typeforce.BufferN(32); +export const Hash160bit = typeforce.BufferN(20); +export const Hash256bit = typeforce.BufferN(32); +export const Number = typeforce.Number; +export const Array = typeforce.Array; +export const Boolean = typeforce.Boolean; +export const String = typeforce.String; +export const Buffer = typeforce.Buffer; +export const Hex = typeforce.Hex; +export const maybe = typeforce.maybe; +export const tuple = typeforce.tuple; +export const UInt8 = typeforce.UInt8; +export const UInt32 = typeforce.UInt32; +export const Function = typeforce.Function; +export const BufferN = typeforce.BufferN; +export const Null = typeforce.Null; +export const oneOf = typeforce.oneOf; From 6526000999dd4344faaa8e4f906d22cb617f751d Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Sun, 3 Mar 2019 23:20:36 +0900 Subject: [PATCH 225/568] style: remove standard --- package.json | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index e55b1a8..b9eb8d8 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,9 @@ "nobuild:coverage-html": "nyc report --reporter=html", "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", "nobuild:integration": "mocha --timeout 50000 test/integration/", - "nobuild:standard": "standard ts_src/**/*.ts", "nobuild:unit": "mocha", "prepare": "npm run build", - "standard": "npm run build && npm run nobuild:standard", - "test": "npm run build && npm run nobuild:standard && npm run nobuild:coverage", + "test": "npm run build && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" }, "repository": { @@ -71,15 +69,8 @@ "nyc": "^11.8.0", "prettier": "^1.16.4", "proxyquire": "^2.0.1", - "standard": "^11.0.1", "typescript": "3.2.2", "typescript-eslint-parser": "^21.0.2" }, - "license": "MIT", - "standard": { - "parser": "typescript-eslint-parser", - "plugins": [ - "typescript" - ] - } + "license": "MIT" } From 0ad8fbc6ba35b1f24113d498e16182ae5155db72 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Sun, 3 Mar 2019 23:31:17 +0900 Subject: [PATCH 226/568] style: add build output after applying prettier --- src/address.js | 3 +- src/block.js | 21 ++++--- src/classify.js | 2 +- src/crypto.js | 12 +++- src/ecpair.js | 13 +++-- src/networks.js | 12 ++-- src/payments/embed.js | 5 +- src/payments/lazy.js | 4 +- src/payments/p2ms.js | 10 ++-- src/payments/p2pk.js | 17 ++---- src/payments/p2pkh.js | 14 ++--- src/payments/p2sh.js | 24 +++----- src/payments/p2wpkh.js | 15 ++--- src/payments/p2wsh.js | 31 +++++----- src/script.js | 14 +++-- src/script_number.js | 19 +++--- src/script_signature.js | 9 +-- src/templates/nulldata.js | 7 ++- src/transaction.js | 62 ++++++++++---------- src/transaction_builder.js | 116 +++++++++++++++++++++---------------- src/types.js | 8 ++- types/index.d.ts | 2 +- 22 files changed, 217 insertions(+), 203 deletions(-) diff --git a/src/address.js b/src/address.js index ed11e5c..f12cd8d 100644 --- a/src/address.js +++ b/src/address.js @@ -25,7 +25,7 @@ function fromBech32(address) { return { version: result.words[0], prefix: result.prefix, - data: Buffer.from(data) + data: Buffer.from(data), }; } exports.fromBech32 = fromBech32; @@ -44,6 +44,7 @@ function toBech32(data, version, prefix) { } exports.toBech32 = toBech32; function fromOutputScript(output, network) { + //TODO: Network network = network || networks.bitcoin; try { return payments.p2pkh({ output, network }).address; diff --git a/src/block.js b/src/block.js index cb593ac..eb3fb8b 100644 --- a/src/block.js +++ b/src/block.js @@ -10,22 +10,22 @@ const varuint = require('varuint-bitcoin'); const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions'); const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block'); function txesHaveWitnessCommit(transactions) { - return transactions instanceof Array && + return (transactions instanceof Array && transactions[0] && transactions[0].ins && transactions[0].ins instanceof Array && transactions[0].ins[0] && transactions[0].ins[0].witness && transactions[0].ins[0].witness instanceof Array && - transactions[0].ins[0].witness.length > 0; + transactions[0].ins[0].witness.length > 0); } function anyTxHasWitness(transactions) { - return transactions instanceof Array && + return (transactions instanceof Array && transactions.some(tx => typeof tx === 'object' && tx.ins instanceof Array && tx.ins.some(input => typeof input === 'object' && input.witness instanceof Array && - input.witness.length > 0)); + input.witness.length > 0))); } class Block { constructor() { @@ -116,9 +116,7 @@ class Block { // There is no rule for the index of the output, so use filter to find it. // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed // If multiple commits are found, the output with highest index is assumed. - let witnessCommits = this.transactions[0].outs - .filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))) - .map(out => out.script.slice(6, 38)); + let witnessCommits = this.transactions[0].outs.filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))).map(out => out.script.slice(6, 38)); if (witnessCommits.length === 0) return null; // Use the commit with the highest output (should only be one though) @@ -141,8 +139,9 @@ class Block { byteLength(headersOnly) { if (headersOnly || !this.transactions) return 80; - return 80 + varuint.encodingLength(this.transactions.length) + - this.transactions.reduce((a, x) => a + x.byteLength(), 0); + return (80 + + varuint.encodingLength(this.transactions.length) + + this.transactions.reduce((a, x) => a + x.byteLength(), 0)); } getHash() { return bcrypto.hash256(this.toBuffer(true)); @@ -197,8 +196,8 @@ class Block { let hasWitnessCommit = this.hasWitnessCommit(); if (!hasWitnessCommit && this.hasWitness()) return false; - return this.__checkMerkleRoot() && - (hasWitnessCommit ? this.__checkWitnessCommit() : true); + return (this.__checkMerkleRoot() && + (hasWitnessCommit ? this.__checkWitnessCommit() : true)); } checkMerkleRoot() { console.warn('Deprecation Warning: Block method checkMerkleRoot will be ' + diff --git a/src/classify.js b/src/classify.js index a2109c2..0aec5c4 100644 --- a/src/classify.js +++ b/src/classify.js @@ -18,7 +18,7 @@ const types = { P2SH: 'scripthash', P2WPKH: 'witnesspubkeyhash', P2WSH: 'witnessscripthash', - WITNESS_COMMITMENT: 'witnesscommitment' + WITNESS_COMMITMENT: 'witnesscommitment', }; exports.types = types; function classifyOutput(script) { diff --git a/src/crypto.js b/src/crypto.js index 4165d30..d494b86 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -2,15 +2,21 @@ Object.defineProperty(exports, "__esModule", { value: true }); const createHash = require('create-hash'); function ripemd160(buffer) { - return createHash('rmd160').update(buffer).digest(); + return createHash('rmd160') + .update(buffer) + .digest(); } exports.ripemd160 = ripemd160; function sha1(buffer) { - return createHash('sha1').update(buffer).digest(); + return createHash('sha1') + .update(buffer) + .digest(); } exports.sha1 = sha1; function sha256(buffer) { - return createHash('sha256').update(buffer).digest(); + return createHash('sha256') + .update(buffer) + .digest(); } exports.sha256 = sha256; function hash160(buffer) { diff --git a/src/ecpair.js b/src/ecpair.js index d4dc94f..7d376ac 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -8,13 +8,14 @@ const typeforce = require('typeforce'); const wif = require('wif'); const isOptions = typeforce.maybe(typeforce.compile({ compressed: types.maybe(types.Boolean), - network: types.maybe(types.Network) + network: types.maybe(types.Network), })); class ECPair { constructor(d, Q, options) { if (options === undefined) options = {}; - this.compressed = options.compressed === undefined ? true : options.compressed; + this.compressed = + options.compressed === undefined ? true : options.compressed; this.network = options.network || NETWORKS.bitcoin; this.__d = undefined; this.__Q = undefined; @@ -64,9 +65,11 @@ function fromWIF(string, network) { const version = decoded.version; // list of networks? if (types.Array(network)) { - network = network.filter(function (x) { + network = network + .filter(function (x) { return version === x.wif; - }).pop(); + }) + .pop(); if (!network) throw new Error('Unknown network version'); // otherwise, assume a network object (or default to bitcoin) @@ -78,7 +81,7 @@ function fromWIF(string, network) { } return fromPrivateKey(decoded.privateKey, { compressed: decoded.compressed, - network: network + network: network, }); } exports.fromWIF = fromWIF; diff --git a/src/networks.js b/src/networks.js index 821cd96..298808d 100644 --- a/src/networks.js +++ b/src/networks.js @@ -5,31 +5,31 @@ exports.bitcoin = { bech32: 'bc', bip32: { public: 0x0488b21e, - private: 0x0488ade4 + private: 0x0488ade4, }, pubKeyHash: 0x00, scriptHash: 0x05, - wif: 0x80 + wif: 0x80, }; exports.regtest = { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'bcrt', bip32: { public: 0x043587cf, - private: 0x04358394 + private: 0x04358394, }, pubKeyHash: 0x6f, scriptHash: 0xc4, - wif: 0xef + wif: 0xef, }; exports.testnet = { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'tb', bip32: { public: 0x043587cf, - private: 0x04358394 + private: 0x04358394, }, pubKeyHash: 0x6f, scriptHash: 0xc4, - wif: 0xef + wif: 0xef, }; diff --git a/src/payments/embed.js b/src/payments/embed.js index 7d341ab..0e2a0ae 100644 --- a/src/payments/embed.js +++ b/src/payments/embed.js @@ -14,14 +14,13 @@ function stacksEqual(a, b) { } // output: OP_RETURN ... function p2data(a, opts) { - if (!a.data && - !a.output) + if (!a.data && !a.output) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); typef({ network: typef.maybe(typef.Object), output: typef.maybe(typef.Buffer), - data: typef.maybe(typef.arrayOf(typef.Buffer)) + data: typef.maybe(typef.arrayOf(typef.Buffer)), }, a); const network = a.network || networks_1.bitcoin; const o = { network }; diff --git a/src/payments/lazy.js b/src/payments/lazy.js index 2c848e1..9eda8e8 100644 --- a/src/payments/lazy.js +++ b/src/payments/lazy.js @@ -14,9 +14,9 @@ function prop(object, name, f) { configurable: true, enumerable: true, value: value, - writable: true + writable: true, }); - } + }, }); } exports.prop = prop; diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index a45bd48..4e3c115 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -24,9 +24,8 @@ function p2ms(a, opts) { throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); function isAcceptableSignature(x) { - return bscript.isCanonicalScriptSignature(x) || - (opts.allowIncomplete && - (x === OPS.OP_0)) !== undefined; // eslint-disable-line + return (bscript.isCanonicalScriptSignature(x) || + (opts.allowIncomplete && x === OPS.OP_0) !== undefined); // eslint-disable-line } typef({ network: typef.maybe(typef.Object), @@ -35,7 +34,7 @@ function p2ms(a, opts) { output: typef.maybe(typef.Buffer), pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), - input: typef.maybe(typef.Buffer) + input: typef.maybe(typef.Buffer), }, a); const network = a.network || networks_1.bitcoin; const o = { network }; @@ -131,7 +130,8 @@ function p2ms(a, opts) { if (a.input) { if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid'); - if (o.signatures.length === 0 || !o.signatures.every(isAcceptableSignature)) + if (o.signatures.length === 0 || + !o.signatures.every(isAcceptableSignature)) throw new TypeError('Input has invalid signature(s)'); if (a.signatures && !stacksEqual(a.signatures, o.signatures)) throw new TypeError('Signature mismatch'); diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index ab8654d..9c9318f 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -9,11 +9,7 @@ const ecc = require('tiny-secp256k1'); // input: {signature} // output: {pubKey} OP_CHECKSIG function p2pk(a, opts) { - if (!a.input && - !a.output && - !a.pubkey && - !a.input && - !a.signature) + if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); typef({ @@ -21,18 +17,17 @@ function p2pk(a, opts) { output: typef.maybe(typef.Buffer), pubkey: typef.maybe(ecc.isPoint), signature: typef.maybe(bscript.isCanonicalScriptSignature), - input: typef.maybe(typef.Buffer) + input: typef.maybe(typef.Buffer), }, a); - const _chunks = lazy.value(function () { return bscript.decompile(a.input); }); + const _chunks = lazy.value(function () { + return bscript.decompile(a.input); + }); const network = a.network || networks_1.bitcoin; const o = { network }; lazy.prop(o, 'output', function () { if (!a.pubkey) return; - return bscript.compile([ - a.pubkey, - OPS.OP_CHECKSIG - ]); + return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); }); lazy.prop(o, 'pubkey', function () { if (!a.output) diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index 55f4817..6b41a77 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -11,11 +11,7 @@ const bs58check = require('bs58check'); // input: {signature} {pubkey} // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG function p2pkh(a, opts) { - if (!a.address && - !a.hash && - !a.output && - !a.pubkey && - !a.input) + if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); typef({ @@ -25,7 +21,7 @@ function p2pkh(a, opts) { output: typef.maybe(typef.BufferN(25)), pubkey: typef.maybe(ecc.isPoint), signature: typef.maybe(bscript.isCanonicalScriptSignature), - input: typef.maybe(typef.Buffer) + input: typef.maybe(typef.Buffer), }, a); const _address = lazy.value(function () { const payload = bs58check.decode(a.address); @@ -33,7 +29,9 @@ function p2pkh(a, opts) { const hash = payload.slice(1); return { version, hash }; }); - const _chunks = lazy.value(function () { return bscript.decompile(a.input); }); + const _chunks = lazy.value(function () { + return bscript.decompile(a.input); + }); const network = a.network || networks_1.bitcoin; const o = { network }; lazy.prop(o, 'address', function () { @@ -60,7 +58,7 @@ function p2pkh(a, opts) { OPS.OP_HASH160, o.hash, OPS.OP_EQUALVERIFY, - OPS.OP_CHECKSIG + OPS.OP_CHECKSIG, ]); }); lazy.prop(o, 'pubkey', function () { diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index 8ec9910..bb8f0a0 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -18,11 +18,7 @@ function stacksEqual(a, b) { // witness: <?> // output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL function p2sh(a, opts) { - if (!a.address && - !a.hash && - !a.output && - !a.redeem && - !a.input) + if (!a.address && !a.hash && !a.output && !a.redeem && !a.input) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); typef({ @@ -34,10 +30,10 @@ function p2sh(a, opts) { network: typef.maybe(typef.Object), output: typef.maybe(typef.Buffer), input: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) + witness: typef.maybe(typef.arrayOf(typef.Buffer)), }), input: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) + witness: typef.maybe(typef.arrayOf(typef.Buffer)), }, a); let network = a.network; if (!network) { @@ -50,14 +46,16 @@ function p2sh(a, opts) { const hash = payload.slice(1); return { version, hash }; }); - const _chunks = lazy.value(function () { return bscript.decompile(a.input); }); + const _chunks = lazy.value(function () { + return bscript.decompile(a.input); + }); const _redeem = lazy.value(function () { const chunks = _chunks(); return { network, output: chunks[chunks.length - 1], input: bscript.compile(chunks.slice(0, -1)), - witness: a.witness || [] + witness: a.witness || [], }; }); // output dependents @@ -81,11 +79,7 @@ function p2sh(a, opts) { lazy.prop(o, 'output', function () { if (!o.hash) return; - return bscript.compile([ - OPS.OP_HASH160, - o.hash, - OPS.OP_EQUAL - ]); + return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); }); // input dependents lazy.prop(o, 'redeem', function () { @@ -153,7 +147,7 @@ function p2sh(a, opts) { if (hasInput && hasWitness) throw new TypeError('Input and witness provided'); if (hasInput) { - const richunks = bscript.decompile(redeem.input); + const richunks = (bscript.decompile(redeem.input)); if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig'); } diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index 7b269b7..9165f96 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -13,11 +13,7 @@ const EMPTY_BUFFER = Buffer.alloc(0); // input: <> // output: OP_0 {pubKeyHash} function p2wpkh(a, opts) { - if (!a.address && - !a.hash && - !a.output && - !a.pubkey && - !a.witness) + if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); typef({ @@ -28,7 +24,7 @@ function p2wpkh(a, opts) { output: typef.maybe(typef.BufferN(22)), pubkey: typef.maybe(ecc.isPoint), signature: typef.maybe(bscript.isCanonicalScriptSignature), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) + witness: typef.maybe(typef.arrayOf(typef.Buffer)), }, a); const _address = lazy.value(function () { const result = bech32.decode(a.address); @@ -37,7 +33,7 @@ function p2wpkh(a, opts) { return { version, prefix: result.prefix, - data: Buffer.from(data) + data: Buffer.from(data), }; }); const network = a.network || networks_1.bitcoin; @@ -60,10 +56,7 @@ function p2wpkh(a, opts) { lazy.prop(o, 'output', function () { if (!o.hash) return; - return bscript.compile([ - OPS.OP_0, - o.hash - ]); + return bscript.compile([OPS.OP_0, o.hash]); }); lazy.prop(o, 'pubkey', function () { if (a.pubkey) diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index a7e9f8b..c5eb067 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -19,11 +19,7 @@ function stacksEqual(a, b) { // witness: [redeemScriptSig ...] {redeemScript} // output: OP_0 {sha256(redeemScript)} function p2wsh(a, opts) { - if (!a.address && - !a.hash && - !a.output && - !a.redeem && - !a.witness) + if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); typef({ @@ -35,10 +31,10 @@ function p2wsh(a, opts) { input: typef.maybe(typef.Buffer), network: typef.maybe(typef.Object), output: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) + witness: typef.maybe(typef.arrayOf(typef.Buffer)), }), input: typef.maybe(typef.BufferN(0)), - witness: typef.maybe(typef.arrayOf(typef.Buffer)) + witness: typef.maybe(typef.arrayOf(typef.Buffer)), }, a); const _address = lazy.value(function () { const result = bech32.decode(a.address); @@ -47,10 +43,12 @@ function p2wsh(a, opts) { return { version, prefix: result.prefix, - data: Buffer.from(data) + data: Buffer.from(data), }; }); - const _rchunks = lazy.value(function () { return bscript.decompile(a.redeem.input); }); + const _rchunks = lazy.value(function () { + return bscript.decompile(a.redeem.input); + }); let network = a.network; if (!network) { network = (a.redeem && a.redeem.network) || networks_1.bitcoin; @@ -74,10 +72,7 @@ function p2wsh(a, opts) { lazy.prop(o, 'output', function () { if (!o.hash) return; - return bscript.compile([ - OPS.OP_0, - o.hash - ]); + return bscript.compile([OPS.OP_0, o.hash]); }); lazy.prop(o, 'redeem', function () { if (!a.witness) @@ -85,7 +80,7 @@ function p2wsh(a, opts) { return { output: a.witness[a.witness.length - 1], input: EMPTY_BUFFER, - witness: a.witness.slice(0, -1) + witness: a.witness.slice(0, -1), }; }); lazy.prop(o, 'input', function () { @@ -165,11 +160,15 @@ function p2wsh(a, opts) { } if (a.redeem.input && !bscript.isPushOnly(_rchunks())) throw new TypeError('Non push-only scriptSig'); - if (a.witness && a.redeem.witness && !stacksEqual(a.witness, a.redeem.witness)) + if (a.witness && + a.redeem.witness && + !stacksEqual(a.witness, a.redeem.witness)) throw new TypeError('Witness and redeem.witness mismatch'); } if (a.witness) { - if (a.redeem && a.redeem.output && !a.redeem.output.equals(a.witness[a.witness.length - 1])) + if (a.redeem && + a.redeem.output && + !a.redeem.output.equals(a.witness[a.witness.length - 1])) throw new TypeError('Witness and redeem.output mismatch'); } } diff --git a/src/script.js b/src/script.js index 3d115cb..a114d1f 100644 --- a/src/script.js +++ b/src/script.js @@ -11,10 +11,10 @@ exports.OPS = require('bitcoin-ops'); const REVERSE_OPS = require('bitcoin-ops/map'); const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1 function isOPInt(value) { - return types.Number(value) && - ((value === exports.OPS.OP_0) || + return (types.Number(value) && + (value === exports.OPS.OP_0 || (value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) || - (value === exports.OPS.OP_1NEGATE)); + value === exports.OPS.OP_1NEGATE)); } function isPushOnlyChunk(value) { return types.Buffer(value) || isOPInt(value); @@ -96,7 +96,7 @@ function decompile(buffer) { while (i < buffer.length) { const opcode = buffer[i]; // data chunk - if ((opcode > exports.OPS.OP_0) && (opcode <= exports.OPS.OP_PUSHDATA4)) { + if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) { const d = pushdata.decode(buffer, i); // did reading a pushDataInt fail? if (d === null) @@ -129,7 +129,8 @@ function toASM(chunks) { if (chunksIsBuffer(chunks)) { chunks = decompile(chunks); } - return chunks.map(function (chunk) { + return chunks + .map(function (chunk) { // data? if (singleChunkIsBuffer(chunk)) { const op = asMinimalOP(chunk); @@ -139,7 +140,8 @@ function toASM(chunks) { } // opcode! return REVERSE_OPS[chunk]; - }).join(' '); + }) + .join(' '); } exports.toASM = toASM; function fromASM(asm) { diff --git a/src/script_number.js b/src/script_number.js index da12de6..a8c42d3 100644 --- a/src/script_number.js +++ b/src/script_number.js @@ -19,8 +19,8 @@ function decode(buffer, maxLength, minimal) { const a = buffer.readUInt32LE(0); const b = buffer.readUInt8(4); if (b & 0x80) - return -(((b & ~0x80) * 0x100000000) + a); - return (b * 0x100000000) + a; + return -((b & ~0x80) * 0x100000000 + a); + return b * 0x100000000 + a; } // 32-bit / 24-bit / 16-bit / 8-bit let result = 0; @@ -33,11 +33,16 @@ function decode(buffer, maxLength, minimal) { } exports.decode = decode; function scriptNumSize(i) { - return i > 0x7fffffff ? 5 - : i > 0x7fffff ? 4 - : i > 0x7fff ? 3 - : i > 0x7f ? 2 - : i > 0x00 ? 1 + return i > 0x7fffffff + ? 5 + : i > 0x7fffff + ? 4 + : i > 0x7fff + ? 3 + : i > 0x7f + ? 2 + : i > 0x00 + ? 1 : 0; } function encode(number) { diff --git a/src/script_signature.js b/src/script_signature.js index c3372cd..c185981 100644 --- a/src/script_signature.js +++ b/src/script_signature.js @@ -34,14 +34,14 @@ function decode(buffer) { const s = fromDER(decode.s); return { signature: Buffer.concat([r, s], 64), - hashType: hashType + hashType: hashType, }; } exports.decode = decode; function encode(signature, hashType) { typeforce({ signature: types.BufferN(64), - hashType: types.UInt8 + hashType: types.UInt8, }, { signature, hashType }); const hashTypeMod = hashType & ~0x80; if (hashTypeMod <= 0 || hashTypeMod >= 4) @@ -50,9 +50,6 @@ function encode(signature, hashType) { hashTypeBuffer.writeUInt8(hashType, 0); const r = toDER(signature.slice(0, 32)); const s = toDER(signature.slice(32, 64)); - return Buffer.concat([ - bip66.encode(r, s), - hashTypeBuffer - ]); + return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]); } exports.encode = encode; diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js index fd5320d..b5ffdce 100644 --- a/src/templates/nulldata.js +++ b/src/templates/nulldata.js @@ -5,10 +5,11 @@ const bscript = require("../script"); const OPS = bscript.OPS; function check(script) { const buffer = bscript.compile(script); - return buffer.length > 1 && - buffer[0] === OPS.OP_RETURN; + return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; } exports.check = check; -check.toJSON = function () { return 'null data output'; }; +check.toJSON = function () { + return 'null data output'; +}; const output = { check }; exports.output = output; diff --git a/src/transaction.js b/src/transaction.js index 34d7b40..cfd31fe 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -14,9 +14,10 @@ function varSliceSize(someScript) { } function vectorSize(someVector) { const length = someVector.length; - return varuint.encodingLength(length) + someVector.reduce((sum, witness) => { - return sum + varSliceSize(witness); - }, 0); + return (varuint.encodingLength(length) + + someVector.reduce((sum, witness) => { + return sum + varSliceSize(witness); + }, 0)); } const EMPTY_SCRIPT = Buffer.allocUnsafe(0); const EMPTY_WITNESS = []; @@ -25,7 +26,7 @@ const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000 const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex'); const BLANK_OUTPUT = { script: EMPTY_SCRIPT, - valueBuffer: VALUE_UINT64_MAX + valueBuffer: VALUE_UINT64_MAX, }; function isOutput(out) { return out.value !== undefined; @@ -90,14 +91,14 @@ class Transaction { index: readUInt32(), script: readVarSlice(), sequence: readUInt32(), - witness: EMPTY_WITNESS + witness: EMPTY_WITNESS, }); } const voutLen = readVarInt(); for (i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), - script: readVarSlice() + script: readVarSlice(), }); } if (hasWitnesses) { @@ -127,7 +128,7 @@ class Transaction { return true; } isCoinbase() { - return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash); + return (this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)); } addInput(hash, index, sequence, scriptSig) { typeforce(types.tuple(types.Hash256bit, types.UInt32, types.maybe(types.UInt32), types.maybe(types.Buffer)), arguments); @@ -140,7 +141,7 @@ class Transaction { index: index, script: scriptSig || EMPTY_SCRIPT, sequence: sequence, - witness: EMPTY_WITNESS + witness: EMPTY_WITNESS, }) - 1); } addOutput(scriptPubKey, value) { @@ -148,11 +149,11 @@ class Transaction { // Add the output and return the output's index return (this.outs.push({ script: scriptPubKey, - value: value + value: value, }) - 1); } hasWitnesses() { - return this.ins.some((x) => { + return this.ins.some(x => { return x.witness.length !== 0; }); } @@ -178,27 +179,29 @@ class Transaction { this.outs.reduce((sum, output) => { return sum + 8 + varSliceSize(output.script); }, 0) + - (hasWitnesses ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness); - }, 0) : 0)); + (hasWitnesses + ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) + : 0)); } clone() { const newTx = new Transaction(); newTx.version = this.version; newTx.locktime = this.locktime; - newTx.ins = this.ins.map((txIn) => { + newTx.ins = this.ins.map(txIn => { return { hash: txIn.hash, index: txIn.index, script: txIn.script, sequence: txIn.sequence, - witness: txIn.witness + witness: txIn.witness, }; }); - newTx.outs = this.outs.map((txOut) => { + newTx.outs = this.outs.map(txOut => { return { script: txOut.script, - value: txOut.value + value: txOut.value, }; }); return newTx; @@ -217,7 +220,7 @@ class Transaction { if (inIndex >= this.ins.length) return ONE; // ignore OP_CODESEPARATOR - const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter((x) => { + const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(x => { return x !== script_1.OPS.OP_CODESEPARATOR; })); const txTmp = this.clone(); @@ -257,7 +260,7 @@ class Transaction { } else { // "blank" others input scripts - txTmp.ins.forEach((input) => { + txTmp.ins.forEach(input => { input.script = EMPTY_SCRIPT; }); txTmp.ins[inIndex].script = ourScript; @@ -295,7 +298,7 @@ class Transaction { if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { tbuffer = Buffer.allocUnsafe(36 * this.ins.length); toffset = 0; - this.ins.forEach((txIn) => { + this.ins.forEach(txIn => { writeSlice(txIn.hash); writeUInt32(txIn.index); }); @@ -306,7 +309,7 @@ class Transaction { (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { tbuffer = Buffer.allocUnsafe(4 * this.ins.length); toffset = 0; - this.ins.forEach((txIn) => { + this.ins.forEach(txIn => { writeUInt32(txIn.sequence); }); hashSequence = bcrypto.hash256(tbuffer); @@ -318,13 +321,14 @@ class Transaction { }, 0); tbuffer = Buffer.allocUnsafe(txOutsSize); toffset = 0; - this.outs.forEach((out) => { + this.outs.forEach(out => { writeUInt64(out.value); writeVarSlice(out.script); }); hashOutputs = bcrypto.hash256(tbuffer); } - else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { + else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && + inIndex < this.outs.length) { const output = this.outs[inIndex]; tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); toffset = 0; @@ -369,13 +373,13 @@ class Transaction { offset += slice.copy(buffer, offset); } function writeUInt8(i) { - offset = (buffer).writeUInt8(i, offset); + offset = buffer.writeUInt8(i, offset); } function writeUInt32(i) { - offset = (buffer).writeUInt32LE(i, offset); + offset = buffer.writeUInt32LE(i, offset); } function writeInt32(i) { - offset = (buffer).writeInt32LE(i, offset); + offset = buffer.writeInt32LE(i, offset); } function writeUInt64(i) { offset = bufferutils.writeUInt64LE(buffer, i, offset); @@ -399,14 +403,14 @@ class Transaction { writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); } writeVarInt(this.ins.length); - this.ins.forEach((txIn) => { + this.ins.forEach(txIn => { writeSlice(txIn.hash); writeUInt32(txIn.index); writeVarSlice(txIn.script); writeUInt32(txIn.sequence); }); writeVarInt(this.outs.length); - this.outs.forEach((txOut) => { + this.outs.forEach(txOut => { if (isOutput(txOut)) { writeUInt64(txOut.value); } @@ -416,7 +420,7 @@ class Transaction { writeVarSlice(txOut.script); }); if (hasWitnesses) { - this.ins.forEach((input) => { + this.ins.forEach(input => { writeVector(input.witness); }); } diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 1cdc6bf..540a17a 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -43,7 +43,7 @@ class TransactionBuilder { txb.__addInputUnsafe(txIn.hash, txIn.index, { sequence: txIn.sequence, script: txIn.script, - witness: txIn.witness + witness: txIn.witness, }); }); // fix some things not possible through the public API @@ -89,7 +89,7 @@ class TransactionBuilder { return this.__addInputUnsafe(txHash, vout, { sequence: sequence, prevOutScript: prevOutScript, - value: value + value: value, }); } __addInputUnsafe(txHash, vout, options) { @@ -194,7 +194,7 @@ class TransactionBuilder { if (!canSign(input)) { if (witnessValue !== undefined) { if (input.value !== undefined && input.value !== witnessValue) - throw new Error('Input didn\'t match witnessValue'); + throw new Error("Input didn't match witnessValue"); typeforce(types.Satoshi, witnessValue); input.value = witnessValue; } @@ -251,18 +251,19 @@ class TransactionBuilder { } // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs // .build() will fail, but .buildIncomplete() is OK - return (this.__tx.outs.length === 0) && this.__inputs.some((input) => { - if (!input.signatures) - return false; - return input.signatures.some((signature) => { - if (!signature) - return false; // no signature, no issue - const hashType = signatureHashType(signature); - if (hashType & transaction_1.Transaction.SIGHASH_NONE) - return false; // SIGHASH_NONE doesn't care about outputs - return true; // SIGHASH_* does care - }); - }); + return (this.__tx.outs.length === 0 && + this.__inputs.some(input => { + if (!input.signatures) + return false; + return input.signatures.some(signature => { + if (!signature) + return false; // no signature, no issue + const hashType = signatureHashType(signature); + if (hashType & transaction_1.Transaction.SIGHASH_NONE) + return false; // SIGHASH_NONE doesn't care about outputs + return true; // SIGHASH_* does care + }); + })); } __canModifyOutputs() { const nInputs = this.__tx.ins.length; @@ -313,21 +314,25 @@ function expandInput(scriptSig, witnessStack, type, scriptPubKey) { } switch (type) { case SCRIPT_TYPES.P2WPKH: { - const { output, pubkey, signature } = payments.p2wpkh({ witness: witnessStack }); + const { output, pubkey, signature } = payments.p2wpkh({ + witness: witnessStack, + }); return { prevOutScript: output, prevOutType: SCRIPT_TYPES.P2WPKH, pubkeys: [pubkey], - signatures: [signature] + signatures: [signature], }; } case SCRIPT_TYPES.P2PKH: { - const { output, pubkey, signature } = payments.p2pkh({ input: scriptSig }); + const { output, pubkey, signature } = payments.p2pkh({ + input: scriptSig, + }); return { prevOutScript: output, prevOutType: SCRIPT_TYPES.P2PKH, pubkeys: [pubkey], - signatures: [signature] + signatures: [signature], }; } case SCRIPT_TYPES.P2PK: { @@ -335,26 +340,26 @@ function expandInput(scriptSig, witnessStack, type, scriptPubKey) { return { prevOutType: SCRIPT_TYPES.P2PK, pubkeys: [undefined], - signatures: [signature] + signatures: [signature], }; } case SCRIPT_TYPES.P2MS: { const { m, pubkeys, signatures } = payments.p2ms({ input: scriptSig, - output: scriptPubKey + output: scriptPubKey, }, { allowIncomplete: true }); return { prevOutType: SCRIPT_TYPES.P2MS, pubkeys: pubkeys, signatures: signatures, - maxSignatures: m + maxSignatures: m, }; } } if (type === SCRIPT_TYPES.P2SH) { const { output, redeem } = payments.p2sh({ input: scriptSig, - witness: witnessStack + witness: witnessStack, }); const outputType = classify.output(redeem.output); const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output); @@ -368,13 +373,13 @@ function expandInput(scriptSig, witnessStack, type, scriptPubKey) { witnessScript: expanded.witnessScript, witnessScriptType: expanded.witnessScriptType, pubkeys: expanded.pubkeys, - signatures: expanded.signatures + signatures: expanded.signatures, }; } if (type === SCRIPT_TYPES.P2WSH) { const { output, redeem } = payments.p2wsh({ input: scriptSig, - witness: witnessStack + witness: witnessStack, }); const outputType = classify.output(redeem.output); let expanded; @@ -392,12 +397,12 @@ function expandInput(scriptSig, witnessStack, type, scriptPubKey) { witnessScript: redeem.output, witnessScriptType: expanded.prevOutType, pubkeys: expanded.pubkeys, - signatures: expanded.signatures + signatures: expanded.signatures, }; } return { prevOutType: SCRIPT_TYPES.NONSTANDARD, - prevOutScript: scriptSig + prevOutScript: scriptSig, }; } // could be done in expandInput, but requires the original Transaction for hashForSignature @@ -444,7 +449,7 @@ function expandOutput(script, ourPubKey) { return { type, pubkeys: [ourPubKey], - signatures: [undefined] + signatures: [undefined], }; } case SCRIPT_TYPES.P2WPKH: { @@ -458,7 +463,7 @@ function expandOutput(script, ourPubKey) { return { type, pubkeys: [ourPubKey], - signatures: [undefined] + signatures: [undefined], }; } case SCRIPT_TYPES.P2PK: { @@ -466,7 +471,7 @@ function expandOutput(script, ourPubKey) { return { type, pubkeys: [p2pk.pubkey], - signatures: [undefined] + signatures: [undefined], }; } case SCRIPT_TYPES.P2MS: { @@ -475,7 +480,7 @@ function expandOutput(script, ourPubKey) { type, pubkeys: p2ms.pubkeys, signatures: p2ms.pubkeys.map(() => undefined), - maxSignatures: p2ms.m + maxSignatures: p2ms.m, }; } } @@ -483,7 +488,7 @@ function expandOutput(script, ourPubKey) { } function prepareInput(input, ourPubKey, redeemScript, witnessScript) { if (redeemScript && witnessScript) { - const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }); + const p2wsh = (payments.p2wsh({ redeem: { output: witnessScript } })); const p2wshAlt = payments.p2wsh({ output: redeemScript }); const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); const p2shAlt = payments.p2sh({ redeem: p2wsh }); @@ -494,7 +499,10 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { throw new Error('Redeem script inconsistent with prevOutScript'); const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); if (!expanded.pubkeys) - throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')'); + throw new Error(expanded.type + + ' not supported as witnessScript (' + + bscript.toASM(witnessScript) + + ')'); if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures; } @@ -513,7 +521,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { signType: expanded.type, pubkeys: expanded.pubkeys, signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures + maxSignatures: expanded.maxSignatures, }; } if (redeemScript) { @@ -531,7 +539,10 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { } const expanded = expandOutput(p2sh.redeem.output, ourPubKey); if (!expanded.pubkeys) - throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')'); + throw new Error(expanded.type + + ' not supported as redeemScript (' + + bscript.toASM(redeemScript) + + ')'); if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures; } @@ -549,7 +560,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { signType: expanded.type, pubkeys: expanded.pubkeys, signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures + maxSignatures: expanded.maxSignatures, }; } if (witnessScript) { @@ -561,7 +572,10 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { } const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); if (!expanded.pubkeys) - throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')'); + throw new Error(expanded.type + + ' not supported as witnessScript (' + + bscript.toASM(witnessScript) + + ')'); if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures; } @@ -578,7 +592,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { signType: expanded.type, pubkeys: expanded.pubkeys, signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures + maxSignatures: expanded.maxSignatures, }; } if (input.prevOutType && input.prevOutScript) { @@ -591,13 +605,16 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { throw new Error('PrevOutScript is missing'); const expanded = expandOutput(input.prevOutScript, ourPubKey); if (!expanded.pubkeys) - throw new Error(expanded.type + ' not supported (' + bscript.toASM(input.prevOutScript) + ')'); + throw new Error(expanded.type + + ' not supported (' + + bscript.toASM(input.prevOutScript) + + ')'); if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures; } let signScript = input.prevOutScript; if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output; + signScript = (payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output); } return { prevOutType: expanded.type, @@ -607,7 +624,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { signType: expanded.type, pubkeys: expanded.pubkeys, signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures + maxSignatures: expanded.maxSignatures, }; } const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output; @@ -618,7 +635,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { signScript: prevOutScript, signType: SCRIPT_TYPES.P2PKH, pubkeys: [ourPubKey], - signatures: [undefined] + signatures: [undefined], }; } function build(type, input, allowIncomplete) { @@ -656,7 +673,7 @@ function build(type, input, allowIncomplete) { } // if the transaction is not not complete (complete), or if signatures.length === m, validate // otherwise, the number of OP_0's may be >= m, so don't validate (boo) - const validate = !allowIncomplete || (m === signatures.length); + const validate = !allowIncomplete || m === signatures.length; return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate }); } case SCRIPT_TYPES.P2SH: { @@ -667,8 +684,8 @@ function build(type, input, allowIncomplete) { redeem: { output: redeem.output || input.redeemScript, input: redeem.input, - witness: redeem.witness - } + witness: redeem.witness, + }, }); } case SCRIPT_TYPES.P2WSH: { @@ -679,21 +696,20 @@ function build(type, input, allowIncomplete) { redeem: { output: input.witnessScript, input: redeem.input, - witness: redeem.witness - } + witness: redeem.witness, + }, }); } } } function canSign(input) { - return input.signScript !== undefined && + return (input.signScript !== undefined && input.signType !== undefined && input.pubkeys !== undefined && input.signatures !== undefined && input.signatures.length === input.pubkeys.length && input.pubkeys.length > 0 && - (input.hasWitness === false || - input.value !== undefined); + (input.hasWitness === false || input.value !== undefined)); } function signatureHashType(buffer) { return buffer.readUInt8(buffer.length - 1); diff --git a/src/types.js b/src/types.js index 13d1bc8..76b98cf 100644 --- a/src/types.js +++ b/src/types.js @@ -10,7 +10,9 @@ function BIP32Path(value) { return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); } exports.BIP32Path = BIP32Path; -BIP32Path.toJSON = function () { return 'BIP32 derivation path'; }; +BIP32Path.toJSON = function () { + return 'BIP32 derivation path'; +}; const SATOSHI_MAX = 21 * 1e14; function Satoshi(value) { return typeforce.UInt53(value) && value <= SATOSHI_MAX; @@ -23,11 +25,11 @@ exports.Network = typeforce.compile({ messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), bip32: { public: typeforce.UInt32, - private: typeforce.UInt32 + private: typeforce.UInt32, }, pubKeyHash: typeforce.UInt8, scriptHash: typeforce.UInt8, - wif: typeforce.UInt8 + wif: typeforce.UInt8, }); exports.Buffer256bit = typeforce.BufferN(32); exports.Hash160bit = typeforce.BufferN(20); diff --git a/types/index.d.ts b/types/index.d.ts index 9682271..3512872 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -5,7 +5,7 @@ import * as crypto from './crypto'; import * as networks from './networks'; import * as payments from './payments'; import * as script from './script'; -export { ECPair, address, bip32, crypto, networks, payments, script, }; +export { ECPair, address, bip32, crypto, networks, payments, script }; export { Block } from './block'; export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; From 8329e7456659807ef6acb80c4a39880866c05ef0 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Mon, 4 Mar 2019 00:27:08 +0900 Subject: [PATCH 227/568] ci: check formats --- .travis.yml | 2 +- package.json | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 397dc23..90088bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ node_js: matrix: include: - node_js: "lts/*" - env: TEST_SUITE=standard + env: TEST_SUITE=format:ci - node_js: "lts/*" env: TEST_SUITE=coverage env: diff --git a/package.json b/package.json index b9eb8d8..d615efa 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", - "format": "prettier ts_src/*.ts ts_src/**/*.ts --ignore-path ./.prettierignore --write", + "format": "npm run prettier -- --write", + "format:ci": "npm run prettier -- --check", "integration": "npm run build && npm run nobuild:integration", "nobuild:coverage-report": "nyc report --reporter=lcov", "nobuild:coverage-html": "nyc report --reporter=html", @@ -27,6 +28,7 @@ "nobuild:integration": "mocha --timeout 50000 test/integration/", "nobuild:unit": "mocha", "prepare": "npm run build", + "prettier": "prettier ts_src/*.ts ts_src/**/*.ts --ignore-path ./.prettierignore", "test": "npm run build && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" }, From 070a782149bfead9d1b3039b38d5d42f01138bb4 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Mon, 4 Mar 2019 22:17:29 +0900 Subject: [PATCH 228/568] style: apply format --- ts_src/payments/p2sh.ts | 26 +++++++++++++------------- ts_src/payments/p2wsh.ts | 14 +++++++------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index 281e3df..b2e6bdf 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -1,19 +1,19 @@ -import { Payment, PaymentOpts } from './index' // eslint-disable-line -import { bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line -import * as bscript from '../script' -import * as bcrypto from '../crypto' -import * as lazy from './lazy' -const typef = require('typeforce') -const OPS = bscript.OPS +import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import { bitcoin as BITCOIN_NETWORK } from '../networks'; // eslint-disable-line +import * as bscript from '../script'; +import * as bcrypto from '../crypto'; +import * as lazy from './lazy'; +const typef = require('typeforce'); +const OPS = bscript.OPS; -const bs58check = require('bs58check') +const bs58check = require('bs58check'); -function stacksEqual (a: Array<Buffer>, b: Array<Buffer>): boolean { - if (a.length !== b.length) return false +function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean { + if (a.length !== b.length) return false; - return a.every(function (x, i) { - return x.equals(b[i]) - }) + return a.every(function(x, i) { + return x.equals(b[i]); + }); } // input: [redeemScriptSig ...] {redeemScript} diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 5f9b02d..78e842d 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -1,10 +1,10 @@ -import { Payment, PaymentOpts } from './index' // eslint-disable-line -import { bitcoin as BITCOIN_NETWORK } from '../networks' // eslint-disable-line -import * as bscript from '../script' -import * as bcrypto from '../crypto' -import * as lazy from './lazy' -const typef = require('typeforce') -const OPS = bscript.OPS +import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import { bitcoin as BITCOIN_NETWORK } from '../networks'; // eslint-disable-line +import * as bscript from '../script'; +import * as bcrypto from '../crypto'; +import * as lazy from './lazy'; +const typef = require('typeforce'); +const OPS = bscript.OPS; const bech32 = require('bech32'); From 763cb8b92312b817b657489b34d1dc3398932502 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Mon, 4 Mar 2019 22:17:54 +0900 Subject: [PATCH 229/568] ci: check formats in npm test --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d615efa..8e40c29 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "nobuild:unit": "mocha", "prepare": "npm run build", "prettier": "prettier ts_src/*.ts ts_src/**/*.ts --ignore-path ./.prettierignore", - "test": "npm run build && npm run nobuild:coverage", + "test": "npm run build && npm run format:ci && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" }, "repository": { From 0729fde36fc13250083c1a085c8dde828607fc02 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 5 Mar 2019 15:11:20 +0900 Subject: [PATCH 230/568] Remove eslint comments. --- src/payments/p2ms.js | 11 ++++------- src/payments/p2sh.js | 2 +- src/payments/p2wsh.js | 2 +- test/ecpair.js | 2 -- ts_src/payments/embed.ts | 2 +- ts_src/payments/index.ts | 2 +- ts_src/payments/p2ms.ts | 15 +++++---------- ts_src/payments/p2pk.ts | 2 +- ts_src/payments/p2pkh.ts | 2 +- ts_src/payments/p2sh.ts | 4 ++-- ts_src/payments/p2wpkh.ts | 2 +- ts_src/payments/p2wsh.ts | 4 ++-- 12 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index 4e3c115..1e51faa 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -25,7 +25,7 @@ function p2ms(a, opts) { opts = Object.assign({ validate: true }, opts || {}); function isAcceptableSignature(x) { return (bscript.isCanonicalScriptSignature(x) || - (opts.allowIncomplete && x === OPS.OP_0) !== undefined); // eslint-disable-line + (opts.allowIncomplete && x === OPS.OP_0) !== undefined); } typef({ network: typef.maybe(typef.Object), @@ -45,8 +45,8 @@ function p2ms(a, opts) { return; decoded = true; chunks = bscript.decompile(output); - o.m = chunks[0] - OP_INT_BASE; // eslint-disable-line - o.n = chunks[chunks.length - 2] - OP_INT_BASE; // eslint-disable-line + o.m = chunks[0] - OP_INT_BASE; + o.n = chunks[chunks.length - 2] - OP_INT_BASE; o.pubkeys = chunks.slice(1, -2); } lazy.prop(o, 'output', function () { @@ -100,10 +100,7 @@ function p2ms(a, opts) { throw new TypeError('Output is invalid'); if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid'); - if (o.m <= 0 || // eslint-disable-line - o.n > 16 || // eslint-disable-line - o.m > o.n || // eslint-disable-line - o.n !== chunks.length - 3) + if (o.m <= 0 || o.n > 16 || o.m > o.n || o.n !== chunks.length - 3) throw new TypeError('Output is invalid'); if (!o.pubkeys.every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid'); diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index bb8f0a0..f98ef71 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const networks_1 = require("../networks"); // eslint-disable-line +const networks_1 = require("../networks"); const bscript = require("../script"); const bcrypto = require("../crypto"); const lazy = require("./lazy"); diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index c5eb067..7c71ac3 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const networks_1 = require("../networks"); // eslint-disable-line +const networks_1 = require("../networks"); const bscript = require("../script"); const bcrypto = require("../crypto"); const lazy = require("./lazy"); diff --git a/test/ecpair.js b/test/ecpair.js index 4299470..0ff0e8b 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -1,5 +1,3 @@ -/* eslint-disable no-new */ - const { describe, it, beforeEach } = require('mocha') const assert = require('assert') const proxyquire = require('proxyquire') diff --git a/ts_src/payments/embed.ts b/ts_src/payments/embed.ts index f7225a7..d7a2b0f 100644 --- a/ts_src/payments/embed.ts +++ b/ts_src/payments/embed.ts @@ -1,4 +1,4 @@ -import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import { Payment, PaymentOpts } from './index'; import * as bscript from '../script'; import * as lazy from './lazy'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; diff --git a/ts_src/payments/index.ts b/ts_src/payments/index.ts index 9791661..6727eeb 100644 --- a/ts_src/payments/index.ts +++ b/ts_src/payments/index.ts @@ -1,4 +1,4 @@ -import { Network } from '../networks'; // eslint-disable-line +import { Network } from '../networks'; import { p2data as embed } from './embed'; import { p2ms } from './p2ms'; import { p2pk } from './p2pk'; diff --git a/ts_src/payments/p2ms.ts b/ts_src/payments/p2ms.ts index fce7616..766f341 100644 --- a/ts_src/payments/p2ms.ts +++ b/ts_src/payments/p2ms.ts @@ -1,4 +1,4 @@ -import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import { Payment, PaymentOpts } from './index'; import * as bscript from '../script'; import * as lazy from './lazy'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; @@ -32,7 +32,7 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { return ( bscript.isCanonicalScriptSignature(<Buffer>x) || (opts!.allowIncomplete && <number>x === OPS.OP_0) !== undefined - ); // eslint-disable-line + ); } typef( @@ -58,8 +58,8 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { if (decoded) return; decoded = true; chunks = <Array<Buffer | number>>bscript.decompile(output); - o.m = <number>chunks[0] - OP_INT_BASE; // eslint-disable-line - o.n = <number>chunks[chunks.length - 2] - OP_INT_BASE; // eslint-disable-line + o.m = <number>chunks[0] - OP_INT_BASE; + o.n = <number>chunks[chunks.length - 2] - OP_INT_BASE; o.pubkeys = <Array<Buffer>>chunks.slice(1, -2); } @@ -115,12 +115,7 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid'); - if ( - o.m! <= 0 || // eslint-disable-line - o.n! > 16 || // eslint-disable-line - o.m! > o.n! || // eslint-disable-line - o.n !== chunks.length - 3 - ) + if (o.m! <= 0 || o.n! > 16 || o.m! > o.n! || o.n !== chunks.length - 3) throw new TypeError('Output is invalid'); if (!o.pubkeys!.every(x => ecc.isPoint(x))) throw new TypeError('Output is invalid'); diff --git a/ts_src/payments/p2pk.ts b/ts_src/payments/p2pk.ts index e17a8f2..69fb404 100644 --- a/ts_src/payments/p2pk.ts +++ b/ts_src/payments/p2pk.ts @@ -1,4 +1,4 @@ -import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import { Payment, PaymentOpts } from './index'; import * as bscript from '../script'; import * as lazy from './lazy'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; diff --git a/ts_src/payments/p2pkh.ts b/ts_src/payments/p2pkh.ts index ccac25d..63594ad 100644 --- a/ts_src/payments/p2pkh.ts +++ b/ts_src/payments/p2pkh.ts @@ -1,4 +1,4 @@ -import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import { Payment, PaymentOpts } from './index'; import * as bscript from '../script'; import * as bcrypto from '../crypto'; import * as lazy from './lazy'; diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index b2e6bdf..b7df1dc 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -1,5 +1,5 @@ -import { Payment, PaymentOpts } from './index'; // eslint-disable-line -import { bitcoin as BITCOIN_NETWORK } from '../networks'; // eslint-disable-line +import { Payment, PaymentOpts } from './index'; +import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; import * as bcrypto from '../crypto'; import * as lazy from './lazy'; diff --git a/ts_src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts index ec7ed40..04f0e92 100644 --- a/ts_src/payments/p2wpkh.ts +++ b/ts_src/payments/p2wpkh.ts @@ -1,4 +1,4 @@ -import { Payment, PaymentOpts } from './index'; // eslint-disable-line +import { Payment, PaymentOpts } from './index'; import * as bscript from '../script'; import * as bcrypto from '../crypto'; import * as lazy from './lazy'; diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 78e842d..28a235c 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -1,5 +1,5 @@ -import { Payment, PaymentOpts } from './index'; // eslint-disable-line -import { bitcoin as BITCOIN_NETWORK } from '../networks'; // eslint-disable-line +import { Payment, PaymentOpts } from './index'; +import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; import * as bcrypto from '../crypto'; import * as lazy from './lazy'; From e8857d3a8499b1cc51f89c32db8a594752e57884 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 5 Mar 2019 15:32:25 +0900 Subject: [PATCH 231/568] Update package-lock.json --- package-lock.json | 1576 ++------------------------------------------- 1 file changed, 70 insertions(+), 1506 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3009813..4af2f69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,124 +4,10 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", - "dev": true - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true - }, - "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" }, "balanced-match": { "version": "1.0.0", @@ -143,14 +29,16 @@ "integrity": "sha512-yuVFUvrNcoJi0sv5phmqc6P+Fl1HjRDRNOOkHY2X/3LBy2bIGNSFx4fZ95HMaXHupuS7cZR15AsvtmCIF4UEyg==" }, "bindings": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz", - "integrity": "sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } }, "bip32": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-1.0.2.tgz", - "integrity": "sha512-kedLYj8yvYzND+EfzeoMSlGiN7ImiRBF/MClJSZPkMfcU+OQO7ZpL5L/Yg+TunebBZIHhunstiQF//KLKSF5rg==", + "version": "git+https://github.com/junderw/bip32.git#0df550ad047210fe5cfcc189bb78db645f2f1a89", + "from": "git+https://github.com/junderw/bip32.git#typeScript", "requires": { "bs58check": "^2.1.1", "create-hash": "^1.2.0", @@ -242,61 +130,6 @@ "safe-buffer": "^5.1.2" } }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - } - } - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true - }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -306,48 +139,6 @@ "safe-buffer": "^5.0.1" } }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", @@ -360,30 +151,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -409,17 +176,6 @@ "sha.js": "^2.4.8" } }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -429,41 +185,6 @@ "ms": "2.0.0" } }, - "debug-log": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", - "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "deglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.1.tgz", - "integrity": "sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw==", - "dev": true, - "requires": { - "find-root": "^1.0.0", - "glob": "^7.0.5", - "ignore": "^3.0.9", - "pkg-config": "^1.1.0", - "run-parallel": "^1.1.2", - "uniq": "^1.0.1" - } - }, "dhttp": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dhttp/-/dhttp-3.0.3.tgz", @@ -479,15 +200,6 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, "elliptic": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", @@ -502,224 +214,25 @@ "minimalistic-crypto-utils": "^1.0.0" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "eslint": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.18.2.tgz", - "integrity": "sha512-qy4i3wODqKMYfz9LUI8N2qYDkHkoieTbiHpMrYUI/WbjhXJQr7lI4VngixTgaG+yHX+NBCv7nW4hA0ShbvaNKw==", + "eslint-plugin-typescript": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-typescript/-/eslint-plugin-typescript-0.14.0.tgz", + "integrity": "sha512-2u1WnnDF2mkWWgU1lFQ2RjypUlmRoBEvQN02y9u+IL12mjWlkKFGEBnVsjs9Y8190bfPQCvWly1c2rYYUSOxWw==", "dev": true, "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.2", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" + "requireindex": "~1.1.0" } }, - "eslint-config-standard": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-11.0.0.tgz", - "integrity": "sha512-oDdENzpViEe5fwuRCWla7AXQd++/oyIp8zP+iP9jiUPG6NBj3SHgdgtl/kTn00AjeN+1HNvavTKmYbMo+xMOlw==", - "dev": true - }, - "eslint-config-standard-jsx": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-5.0.0.tgz", - "integrity": "sha512-rLToPAEqLMPBfWnYTu6xRhm2OWziS2n40QFqJ8jAM8NSVzeVKTa3nclhsU4DpPJQRY60F34Oo1wi/71PN/eITg==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "eslint-module-utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz", - "integrity": "sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "eslint-plugin-import": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.9.0.tgz", - "integrity": "sha1-JgAu+/ylmJtyiKwEdQi9JPIXsWk=", - "dev": true, - "requires": { - "builtin-modules": "^1.1.1", - "contains-path": "^0.1.0", - "debug": "^2.6.8", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.1", - "eslint-module-utils": "^2.1.1", - "has": "^1.0.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.3", - "read-pkg-up": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - } - } - }, - "eslint-plugin-node": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz", - "integrity": "sha512-Q/Cc2sW1OAISDS+Ji6lZS2KV4b7ueA/WydVWd1BECTQwVvfQy5JAi3glhINoKzoMnfnuRgNP+ZWKrGAbp3QDxw==", - "dev": true, - "requires": { - "ignore": "^3.3.6", - "minimatch": "^3.0.4", - "resolve": "^1.3.3", - "semver": "^5.4.1" - } - }, - "eslint-plugin-promise": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.7.0.tgz", - "integrity": "sha512-2WO+ZFh7vxUKRfR0cOIMrWgYKdR6S1AlOezw6pC52B6oYpd5WFghN+QHxvrRdZMtbo8h3dfUZ2o1rWb0UPbKtg==", - "dev": true - }, - "eslint-plugin-react": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz", - "integrity": "sha512-KC7Snr4YsWZD5flu6A5c0AcIZidzW3Exbqp7OT67OaD2AppJtlBr/GuPrW/vaQM/yfZotEvKAdrxrO+v8vwYJA==", - "dev": true, - "requires": { - "doctrine": "^2.0.2", - "has": "^1.0.1", - "jsx-ast-utils": "^2.0.1", - "prop-types": "^15.6.0" - } - }, - "eslint-plugin-standard": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz", - "integrity": "sha1-NNDJFbRe3G8BA5PH7vOCOwhWXPI=", - "dev": true - }, "eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.2.tgz", + "integrity": "sha512-5q1+B/ogmHl8+paxtOKx38Z8LtWkVGuNt3+GQNErqwLl6ViNp/gdJGMCjZNxZ8j/VYjDNZ2Fo+eQc1TAVPIzbg==", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -732,31 +245,6 @@ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, - "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "dev": true, - "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", @@ -772,59 +260,10 @@ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "dev": true }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "fill-keys": { "version": "1.0.2", @@ -836,57 +275,12 @@ "merge-descriptors": "~1.0.0" } }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", - "dev": true, - "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -901,54 +295,18 @@ "path-is-absolute": "^1.0.0" } }, - "globals": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", - "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==", - "dev": true - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, "hash-base": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", @@ -989,33 +347,6 @@ "integrity": "sha512-j1jog3tDfhpWlqbVbh29qc7FG7w+NT4ed+QQFGqvww83+50AzzretB7wykZGOe28mBdvCYH3GdHaVWJQ2lJ/4w==", "dev": true }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1031,209 +362,18 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, "is-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", "dev": true }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", "dev": true }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", - "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "jsx-ast-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", - "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", - "dev": true, - "requires": { - "array-includes": "^3.0.3" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -1255,12 +395,6 @@ "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", "integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY=" }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, "minimaldata": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/minimaldata/-/minimaldata-1.0.2.tgz", @@ -1336,35 +470,11 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, "nan": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, "nyc": { "version": "11.9.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.9.0.tgz", @@ -3997,18 +3107,6 @@ } } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", - "dev": true - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4018,101 +3116,18 @@ "wrappy": "1" } }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, "pbkdf2": { "version": "3.0.17", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", @@ -4126,106 +3141,12 @@ "sha.js": "^2.4.8" } }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "prettier": { + "version": "1.16.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", + "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==", "dev": true }, - "pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "pkg-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", - "dev": true, - "requires": { - "debug-log": "^1.0.0", - "find-root": "^1.0.0", - "xtend": "^4.0.1" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", - "dev": true, - "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" - } - }, "proxyquire": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.0.tgz", @@ -4237,12 +3158,6 @@ "resolve": "~1.8.1" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "pushdata-bitcoin": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz", @@ -4252,58 +3167,18 @@ } }, "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "requires": { "safe-buffer": "^5.1.0" } }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - } + "requireindex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz", + "integrity": "sha1-5UBLgVV+91225JxacgBIk/4D4WI=", + "dev": true }, "resolve": { "version": "1.8.1", @@ -4314,47 +3189,6 @@ "path-parse": "^1.0.5" } }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, "ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -4364,51 +3198,15 @@ "inherits": "^2.0.1" } }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "dev": true, - "requires": { - "rx-lite": "*" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, "sha.js": { @@ -4420,165 +3218,12 @@ "safe-buffer": "^5.0.1" } }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "standard": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/standard/-/standard-11.0.1.tgz", - "integrity": "sha512-nu0jAcHiSc8H+gJCXeiziMVZNDYi8MuqrYJKxTgjP4xKXZMKm311boqQIzDrYI/ktosltxt2CbDjYQs9ANC8IA==", - "dev": true, - "requires": { - "eslint": "~4.18.0", - "eslint-config-standard": "11.0.0", - "eslint-config-standard-jsx": "5.0.0", - "eslint-plugin-import": "~2.9.0", - "eslint-plugin-node": "~6.0.0", - "eslint-plugin-promise": "~3.7.0", - "eslint-plugin-react": "~7.7.0", - "eslint-plugin-standard": "~3.0.1", - "standard-engine": "~8.0.0" - } - }, - "standard-engine": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-8.0.1.tgz", - "integrity": "sha512-LA531C3+nljom/XRvdW/hGPXwmilRkaRkENhO3FAGF1Vtq/WtCXzgmnc5S6vUHHsgv534MRy02C1ikMwZXC+tw==", - "dev": true, - "requires": { - "deglob": "^2.1.0", - "get-stdin": "^6.0.0", - "minimist": "^1.1.0", - "pkg-conf": "^2.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", @@ -4588,32 +3233,6 @@ "has-flag": "^3.0.0" } }, - "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", - "dev": true, - "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, "tiny-secp256k1": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.0.1.tgz", @@ -4626,63 +3245,44 @@ "nan": "^2.10.0" } }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, "typeforce": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "typescript": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", + "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", "dev": true }, - "unorm": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.4.1.tgz", - "integrity": "sha1-NkIA1fE2RsqLzURJAnEzVhR5IwA=", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "typescript-eslint-parser": { + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-21.0.2.tgz", + "integrity": "sha512-u+pj4RVJBr4eTzj0n5npoXD/oRthvfUCjSKndhNI714MG0mQq2DJw5WP7qmonRNIFgmZuvdDOH3BHm9iOjIAfg==", "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0", + "typescript-estree": "5.3.0" } }, + "typescript-estree": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/typescript-estree/-/typescript-estree-5.3.0.tgz", + "integrity": "sha512-Vu0KmYdSCkpae+J48wsFC1ti19Hq3Wi/lODUaE+uesc3gzqhWbZ5itWbsjylLVbjNW4K41RqDzSfnaYNbmEiMQ==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + } + }, + "unorm": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz", + "integrity": "sha512-sMfSWoiRaXXeDZSXC+YRZ23H4xchQpwxjpw1tmfR+kgbBCaOgln4NI0LXejJIhnBuKINrB3WRn+ZI8IWssirVw==", + "dev": true + }, "varuint-bitcoin": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz", @@ -4691,15 +3291,6 @@ "safe-buffer": "^5.1.1" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "wif": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", @@ -4708,38 +3299,11 @@ "bs58check": "<3.0.0" } }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true } } } From a567a978776465ed9240cac35b753e5da52b9355 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Wed, 6 Mar 2019 21:38:36 +0900 Subject: [PATCH 232/568] style: fix glob pattern for prettier --- package.json | 2 +- ts_src/templates/multisig/index.ts | 9 +-- ts_src/templates/multisig/input.ts | 31 +++++---- ts_src/templates/multisig/output.ts | 45 +++++++------ ts_src/templates/pubkey/index.ts | 9 +-- ts_src/templates/pubkey/input.ts | 15 +++-- ts_src/templates/pubkey/output.ts | 16 +++-- ts_src/templates/pubkeyhash/index.ts | 9 +-- ts_src/templates/pubkeyhash/input.ts | 14 ++-- ts_src/templates/pubkeyhash/output.ts | 16 +++-- ts_src/templates/scripthash/index.ts | 9 +-- ts_src/templates/scripthash/input.ts | 69 ++++++++++++-------- ts_src/templates/scripthash/output.ts | 16 +++-- ts_src/templates/witnesscommitment/index.ts | 6 +- ts_src/templates/witnesscommitment/output.ts | 40 +++++++----- ts_src/templates/witnesspubkeyhash/index.ts | 9 +-- ts_src/templates/witnesspubkeyhash/input.ts | 18 +++-- ts_src/templates/witnesspubkeyhash/output.ts | 16 ++--- ts_src/templates/witnessscripthash/index.ts | 9 +-- ts_src/templates/witnessscripthash/input.ts | 56 ++++++++++------ ts_src/templates/witnessscripthash/output.ts | 16 ++--- 21 files changed, 240 insertions(+), 190 deletions(-) diff --git a/package.json b/package.json index e60b6a9..b98f412 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "nobuild:integration": "mocha --timeout 50000 test/integration/", "nobuild:unit": "mocha", "prepare": "npm run build", - "prettier": "prettier ts_src/*.ts ts_src/**/*.ts --ignore-path ./.prettierignore", + "prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore", "test": "npm run build && npm run format:ci && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" }, diff --git a/ts_src/templates/multisig/index.ts b/ts_src/templates/multisig/index.ts index 42aeb7b..aa2bdcb 100644 --- a/ts_src/templates/multisig/index.ts +++ b/ts_src/templates/multisig/index.ts @@ -1,7 +1,4 @@ -import * as input from './input' -import * as output from './output' +import * as input from './input'; +import * as output from './output'; -export { - input, - output, -} +export { input, output }; diff --git a/ts_src/templates/multisig/input.ts b/ts_src/templates/multisig/input.ts index 1ec2874..222ff10 100644 --- a/ts_src/templates/multisig/input.ts +++ b/ts_src/templates/multisig/input.ts @@ -1,21 +1,30 @@ // OP_0 [signatures ...] -import * as bscript from '../../script' -import { OPS } from '../../script' +import * as bscript from '../../script'; +import { OPS } from '../../script'; -function partialSignature (value: number | Buffer): boolean { - return value === OPS.OP_0 || bscript.isCanonicalScriptSignature(<Buffer>value) +function partialSignature(value: number | Buffer): boolean { + return ( + value === OPS.OP_0 || bscript.isCanonicalScriptSignature(<Buffer>value) + ); } -export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script) - if (chunks.length < 2) return false - if (chunks[0] !== OPS.OP_0) return false +export function check( + script: Buffer | Array<number | Buffer>, + allowIncomplete?: boolean, +): boolean { + const chunks = <Array<number | Buffer>>bscript.decompile(script); + if (chunks.length < 2) return false; + if (chunks[0] !== OPS.OP_0) return false; if (allowIncomplete) { - return chunks.slice(1).every(partialSignature) + return chunks.slice(1).every(partialSignature); } - return (<Array<Buffer>>chunks.slice(1)).every(bscript.isCanonicalScriptSignature) + return (<Array<Buffer>>chunks.slice(1)).every( + bscript.isCanonicalScriptSignature, + ); } -check.toJSON = function () { return 'multisig input' } +check.toJSON = function() { + return 'multisig input'; +}; diff --git a/ts_src/templates/multisig/output.ts b/ts_src/templates/multisig/output.ts index e46fa78..040649b 100644 --- a/ts_src/templates/multisig/output.ts +++ b/ts_src/templates/multisig/output.ts @@ -1,27 +1,32 @@ // m [pubKeys ...] n OP_CHECKMULTISIG -import * as bscript from '../../script' -import * as types from '../../types' -import { OPS } from '../../script' -const OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1 +import * as bscript from '../../script'; +import * as types from '../../types'; +import { OPS } from '../../script'; +const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 -export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script) +export function check( + script: Buffer | Array<number | Buffer>, + allowIncomplete?: boolean, +): boolean { + const chunks = <Array<number | Buffer>>bscript.decompile(script); - if (chunks.length < 4) return false - if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false - if (!types.Number(chunks[0])) return false - if (!types.Number(chunks[chunks.length - 2])) return false - const m = <number>chunks[0] - OP_INT_BASE - const n = <number>chunks[chunks.length - 2] - OP_INT_BASE + if (chunks.length < 4) return false; + if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false; + if (!types.Number(chunks[0])) return false; + if (!types.Number(chunks[chunks.length - 2])) return false; + const m = <number>chunks[0] - OP_INT_BASE; + const n = <number>chunks[chunks.length - 2] - OP_INT_BASE; - if (m <= 0) return false - if (n > 16) return false - if (m > n) return false - if (n !== chunks.length - 3) return false - if (allowIncomplete) return true + if (m <= 0) return false; + if (n > 16) return false; + if (m > n) return false; + if (n !== chunks.length - 3) return false; + if (allowIncomplete) return true; - const keys = <Array<Buffer>> chunks.slice(1, -2) - return keys.every(bscript.isCanonicalPubKey) + const keys = <Array<Buffer>>chunks.slice(1, -2); + return keys.every(bscript.isCanonicalPubKey); } -check.toJSON = function () { return 'multi-sig output' } +check.toJSON = function() { + return 'multi-sig output'; +}; diff --git a/ts_src/templates/pubkey/index.ts b/ts_src/templates/pubkey/index.ts index 42aeb7b..aa2bdcb 100644 --- a/ts_src/templates/pubkey/index.ts +++ b/ts_src/templates/pubkey/index.ts @@ -1,7 +1,4 @@ -import * as input from './input' -import * as output from './output' +import * as input from './input'; +import * as output from './output'; -export { - input, - output, -} +export { input, output }; diff --git a/ts_src/templates/pubkey/input.ts b/ts_src/templates/pubkey/input.ts index f9245e4..e9ea1be 100644 --- a/ts_src/templates/pubkey/input.ts +++ b/ts_src/templates/pubkey/input.ts @@ -1,11 +1,14 @@ // {signature} -import * as bscript from '../../script' +import * as bscript from '../../script'; -export function check (script: Buffer | Array<number | Buffer>): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const chunks = <Array<number | Buffer>>bscript.decompile(script); - return chunks.length === 1 && - bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) + return ( + chunks.length === 1 && bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) + ); } -check.toJSON = function () { return 'pubKey input' } +check.toJSON = function() { + return 'pubKey input'; +}; diff --git a/ts_src/templates/pubkey/output.ts b/ts_src/templates/pubkey/output.ts index aa321b3..8378870 100644 --- a/ts_src/templates/pubkey/output.ts +++ b/ts_src/templates/pubkey/output.ts @@ -1,13 +1,17 @@ // {pubKey} OP_CHECKSIG -import * as bscript from '../../script' -import { OPS } from '../../script' +import * as bscript from '../../script'; +import { OPS } from '../../script'; -export function check (script: Buffer | Array<number | Buffer>): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const chunks = <Array<number | Buffer>>bscript.decompile(script); - return chunks.length === 2 && + return ( + chunks.length === 2 && bscript.isCanonicalPubKey(<Buffer>chunks[0]) && chunks[1] === OPS.OP_CHECKSIG + ); } -check.toJSON = function () { return 'pubKey output' } +check.toJSON = function() { + return 'pubKey output'; +}; diff --git a/ts_src/templates/pubkeyhash/index.ts b/ts_src/templates/pubkeyhash/index.ts index 42aeb7b..aa2bdcb 100644 --- a/ts_src/templates/pubkeyhash/index.ts +++ b/ts_src/templates/pubkeyhash/index.ts @@ -1,7 +1,4 @@ -import * as input from './input' -import * as output from './output' +import * as input from './input'; +import * as output from './output'; -export { - input, - output, -} +export { input, output }; diff --git a/ts_src/templates/pubkeyhash/input.ts b/ts_src/templates/pubkeyhash/input.ts index f27f809..c091764 100644 --- a/ts_src/templates/pubkeyhash/input.ts +++ b/ts_src/templates/pubkeyhash/input.ts @@ -1,12 +1,16 @@ // {signature} {pubKey} -import * as bscript from '../../script' +import * as bscript from '../../script'; -export function check (script: Buffer | Array<number | Buffer>): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const chunks = <Array<number | Buffer>>bscript.decompile(script); - return chunks.length === 2 && + return ( + chunks.length === 2 && bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) && bscript.isCanonicalPubKey(<Buffer>chunks[1]) + ); } -check.toJSON = function () { return 'pubKeyHash input' } +check.toJSON = function() { + return 'pubKeyHash input'; +}; diff --git a/ts_src/templates/pubkeyhash/output.ts b/ts_src/templates/pubkeyhash/output.ts index 242cb0e..a5c5c9b 100644 --- a/ts_src/templates/pubkeyhash/output.ts +++ b/ts_src/templates/pubkeyhash/output.ts @@ -1,16 +1,20 @@ // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG -import * as bscript from '../../script' -import { OPS } from '../../script' +import * as bscript from '../../script'; +import { OPS } from '../../script'; -export function check (script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const buffer = bscript.compile(script); - return buffer.length === 25 && + return ( + buffer.length === 25 && buffer[0] === OPS.OP_DUP && buffer[1] === OPS.OP_HASH160 && buffer[2] === 0x14 && buffer[23] === OPS.OP_EQUALVERIFY && buffer[24] === OPS.OP_CHECKSIG + ); } -check.toJSON = function () { return 'pubKeyHash output' } +check.toJSON = function() { + return 'pubKeyHash output'; +}; diff --git a/ts_src/templates/scripthash/index.ts b/ts_src/templates/scripthash/index.ts index 42aeb7b..aa2bdcb 100644 --- a/ts_src/templates/scripthash/index.ts +++ b/ts_src/templates/scripthash/index.ts @@ -1,7 +1,4 @@ -import * as input from './input' -import * as output from './output' +import * as input from './input'; +import * as output from './output'; -export { - input, - output, -} +export { input, output }; diff --git a/ts_src/templates/scripthash/input.ts b/ts_src/templates/scripthash/input.ts index 7b3107d..0b86d63 100644 --- a/ts_src/templates/scripthash/input.ts +++ b/ts_src/templates/scripthash/input.ts @@ -1,46 +1,61 @@ // <scriptSig> {serialized scriptPubKey script} -import * as bscript from '../../script' -import * as p2ms from '../multisig' -import * as p2pk from '../pubkey' -import * as p2pkh from '../pubkeyhash' -import * as p2wpkho from '../witnesspubkeyhash/output' -import * as p2wsho from '../witnessscripthash/output' +import * as bscript from '../../script'; +import * as p2ms from '../multisig'; +import * as p2pk from '../pubkey'; +import * as p2pkh from '../pubkeyhash'; +import * as p2wpkho from '../witnesspubkeyhash/output'; +import * as p2wsho from '../witnessscripthash/output'; +export function check( + script: Buffer | Array<number | Buffer>, + allowIncomplete?: boolean, +): boolean { + const chunks = bscript.decompile(script)!; + if (chunks.length < 1) return false; + const lastChunk = chunks[chunks.length - 1]; + if (!Buffer.isBuffer(lastChunk)) return false; -export function check (script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean { - const chunks = bscript.decompile(script)! - if (chunks.length < 1) return false - - const lastChunk = chunks[chunks.length - 1] - if (!Buffer.isBuffer(lastChunk)) return false - - const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1)))! - const redeemScriptChunks = bscript.decompile(lastChunk) + const scriptSigChunks = bscript.decompile( + bscript.compile(chunks.slice(0, -1)), + )!; + const redeemScriptChunks = bscript.decompile(lastChunk); // is redeemScript a valid script? - if (!redeemScriptChunks) return false + if (!redeemScriptChunks) return false; // is redeemScriptSig push only? - if (!bscript.isPushOnly(scriptSigChunks)) return false + if (!bscript.isPushOnly(scriptSigChunks)) return false; // is witness? if (chunks.length === 1) { - return p2wsho.check(redeemScriptChunks) || - p2wpkho.check(redeemScriptChunks) + return ( + p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks) + ); } // match types - if (p2pkh.input.check(scriptSigChunks) && - p2pkh.output.check(redeemScriptChunks)) return true + if ( + p2pkh.input.check(scriptSigChunks) && + p2pkh.output.check(redeemScriptChunks) + ) + return true; - if (p2ms.input.check(scriptSigChunks, allowIncomplete) && - p2ms.output.check(redeemScriptChunks)) return true + if ( + p2ms.input.check(scriptSigChunks, allowIncomplete) && + p2ms.output.check(redeemScriptChunks) + ) + return true; - if (p2pk.input.check(scriptSigChunks) && - p2pk.output.check(redeemScriptChunks)) return true + if ( + p2pk.input.check(scriptSigChunks) && + p2pk.output.check(redeemScriptChunks) + ) + return true; - return false + return false; } -check.toJSON = function () { return 'scriptHash input' } +check.toJSON = function() { + return 'scriptHash input'; +}; diff --git a/ts_src/templates/scripthash/output.ts b/ts_src/templates/scripthash/output.ts index 0b968bf..6ff138a 100644 --- a/ts_src/templates/scripthash/output.ts +++ b/ts_src/templates/scripthash/output.ts @@ -1,14 +1,18 @@ // OP_HASH160 {scriptHash} OP_EQUAL -import * as bscript from '../../script' -import { OPS } from '../../script' +import * as bscript from '../../script'; +import { OPS } from '../../script'; -export function check (script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const buffer = bscript.compile(script); - return buffer.length === 23 && + return ( + buffer.length === 23 && buffer[0] === OPS.OP_HASH160 && buffer[1] === 0x14 && buffer[22] === OPS.OP_EQUAL + ); } -check.toJSON = function () { return 'scriptHash output' } +check.toJSON = function() { + return 'scriptHash output'; +}; diff --git a/ts_src/templates/witnesscommitment/index.ts b/ts_src/templates/witnesscommitment/index.ts index c36a2b8..d5c166d 100644 --- a/ts_src/templates/witnesscommitment/index.ts +++ b/ts_src/templates/witnesscommitment/index.ts @@ -1,5 +1,3 @@ -import * as output from './output' +import * as output from './output'; -export { - output, -} +export { output }; diff --git a/ts_src/templates/witnesscommitment/output.ts b/ts_src/templates/witnesscommitment/output.ts index 0bdf11c..29beb39 100644 --- a/ts_src/templates/witnesscommitment/output.ts +++ b/ts_src/templates/witnesscommitment/output.ts @@ -1,36 +1,40 @@ // OP_RETURN {aa21a9ed} {commitment} -import * as bscript from '../../script' -import * as types from '../../types' +import * as bscript from '../../script'; +import * as types from '../../types'; -const typeforce = require('typeforce') -import { OPS } from '../../script' +const typeforce = require('typeforce'); +import { OPS } from '../../script'; -const HEADER: Buffer = Buffer.from('aa21a9ed', 'hex') +const HEADER: Buffer = Buffer.from('aa21a9ed', 'hex'); -export function check (script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const buffer = bscript.compile(script); - return buffer.length > 37 && + return ( + buffer.length > 37 && buffer[0] === OPS.OP_RETURN && buffer[1] === 0x24 && buffer.slice(2, 6).equals(HEADER) + ); } -check.toJSON = function () { return 'Witness commitment output' } +check.toJSON = function() { + return 'Witness commitment output'; +}; -export function encode (commitment: Buffer): Buffer { - typeforce(types.Hash256bit, commitment) +export function encode(commitment: Buffer): Buffer { + typeforce(types.Hash256bit, commitment); - const buffer = Buffer.allocUnsafe(36) - HEADER.copy(buffer, 0) - commitment.copy(buffer, 4) + const buffer = Buffer.allocUnsafe(36); + HEADER.copy(buffer, 0); + commitment.copy(buffer, 4); - return bscript.compile([OPS.OP_RETURN, buffer]) + return bscript.compile([OPS.OP_RETURN, buffer]); } -export function decode (buffer: Buffer): Buffer { - typeforce(check, buffer) +export function decode(buffer: Buffer): Buffer { + typeforce(check, buffer); - return (<Buffer>bscript.decompile(buffer)![1]).slice(4, 36) + return (<Buffer>bscript.decompile(buffer)![1]).slice(4, 36); } diff --git a/ts_src/templates/witnesspubkeyhash/index.ts b/ts_src/templates/witnesspubkeyhash/index.ts index 42aeb7b..aa2bdcb 100644 --- a/ts_src/templates/witnesspubkeyhash/index.ts +++ b/ts_src/templates/witnesspubkeyhash/index.ts @@ -1,7 +1,4 @@ -import * as input from './input' -import * as output from './output' +import * as input from './input'; +import * as output from './output'; -export { - input, - output, -} +export { input, output }; diff --git a/ts_src/templates/witnesspubkeyhash/input.ts b/ts_src/templates/witnesspubkeyhash/input.ts index 91b8357..22fc2cf 100644 --- a/ts_src/templates/witnesspubkeyhash/input.ts +++ b/ts_src/templates/witnesspubkeyhash/input.ts @@ -1,16 +1,20 @@ // {signature} {pubKey} -import * as bscript from '../../script' +import * as bscript from '../../script'; -function isCompressedCanonicalPubKey (pubKey: Buffer): boolean { - return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33 +function isCompressedCanonicalPubKey(pubKey: Buffer): boolean { + return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; } -export function check (script: Buffer | Array<number | Buffer>): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const chunks = <Array<number | Buffer>>bscript.decompile(script); - return chunks.length === 2 && + return ( + chunks.length === 2 && bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) && isCompressedCanonicalPubKey(<Buffer>chunks[1]) + ); } -check.toJSON = function () { return 'witnessPubKeyHash input' } +check.toJSON = function() { + return 'witnessPubKeyHash input'; +}; diff --git a/ts_src/templates/witnesspubkeyhash/output.ts b/ts_src/templates/witnesspubkeyhash/output.ts index bc7a3a0..09c49d0 100644 --- a/ts_src/templates/witnesspubkeyhash/output.ts +++ b/ts_src/templates/witnesspubkeyhash/output.ts @@ -1,13 +1,13 @@ // OP_0 {pubKeyHash} -import * as bscript from '../../script' -import { OPS } from '../../script' +import * as bscript from '../../script'; +import { OPS } from '../../script'; -export function check (script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const buffer = bscript.compile(script); - return buffer.length === 22 && - buffer[0] === OPS.OP_0 && - buffer[1] === 0x14 + return buffer.length === 22 && buffer[0] === OPS.OP_0 && buffer[1] === 0x14; } -check.toJSON = function () { return 'Witness pubKeyHash output' } +check.toJSON = function() { + return 'Witness pubKeyHash output'; +}; diff --git a/ts_src/templates/witnessscripthash/index.ts b/ts_src/templates/witnessscripthash/index.ts index 42aeb7b..aa2bdcb 100644 --- a/ts_src/templates/witnessscripthash/index.ts +++ b/ts_src/templates/witnessscripthash/index.ts @@ -1,7 +1,4 @@ -import * as input from './input' -import * as output from './output' +import * as input from './input'; +import * as output from './output'; -export { - input, - output, -} +export { input, output }; diff --git a/ts_src/templates/witnessscripthash/input.ts b/ts_src/templates/witnessscripthash/input.ts index 7f7e415..6cf35bf 100644 --- a/ts_src/templates/witnessscripthash/input.ts +++ b/ts_src/templates/witnessscripthash/input.ts @@ -1,36 +1,50 @@ // <scriptSig> {serialized scriptPubKey script} -import * as bscript from '../../script' -const typeforce = require('typeforce') +import * as bscript from '../../script'; +const typeforce = require('typeforce'); -import * as p2ms from '../multisig' -import * as p2pk from '../pubkey' -import * as p2pkh from '../pubkeyhash' +import * as p2ms from '../multisig'; +import * as p2pk from '../pubkey'; +import * as p2pkh from '../pubkeyhash'; -export function check (chunks: Array<Buffer>, allowIncomplete?: boolean): boolean { - typeforce(typeforce.Array, chunks) - if (chunks.length < 1) return false +export function check( + chunks: Array<Buffer>, + allowIncomplete?: boolean, +): boolean { + typeforce(typeforce.Array, chunks); + if (chunks.length < 1) return false; - const witnessScript = chunks[chunks.length - 1] - if (!Buffer.isBuffer(witnessScript)) return false + const witnessScript = chunks[chunks.length - 1]; + if (!Buffer.isBuffer(witnessScript)) return false; - const witnessScriptChunks = bscript.decompile(witnessScript) + const witnessScriptChunks = bscript.decompile(witnessScript); // is witnessScript a valid script? - if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false + if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false; - const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)) + const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); // match types - if (p2pkh.input.check(witnessRawScriptSig) && - p2pkh.output.check(witnessScriptChunks)) return true + if ( + p2pkh.input.check(witnessRawScriptSig) && + p2pkh.output.check(witnessScriptChunks) + ) + return true; - if (p2ms.input.check(witnessRawScriptSig, allowIncomplete) && - p2ms.output.check(witnessScriptChunks)) return true + if ( + p2ms.input.check(witnessRawScriptSig, allowIncomplete) && + p2ms.output.check(witnessScriptChunks) + ) + return true; - if (p2pk.input.check(witnessRawScriptSig) && - p2pk.output.check(witnessScriptChunks)) return true + if ( + p2pk.input.check(witnessRawScriptSig) && + p2pk.output.check(witnessScriptChunks) + ) + return true; - return false + return false; } -check.toJSON = function () { return 'witnessScriptHash input' } +check.toJSON = function() { + return 'witnessScriptHash input'; +}; diff --git a/ts_src/templates/witnessscripthash/output.ts b/ts_src/templates/witnessscripthash/output.ts index deffe1f..430cc4f 100644 --- a/ts_src/templates/witnessscripthash/output.ts +++ b/ts_src/templates/witnessscripthash/output.ts @@ -1,13 +1,13 @@ // OP_0 {scriptHash} -import * as bscript from '../../script' -import { OPS } from '../../script' +import * as bscript from '../../script'; +import { OPS } from '../../script'; -export function check (script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script) +export function check(script: Buffer | Array<number | Buffer>): boolean { + const buffer = bscript.compile(script); - return buffer.length === 34 && - buffer[0] === OPS.OP_0 && - buffer[1] === 0x20 + return buffer.length === 34 && buffer[0] === OPS.OP_0 && buffer[1] === 0x20; } -check.toJSON = function () { return 'Witness scriptHash output' } +check.toJSON = function() { + return 'Witness scriptHash output'; +}; From ee60862df6e6a63d390df9ab523eacd60f5536cf Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 11:20:28 +0900 Subject: [PATCH 233/568] Add JS for prettier glob fix --- src/templates/multisig/input.js | 6 ++++-- src/templates/multisig/output.js | 4 +++- src/templates/pubkey/input.js | 7 ++++--- src/templates/pubkey/output.js | 8 +++++--- src/templates/pubkeyhash/input.js | 8 +++++--- src/templates/pubkeyhash/output.js | 8 +++++--- src/templates/scripthash/input.js | 7 ++++--- src/templates/scripthash/output.js | 8 +++++--- src/templates/witnesscommitment/output.js | 8 +++++--- src/templates/witnesspubkeyhash/input.js | 8 +++++--- src/templates/witnesspubkeyhash/output.js | 8 ++++---- src/templates/witnessscripthash/input.js | 4 +++- src/templates/witnessscripthash/output.js | 8 ++++---- types/templates/multisig/index.d.ts | 2 +- types/templates/pubkey/index.d.ts | 2 +- types/templates/pubkeyhash/index.d.ts | 2 +- types/templates/scripthash/index.d.ts | 2 +- types/templates/witnesscommitment/index.d.ts | 2 +- types/templates/witnesspubkeyhash/index.d.ts | 2 +- types/templates/witnessscripthash/index.d.ts | 2 +- 20 files changed, 63 insertions(+), 43 deletions(-) diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js index f8dd3d3..24a9710 100644 --- a/src/templates/multisig/input.js +++ b/src/templates/multisig/input.js @@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const bscript = require("../../script"); const script_1 = require("../../script"); function partialSignature(value) { - return value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value); + return (value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value)); } function check(script, allowIncomplete) { const chunks = bscript.decompile(script); @@ -18,4 +18,6 @@ function check(script, allowIncomplete) { return chunks.slice(1).every(bscript.isCanonicalScriptSignature); } exports.check = check; -check.toJSON = function () { return 'multisig input'; }; +check.toJSON = function () { + return 'multisig input'; +}; diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.js index 811ed12..64d07bc 100644 --- a/src/templates/multisig/output.js +++ b/src/templates/multisig/output.js @@ -31,4 +31,6 @@ function check(script, allowIncomplete) { return keys.every(bscript.isCanonicalPubKey); } exports.check = check; -check.toJSON = function () { return 'multi-sig output'; }; +check.toJSON = function () { + return 'multi-sig output'; +}; diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js index bc5e566..10d3c07 100644 --- a/src/templates/pubkey/input.js +++ b/src/templates/pubkey/input.js @@ -4,8 +4,9 @@ Object.defineProperty(exports, "__esModule", { value: true }); const bscript = require("../../script"); function check(script) { const chunks = bscript.decompile(script); - return chunks.length === 1 && - bscript.isCanonicalScriptSignature(chunks[0]); + return (chunks.length === 1 && bscript.isCanonicalScriptSignature(chunks[0])); } exports.check = check; -check.toJSON = function () { return 'pubKey input'; }; +check.toJSON = function () { + return 'pubKey input'; +}; diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.js index 043150e..e2f87c2 100644 --- a/src/templates/pubkey/output.js +++ b/src/templates/pubkey/output.js @@ -5,9 +5,11 @@ const bscript = require("../../script"); const script_1 = require("../../script"); function check(script) { const chunks = bscript.decompile(script); - return chunks.length === 2 && + return (chunks.length === 2 && bscript.isCanonicalPubKey(chunks[0]) && - chunks[1] === script_1.OPS.OP_CHECKSIG; + chunks[1] === script_1.OPS.OP_CHECKSIG); } exports.check = check; -check.toJSON = function () { return 'pubKey output'; }; +check.toJSON = function () { + return 'pubKey output'; +}; diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js index 29a684e..f03d391 100644 --- a/src/templates/pubkeyhash/input.js +++ b/src/templates/pubkeyhash/input.js @@ -4,9 +4,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); const bscript = require("../../script"); function check(script) { const chunks = bscript.decompile(script); - return chunks.length === 2 && + return (chunks.length === 2 && bscript.isCanonicalScriptSignature(chunks[0]) && - bscript.isCanonicalPubKey(chunks[1]); + bscript.isCanonicalPubKey(chunks[1])); } exports.check = check; -check.toJSON = function () { return 'pubKeyHash input'; }; +check.toJSON = function () { + return 'pubKeyHash input'; +}; diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.js index 9016c6a..222244b 100644 --- a/src/templates/pubkeyhash/output.js +++ b/src/templates/pubkeyhash/output.js @@ -5,12 +5,14 @@ const bscript = require("../../script"); const script_1 = require("../../script"); function check(script) { const buffer = bscript.compile(script); - return buffer.length === 25 && + return (buffer.length === 25 && buffer[0] === script_1.OPS.OP_DUP && buffer[1] === script_1.OPS.OP_HASH160 && buffer[2] === 0x14 && buffer[23] === script_1.OPS.OP_EQUALVERIFY && - buffer[24] === script_1.OPS.OP_CHECKSIG; + buffer[24] === script_1.OPS.OP_CHECKSIG); } exports.check = check; -check.toJSON = function () { return 'pubKeyHash output'; }; +check.toJSON = function () { + return 'pubKeyHash output'; +}; diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js index c40bc26..5d2b576 100644 --- a/src/templates/scripthash/input.js +++ b/src/templates/scripthash/input.js @@ -24,8 +24,7 @@ function check(script, allowIncomplete) { return false; // is witness? if (chunks.length === 1) { - return p2wsho.check(redeemScriptChunks) || - p2wpkho.check(redeemScriptChunks); + return (p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks)); } // match types if (p2pkh.input.check(scriptSigChunks) && @@ -40,4 +39,6 @@ function check(script, allowIncomplete) { return false; } exports.check = check; -check.toJSON = function () { return 'scriptHash input'; }; +check.toJSON = function () { + return 'scriptHash input'; +}; diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.js index bd38d5a..5fd2f65 100644 --- a/src/templates/scripthash/output.js +++ b/src/templates/scripthash/output.js @@ -5,10 +5,12 @@ const bscript = require("../../script"); const script_1 = require("../../script"); function check(script) { const buffer = bscript.compile(script); - return buffer.length === 23 && + return (buffer.length === 23 && buffer[0] === script_1.OPS.OP_HASH160 && buffer[1] === 0x14 && - buffer[22] === script_1.OPS.OP_EQUAL; + buffer[22] === script_1.OPS.OP_EQUAL); } exports.check = check; -check.toJSON = function () { return 'scriptHash output'; }; +check.toJSON = function () { + return 'scriptHash output'; +}; diff --git a/src/templates/witnesscommitment/output.js b/src/templates/witnesscommitment/output.js index 95a8bc1..38f622a 100644 --- a/src/templates/witnesscommitment/output.js +++ b/src/templates/witnesscommitment/output.js @@ -8,13 +8,15 @@ const script_1 = require("../../script"); const HEADER = Buffer.from('aa21a9ed', 'hex'); function check(script) { const buffer = bscript.compile(script); - return buffer.length > 37 && + return (buffer.length > 37 && buffer[0] === script_1.OPS.OP_RETURN && buffer[1] === 0x24 && - buffer.slice(2, 6).equals(HEADER); + buffer.slice(2, 6).equals(HEADER)); } exports.check = check; -check.toJSON = function () { return 'Witness commitment output'; }; +check.toJSON = function () { + return 'Witness commitment output'; +}; function encode(commitment) { typeforce(types.Hash256bit, commitment); const buffer = Buffer.allocUnsafe(36); diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.js index 131a49e..4585623 100644 --- a/src/templates/witnesspubkeyhash/input.js +++ b/src/templates/witnesspubkeyhash/input.js @@ -7,9 +7,11 @@ function isCompressedCanonicalPubKey(pubKey) { } function check(script) { const chunks = bscript.decompile(script); - return chunks.length === 2 && + return (chunks.length === 2 && bscript.isCanonicalScriptSignature(chunks[0]) && - isCompressedCanonicalPubKey(chunks[1]); + isCompressedCanonicalPubKey(chunks[1])); } exports.check = check; -check.toJSON = function () { return 'witnessPubKeyHash input'; }; +check.toJSON = function () { + return 'witnessPubKeyHash input'; +}; diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.js index 2fb6e15..9c508a0 100644 --- a/src/templates/witnesspubkeyhash/output.js +++ b/src/templates/witnesspubkeyhash/output.js @@ -5,9 +5,9 @@ const bscript = require("../../script"); const script_1 = require("../../script"); function check(script) { const buffer = bscript.compile(script); - return buffer.length === 22 && - buffer[0] === script_1.OPS.OP_0 && - buffer[1] === 0x14; + return buffer.length === 22 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x14; } exports.check = check; -check.toJSON = function () { return 'Witness pubKeyHash output'; }; +check.toJSON = function () { + return 'Witness pubKeyHash output'; +}; diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js index 255ea86..ca8c8b6 100644 --- a/src/templates/witnessscripthash/input.js +++ b/src/templates/witnessscripthash/input.js @@ -31,4 +31,6 @@ function check(chunks, allowIncomplete) { return false; } exports.check = check; -check.toJSON = function () { return 'witnessScriptHash input'; }; +check.toJSON = function () { + return 'witnessScriptHash input'; +}; diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.js index 994a75a..f283b86 100644 --- a/src/templates/witnessscripthash/output.js +++ b/src/templates/witnessscripthash/output.js @@ -5,9 +5,9 @@ const bscript = require("../../script"); const script_1 = require("../../script"); function check(script) { const buffer = bscript.compile(script); - return buffer.length === 34 && - buffer[0] === script_1.OPS.OP_0 && - buffer[1] === 0x20; + return buffer.length === 34 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x20; } exports.check = check; -check.toJSON = function () { return 'Witness scriptHash output'; }; +check.toJSON = function () { + return 'Witness scriptHash output'; +}; diff --git a/types/templates/multisig/index.d.ts b/types/templates/multisig/index.d.ts index cff90ee..f6288e2 100644 --- a/types/templates/multisig/index.d.ts +++ b/types/templates/multisig/index.d.ts @@ -1,3 +1,3 @@ import * as input from './input'; import * as output from './output'; -export { input, output, }; +export { input, output }; diff --git a/types/templates/pubkey/index.d.ts b/types/templates/pubkey/index.d.ts index cff90ee..f6288e2 100644 --- a/types/templates/pubkey/index.d.ts +++ b/types/templates/pubkey/index.d.ts @@ -1,3 +1,3 @@ import * as input from './input'; import * as output from './output'; -export { input, output, }; +export { input, output }; diff --git a/types/templates/pubkeyhash/index.d.ts b/types/templates/pubkeyhash/index.d.ts index cff90ee..f6288e2 100644 --- a/types/templates/pubkeyhash/index.d.ts +++ b/types/templates/pubkeyhash/index.d.ts @@ -1,3 +1,3 @@ import * as input from './input'; import * as output from './output'; -export { input, output, }; +export { input, output }; diff --git a/types/templates/scripthash/index.d.ts b/types/templates/scripthash/index.d.ts index cff90ee..f6288e2 100644 --- a/types/templates/scripthash/index.d.ts +++ b/types/templates/scripthash/index.d.ts @@ -1,3 +1,3 @@ import * as input from './input'; import * as output from './output'; -export { input, output, }; +export { input, output }; diff --git a/types/templates/witnesscommitment/index.d.ts b/types/templates/witnesscommitment/index.d.ts index a072ea9..c37ee7c 100644 --- a/types/templates/witnesscommitment/index.d.ts +++ b/types/templates/witnesscommitment/index.d.ts @@ -1,2 +1,2 @@ import * as output from './output'; -export { output, }; +export { output }; diff --git a/types/templates/witnesspubkeyhash/index.d.ts b/types/templates/witnesspubkeyhash/index.d.ts index cff90ee..f6288e2 100644 --- a/types/templates/witnesspubkeyhash/index.d.ts +++ b/types/templates/witnesspubkeyhash/index.d.ts @@ -1,3 +1,3 @@ import * as input from './input'; import * as output from './output'; -export { input, output, }; +export { input, output }; diff --git a/types/templates/witnessscripthash/index.d.ts b/types/templates/witnessscripthash/index.d.ts index cff90ee..f6288e2 100644 --- a/types/templates/witnessscripthash/index.d.ts +++ b/types/templates/witnessscripthash/index.d.ts @@ -1,3 +1,3 @@ import * as input from './input'; import * as output from './output'; -export { input, output, }; +export { input, output }; From f084439054543ee058f1e3c839daa121a20b6bb9 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Tue, 5 Mar 2019 23:51:45 +0900 Subject: [PATCH 234/568] ci: add tslint --- package-lock.json | 249 +++++++++++++++++++++++++++++++++------------- package.json | 8 +- tslint.json | 31 ++++++ 3 files changed, 214 insertions(+), 74 deletions(-) create mode 100644 tslint.json diff --git a/package-lock.json b/package-lock.json index 4af2f69..cbbf172 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,59 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -130,6 +183,34 @@ "safe-buffer": "^5.1.2" } }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } + } + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -139,6 +220,21 @@ "safe-buffer": "^5.0.1" } }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", @@ -220,44 +316,16 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "eslint-plugin-typescript": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-typescript/-/eslint-plugin-typescript-0.14.0.tgz", - "integrity": "sha512-2u1WnnDF2mkWWgU1lFQ2RjypUlmRoBEvQN02y9u+IL12mjWlkKFGEBnVsjs9Y8190bfPQCvWly1c2rYYUSOxWw==", - "dev": true, - "requires": { - "requireindex": "~1.1.0" - } - }, - "eslint-scope": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.2.tgz", - "integrity": "sha512-5q1+B/ogmHl8+paxtOKx38Z8LtWkVGuNt3+GQNErqwLl6ViNp/gdJGMCjZNxZ8j/VYjDNZ2Fo+eQc1TAVPIzbg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, "file-uri-to-path": { @@ -301,6 +369,15 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -368,12 +445,22 @@ "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", "dev": true }, - "lodash.unescape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", - "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, + "js-yaml": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.2.tgz", + "integrity": "sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -514,7 +601,6 @@ "version": "0.1.4", "bundled": true, "dev": true, - "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -1697,8 +1783,7 @@ "longest": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "loose-envify": { "version": "1.3.1", @@ -3174,12 +3259,6 @@ "safe-buffer": "^5.1.0" } }, - "requireindex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz", - "integrity": "sha1-5UBLgVV+91225JxacgBIk/4D4WI=", - "dev": true - }, "resolve": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", @@ -3218,12 +3297,27 @@ "safe-buffer": "^5.0.1" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", @@ -3245,6 +3339,42 @@ "nan": "^2.10.0" } }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tslint": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.13.1.tgz", + "integrity": "sha512-fplQqb2miLbcPhyHoMV4FU9PtNRbgmm/zI5d3SZwwmJQM6V0eodju+hplpyfhLWpmwrDNfNYU57uYRb8s0zZoQ==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "typeforce": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", @@ -3256,27 +3386,6 @@ "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", "dev": true }, - "typescript-eslint-parser": { - "version": "21.0.2", - "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-21.0.2.tgz", - "integrity": "sha512-u+pj4RVJBr4eTzj0n5npoXD/oRthvfUCjSKndhNI714MG0mQq2DJw5WP7qmonRNIFgmZuvdDOH3BHm9iOjIAfg==", - "dev": true, - "requires": { - "eslint-scope": "^4.0.0", - "eslint-visitor-keys": "^1.0.0", - "typescript-estree": "5.3.0" - } - }, - "typescript-estree": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/typescript-estree/-/typescript-estree-5.3.0.tgz", - "integrity": "sha512-Vu0KmYdSCkpae+J48wsFC1ti19Hq3Wi/lODUaE+uesc3gzqhWbZ5itWbsjylLVbjNW4K41RqDzSfnaYNbmEiMQ==", - "dev": true, - "requires": { - "lodash.unescape": "4.0.1", - "semver": "5.5.0" - } - }, "unorm": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz", diff --git a/package.json b/package.json index b98f412..898fac8 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "format": "npm run prettier -- --write", "format:ci": "npm run prettier -- --check", "integration": "npm run build && npm run nobuild:integration", + "lint": "tslint -p tsconfig.json -c tslint.json", "nobuild:coverage-report": "nyc report --reporter=lcov", "nobuild:coverage-html": "nyc report --reporter=html", "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", @@ -29,7 +30,7 @@ "nobuild:unit": "mocha", "prepare": "npm run build", "prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore", - "test": "npm run build && npm run format:ci && npm run nobuild:coverage", + "test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" }, "repository": { @@ -64,15 +65,14 @@ "bn.js": "^4.11.8", "bs58": "^4.0.0", "dhttp": "^3.0.0", - "eslint-plugin-typescript": "^0.14.0", "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^5.2.0", "nyc": "^11.8.0", "prettier": "^1.16.4", "proxyquire": "^2.0.1", - "typescript": "3.2.2", - "typescript-eslint-parser": "^21.0.2" + "tslint": "^5.13.1", + "typescript": "3.2.2" }, "license": "MIT" } diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..18bf61d --- /dev/null +++ b/tslint.json @@ -0,0 +1,31 @@ +{ + "defaultSeverity": "error", + "extends": ["tslint:recommended"], + "rules": { + "curly": false, + "indent": [ + true, + "spaces", + 2 + ], + "interface-name": [false], + "match-default-export-name": true, + "max-classes-per-file": [false], + "member-access": [true, "no-public"], + "no-empty": [true, "allow-empty-catch"], + "no-implicit-dependencies": true, + "no-return-await": true, + "no-var-requires": false, + "no-unused-expression": false, + "object-literal-sort-keys": false, + "quotemark": [true, "single"], + "variable-name": [ + true, + "ban-keywords", + "check-format", + "allow-leading-underscore", + "allow-pascal-case" + ] + }, + "rulesDirectory": [] +} From 99d0de8b3d9bff5c0ba57af9b98c299b2f602c4f Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 11:22:04 +0900 Subject: [PATCH 235/568] update package-lock --- package-lock.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index cbbf172..9de2ff5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -601,6 +601,7 @@ "version": "0.1.4", "bundled": true, "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -1783,7 +1784,8 @@ "longest": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "loose-envify": { "version": "1.3.1", From d9cba6f176931833a8a3fd8cc26af3a160c820e7 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 11:32:06 +0900 Subject: [PATCH 236/568] Fixed address.ts lint --- src/address.js | 12 ++++++------ ts_src/address.ts | 36 ++++++++++++++++++------------------ types/address.d.ts | 8 ++++---- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/address.js b/src/address.js index f12cd8d..b0be0e1 100644 --- a/src/address.js +++ b/src/address.js @@ -1,9 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const types = require("./types"); -const bscript = require("./script"); const networks = require("./networks"); const payments = require("./payments"); +const bscript = require("./script"); +const types = require("./types"); const bech32 = require('bech32'); const bs58check = require('bs58check'); const typeforce = require('typeforce'); @@ -16,7 +16,7 @@ function fromBase58Check(address) { throw new TypeError(address + ' is too long'); const version = payload.readUInt8(0); const hash = payload.slice(1); - return { version: version, hash: hash }; + return { version, hash }; } exports.fromBase58Check = fromBase58Check; function fromBech32(address) { @@ -44,7 +44,7 @@ function toBech32(data, version, prefix) { } exports.toBech32 = toBech32; function fromOutputScript(output, network) { - //TODO: Network + // TODO: Network network = network || networks.bitcoin; try { return payments.p2pkh({ output, network }).address; @@ -67,8 +67,8 @@ function fromOutputScript(output, network) { exports.fromOutputScript = fromOutputScript; function toOutputScript(address, network) { network = network || networks.bitcoin; - let decodeBase58 = undefined; - let decodeBech32 = undefined; + let decodeBase58; + let decodeBech32; try { decodeBase58 = fromBase58Check(address); } diff --git a/ts_src/address.ts b/ts_src/address.ts index d547e78..0c0cda5 100644 --- a/ts_src/address.ts +++ b/ts_src/address.ts @@ -1,23 +1,23 @@ import { Network } from './networks'; -import * as types from './types'; -import * as bscript from './script'; import * as networks from './networks'; import * as payments from './payments'; +import * as bscript from './script'; +import * as types from './types'; const bech32 = require('bech32'); const bs58check = require('bs58check'); const typeforce = require('typeforce'); -export type Base58CheckResult = { +export interface Base58CheckResult { hash: Buffer; version: number; -}; +} -export type Bech32Result = { +export interface Bech32Result { version: number; prefix: string; data: Buffer; -}; +} export function fromBase58Check(address: string): Base58CheckResult { const payload = bs58check.decode(address); @@ -29,7 +29,7 @@ export function fromBase58Check(address: string): Base58CheckResult { const version = payload.readUInt8(0); const hash = payload.slice(1); - return { version: version, hash: hash }; + return { version, hash }; } export function fromBech32(address: string): Bech32Result { @@ -65,20 +65,20 @@ export function toBech32( } export function fromOutputScript(output: Buffer, network: Network): string { - //TODO: Network + // TODO: Network network = network || networks.bitcoin; try { - return <string>payments.p2pkh({ output, network }).address; + return payments.p2pkh({ output, network }).address as string; } catch (e) {} try { - return <string>payments.p2sh({ output, network }).address; + return payments.p2sh({ output, network }).address as string; } catch (e) {} try { - return <string>payments.p2wpkh({ output, network }).address; + return payments.p2wpkh({ output, network }).address as string; } catch (e) {} try { - return <string>payments.p2wsh({ output, network }).address; + return payments.p2wsh({ output, network }).address as string; } catch (e) {} throw new Error(bscript.toASM(output) + ' has no matching Address'); @@ -87,17 +87,17 @@ export function fromOutputScript(output: Buffer, network: Network): string { export function toOutputScript(address: string, network: Network): Buffer { network = network || networks.bitcoin; - let decodeBase58: Base58CheckResult | undefined = undefined; - let decodeBech32: Bech32Result | undefined = undefined; + let decodeBase58: Base58CheckResult | undefined; + let decodeBech32: Bech32Result | undefined; try { decodeBase58 = fromBase58Check(address); } catch (e) {} if (decodeBase58) { if (decodeBase58.version === network.pubKeyHash) - return <Buffer>payments.p2pkh({ hash: decodeBase58.hash }).output; + return payments.p2pkh({ hash: decodeBase58.hash }).output as Buffer; if (decodeBase58.version === network.scriptHash) - return <Buffer>payments.p2sh({ hash: decodeBase58.hash }).output; + return payments.p2sh({ hash: decodeBase58.hash }).output as Buffer; } else { try { decodeBech32 = fromBech32(address); @@ -108,9 +108,9 @@ export function toOutputScript(address: string, network: Network): Buffer { throw new Error(address + ' has an invalid prefix'); if (decodeBech32.version === 0) { if (decodeBech32.data.length === 20) - return <Buffer>payments.p2wpkh({ hash: decodeBech32.data }).output; + return payments.p2wpkh({ hash: decodeBech32.data }).output as Buffer; if (decodeBech32.data.length === 32) - return <Buffer>payments.p2wsh({ hash: decodeBech32.data }).output; + return payments.p2wsh({ hash: decodeBech32.data }).output as Buffer; } } } diff --git a/types/address.d.ts b/types/address.d.ts index 68fbedd..1da68ac 100644 --- a/types/address.d.ts +++ b/types/address.d.ts @@ -1,14 +1,14 @@ /// <reference types="node" /> import { Network } from './networks'; -export declare type Base58CheckResult = { +export interface Base58CheckResult { hash: Buffer; version: number; -}; -export declare type Bech32Result = { +} +export interface Bech32Result { version: number; prefix: string; data: Buffer; -}; +} export declare function fromBase58Check(address: string): Base58CheckResult; export declare function fromBech32(address: string): Bech32Result; export declare function toBase58Check(hash: Buffer, version: number): string; From 51d078cbcedd378e02870c65b51b9bcb4aee5924 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 11:47:00 +0900 Subject: [PATCH 237/568] Fix block.ts lint --- src/block.js | 44 +++++++++++++-------------- ts_src/block.ts | 78 ++++++++++++++++++++++++------------------------ tslint.json | 3 ++ types/block.d.ts | 14 ++++----- 4 files changed, 71 insertions(+), 68 deletions(-) diff --git a/src/block.js b/src/block.js index eb3fb8b..9419989 100644 --- a/src/block.js +++ b/src/block.js @@ -1,9 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +const bufferutils_1 = require("./bufferutils"); +const bcrypto = require("./crypto"); const transaction_1 = require("./transaction"); const types = require("./types"); -const bcrypto = require("./crypto"); -const bufferutils_1 = require("./bufferutils"); const fastMerkleRoot = require('merkle-lib/fastRoot'); const typeforce = require('typeforce'); const varuint = require('varuint-bitcoin'); @@ -28,16 +28,6 @@ function anyTxHasWitness(transactions) { input.witness.length > 0))); } class Block { - constructor() { - this.version = 1; - this.timestamp = 0; - this.bits = 0; - this.nonce = 0; - this.prevHash = undefined; - this.merkleRoot = undefined; - this.witnessCommit = undefined; - this.transactions = undefined; - } static fromBuffer(buffer) { if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); @@ -77,11 +67,11 @@ class Block { }; const nTransactions = readVarInt(); block.transactions = []; - for (var i = 0; i < nTransactions; ++i) { + for (let i = 0; i < nTransactions; ++i) { const tx = readTransaction(); block.transactions.push(tx); } - let witnessCommit = block.getWitnessCommit(); + const witnessCommit = block.getWitnessCommit(); // This Block contains a witness commit if (witnessCommit) block.witnessCommit = witnessCommit; @@ -109,6 +99,16 @@ class Block { ? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) : rootHash; } + constructor() { + this.version = 1; + this.timestamp = 0; + this.bits = 0; + this.nonce = 0; + this.prevHash = undefined; + this.merkleRoot = undefined; + this.witnessCommit = undefined; + this.transactions = undefined; + } getWitnessCommit() { if (!txesHaveWitnessCommit(this.transactions)) return null; @@ -116,11 +116,11 @@ class Block { // There is no rule for the index of the output, so use filter to find it. // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed // If multiple commits are found, the output with highest index is assumed. - let witnessCommits = this.transactions[0].outs.filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))).map(out => out.script.slice(6, 38)); + const witnessCommits = this.transactions[0].outs.filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))).map(out => out.script.slice(6, 38)); if (witnessCommits.length === 0) return null; // Use the commit with the highest output (should only be one though) - let result = witnessCommits[witnessCommits.length - 1]; + const result = witnessCommits[witnessCommits.length - 1]; if (!(result instanceof Buffer && result.length === 32)) return null; return result; @@ -193,7 +193,7 @@ class Block { checkTxRoots() { // If the Block has segwit transactions but no witness commit, // there's no way it can be valid, so fail the check. - let hasWitnessCommit = this.hasWitnessCommit(); + const hasWitnessCommit = this.hasWitnessCommit(); if (!hasWitnessCommit && this.hasWitness()) return false; return (this.__checkMerkleRoot() && @@ -204,6 +204,11 @@ class Block { 'deprecated in v5. Please use checkTxRoots instead.'); return this.checkTxRoots(); } + checkProofOfWork() { + const hash = bufferutils_1.reverseBuffer(this.getHash()); + const target = Block.calculateTarget(this.bits); + return hash.compare(target) <= 0; + } __checkMerkleRoot() { if (!this.transactions) throw errorMerkleNoTxes; @@ -218,10 +223,5 @@ class Block { const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true); return this.witnessCommit.compare(actualWitnessCommit) === 0; } - checkProofOfWork() { - const hash = bufferutils_1.reverseBuffer(this.getHash()); - const target = Block.calculateTarget(this.bits); - return hash.compare(target) <= 0; - } } exports.Block = Block; diff --git a/ts_src/block.ts b/ts_src/block.ts index feb141e..888337d 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -1,7 +1,7 @@ +import { reverseBuffer } from './bufferutils'; +import * as bcrypto from './crypto'; import { Transaction } from './transaction'; import * as types from './types'; -import * as bcrypto from './crypto'; -import { reverseBuffer } from './bufferutils'; const fastMerkleRoot = require('merkle-lib/fastRoot'); const typeforce = require('typeforce'); @@ -14,7 +14,7 @@ const errorWitnessNotSegwit = new TypeError( 'Cannot compute witness commit for non-segwit block', ); -function txesHaveWitnessCommit(transactions: Array<Transaction>): boolean { +function txesHaveWitnessCommit(transactions: Transaction[]): boolean { return ( transactions instanceof Array && transactions[0] && @@ -27,7 +27,7 @@ function txesHaveWitnessCommit(transactions: Array<Transaction>): boolean { ); } -function anyTxHasWitness(transactions: Array<Transaction>): boolean { +function anyTxHasWitness(transactions: Transaction[]): boolean { return ( transactions instanceof Array && transactions.some( @@ -45,26 +45,6 @@ function anyTxHasWitness(transactions: Array<Transaction>): boolean { } export class Block { - version: number; - prevHash?: Buffer; - merkleRoot?: Buffer; - timestamp: number; - witnessCommit?: Buffer; - bits: number; - nonce: number; - transactions?: Array<Transaction>; - - constructor() { - this.version = 1; - this.timestamp = 0; - this.bits = 0; - this.nonce = 0; - this.prevHash = undefined; - this.merkleRoot = undefined; - this.witnessCommit = undefined; - this.transactions = undefined; - } - static fromBuffer(buffer: Buffer): Block { if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); @@ -111,12 +91,12 @@ export class Block { const nTransactions = readVarInt(); block.transactions = []; - for (var i = 0; i < nTransactions; ++i) { + for (let i = 0; i < nTransactions; ++i) { const tx = readTransaction(); block.transactions.push(tx); } - let witnessCommit = block.getWitnessCommit(); + const witnessCommit = block.getWitnessCommit(); // This Block contains a witness commit if (witnessCommit) block.witnessCommit = witnessCommit; @@ -136,7 +116,7 @@ export class Block { } static calculateMerkleRoot( - transactions: Array<Transaction>, + transactions: Transaction[], forWitness?: boolean, ): Buffer { typeforce([{ getHash: types.Function }], transactions); @@ -157,6 +137,26 @@ export class Block { : rootHash; } + version: number; + prevHash?: Buffer; + merkleRoot?: Buffer; + timestamp: number; + witnessCommit?: Buffer; + bits: number; + nonce: number; + transactions?: Transaction[]; + + constructor() { + this.version = 1; + this.timestamp = 0; + this.bits = 0; + this.nonce = 0; + this.prevHash = undefined; + this.merkleRoot = undefined; + this.witnessCommit = undefined; + this.transactions = undefined; + } + getWitnessCommit(): Buffer | null { if (!txesHaveWitnessCommit(this.transactions!)) return null; @@ -164,12 +164,12 @@ export class Block { // There is no rule for the index of the output, so use filter to find it. // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed // If multiple commits are found, the output with highest index is assumed. - let witnessCommits = this.transactions![0].outs.filter(out => + const witnessCommits = this.transactions![0].outs.filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')), ).map(out => out.script.slice(6, 38)); if (witnessCommits.length === 0) return null; // Use the commit with the highest output (should only be one though) - let result = witnessCommits[witnessCommits.length - 1]; + const result = witnessCommits[witnessCommits.length - 1]; if (!(result instanceof Buffer && result.length === 32)) return null; return result; @@ -261,7 +261,7 @@ export class Block { checkTxRoots(): boolean { // If the Block has segwit transactions but no witness commit, // there's no way it can be valid, so fail the check. - let hasWitnessCommit = this.hasWitnessCommit(); + const hasWitnessCommit = this.hasWitnessCommit(); if (!hasWitnessCommit && this.hasWitness()) return false; return ( this.__checkMerkleRoot() && @@ -277,14 +277,21 @@ export class Block { return this.checkTxRoots(); } - __checkMerkleRoot(): boolean { + checkProofOfWork(): boolean { + const hash: Buffer = reverseBuffer(this.getHash()); + const target = Block.calculateTarget(this.bits); + + return hash.compare(target) <= 0; + } + + private __checkMerkleRoot(): boolean { if (!this.transactions) throw errorMerkleNoTxes; const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions); return this.merkleRoot!.compare(actualMerkleRoot) === 0; } - __checkWitnessCommit(): boolean { + private __checkWitnessCommit(): boolean { if (!this.transactions) throw errorMerkleNoTxes; if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit; @@ -294,11 +301,4 @@ export class Block { ); return this.witnessCommit!.compare(actualWitnessCommit) === 0; } - - checkProofOfWork(): boolean { - const hash: Buffer = reverseBuffer(this.getHash()); - const target = Block.calculateTarget(this.bits); - - return hash.compare(target) <= 0; - } } diff --git a/tslint.json b/tslint.json index 18bf61d..9708e8b 100644 --- a/tslint.json +++ b/tslint.json @@ -2,6 +2,7 @@ "defaultSeverity": "error", "extends": ["tslint:recommended"], "rules": { + "arrow-parens": [true, "ban-single-arg-parens"], "curly": false, "indent": [ true, @@ -12,6 +13,8 @@ "match-default-export-name": true, "max-classes-per-file": [false], "member-access": [true, "no-public"], + "no-bitwise": false, + "no-console": false, "no-empty": [true, "allow-empty-catch"], "no-implicit-dependencies": true, "no-return-await": true, diff --git a/types/block.d.ts b/types/block.d.ts index 8b4f2f1..6f2d5b9 100644 --- a/types/block.d.ts +++ b/types/block.d.ts @@ -1,6 +1,10 @@ /// <reference types="node" /> import { Transaction } from './transaction'; export declare class Block { + static fromBuffer(buffer: Buffer): Block; + static fromHex(hex: string): Block; + static calculateTarget(bits: number): Buffer; + static calculateMerkleRoot(transactions: Transaction[], forWitness?: boolean): Buffer; version: number; prevHash?: Buffer; merkleRoot?: Buffer; @@ -8,12 +12,8 @@ export declare class Block { witnessCommit?: Buffer; bits: number; nonce: number; - transactions?: Array<Transaction>; + transactions?: Transaction[]; constructor(); - static fromBuffer(buffer: Buffer): Block; - static fromHex(hex: string): Block; - static calculateTarget(bits: number): Buffer; - static calculateMerkleRoot(transactions: Array<Transaction>, forWitness?: boolean): Buffer; getWitnessCommit(): Buffer | null; hasWitnessCommit(): boolean; hasWitness(): boolean; @@ -25,7 +25,7 @@ export declare class Block { toHex(headersOnly: boolean): string; checkTxRoots(): boolean; checkMerkleRoot(): boolean; - __checkMerkleRoot(): boolean; - __checkWitnessCommit(): boolean; checkProofOfWork(): boolean; + private __checkMerkleRoot; + private __checkWitnessCommit; } From cb5ab7684e04d7add2ecc78d3749089a7f1b53b4 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 11:54:37 +0900 Subject: [PATCH 238/568] Fix classify.ts lint --- src/classify.js | 2 +- ts_src/classify.ts | 27 ++++++++++++--------------- types/classify.d.ts | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/classify.js b/src/classify.js index 0aec5c4..7d8e57d 100644 --- a/src/classify.js +++ b/src/classify.js @@ -6,9 +6,9 @@ const nullData = require("./templates/nulldata"); const pubKey = require("./templates/pubkey"); const pubKeyHash = require("./templates/pubkeyhash"); const scriptHash = require("./templates/scripthash"); +const witnessCommitment = require("./templates/witnesscommitment"); const witnessPubKeyHash = require("./templates/witnesspubkeyhash"); const witnessScriptHash = require("./templates/witnessscripthash"); -const witnessCommitment = require("./templates/witnesscommitment"); const types = { P2MS: 'multisig', NONSTANDARD: 'nonstandard', diff --git a/ts_src/classify.ts b/ts_src/classify.ts index 0de93f9..a7ec68c 100644 --- a/ts_src/classify.ts +++ b/ts_src/classify.ts @@ -4,20 +4,20 @@ import * as nullData from './templates/nulldata'; import * as pubKey from './templates/pubkey'; import * as pubKeyHash from './templates/pubkeyhash'; import * as scriptHash from './templates/scripthash'; +import * as witnessCommitment from './templates/witnesscommitment'; import * as witnessPubKeyHash from './templates/witnesspubkeyhash'; import * as witnessScriptHash from './templates/witnessscripthash'; -import * as witnessCommitment from './templates/witnesscommitment'; const types = { - P2MS: <string>'multisig', - NONSTANDARD: <string>'nonstandard', - NULLDATA: <string>'nulldata', - P2PK: <string>'pubkey', - P2PKH: <string>'pubkeyhash', - P2SH: <string>'scripthash', - P2WPKH: <string>'witnesspubkeyhash', - P2WSH: <string>'witnessscripthash', - WITNESS_COMMITMENT: <string>'witnesscommitment', + P2MS: 'multisig' as string, + NONSTANDARD: 'nonstandard' as string, + NULLDATA: 'nulldata' as string, + P2PK: 'pubkey' as string, + P2PKH: 'pubkeyhash' as string, + P2SH: 'scripthash' as string, + P2WPKH: 'witnesspubkeyhash' as string, + P2WSH: 'witnessscripthash' as string, + WITNESS_COMMITMENT: 'witnesscommitment' as string, }; function classifyOutput(script: Buffer): string { @@ -51,16 +51,13 @@ function classifyInput(script: Buffer, allowIncomplete: boolean): string { return types.NONSTANDARD; } -function classifyWitness( - script: Array<Buffer>, - allowIncomplete: boolean, -): string { +function classifyWitness(script: Buffer[], allowIncomplete: boolean): string { // XXX: optimization, below functions .decompile before use const chunks = decompile(script); if (!chunks) throw new TypeError('Invalid script'); if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH; - if (witnessScriptHash.input.check(<Array<Buffer>>chunks, allowIncomplete)) + if (witnessScriptHash.input.check(chunks as Buffer[], allowIncomplete)) return types.P2WSH; return types.NONSTANDARD; diff --git a/types/classify.d.ts b/types/classify.d.ts index b394c71..67f913b 100644 --- a/types/classify.d.ts +++ b/types/classify.d.ts @@ -12,5 +12,5 @@ declare const types: { }; declare function classifyOutput(script: Buffer): string; declare function classifyInput(script: Buffer, allowIncomplete: boolean): string; -declare function classifyWitness(script: Array<Buffer>, allowIncomplete: boolean): string; +declare function classifyWitness(script: Buffer[], allowIncomplete: boolean): string; export { classifyInput as input, classifyOutput as output, classifyWitness as witness, types, }; From 6a734aef4c0dfc2503ee67ef878f3296291e0397 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 12:06:12 +0900 Subject: [PATCH 239/568] Fix lint for ecpair.ts --- src/ecpair.js | 22 +++++++++++----------- test/ecpair.js | 2 +- ts_src/ecpair.ts | 36 ++++++++++++++++++------------------ types/ecpair.d.ts | 4 ++-- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index 7d376ac..1335014 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -17,30 +17,30 @@ class ECPair { this.compressed = options.compressed === undefined ? true : options.compressed; this.network = options.network || NETWORKS.bitcoin; - this.__d = undefined; + this.__D = undefined; this.__Q = undefined; if (d !== undefined) - this.__d = d; + this.__D = d; if (Q !== undefined) this.__Q = ecc.pointCompress(Q, this.compressed); } get privateKey() { - return this.__d; + return this.__D; } get publicKey() { if (!this.__Q) - this.__Q = ecc.pointFromScalar(this.__d, this.compressed); + this.__Q = ecc.pointFromScalar(this.__D, this.compressed); return this.__Q; } toWIF() { - if (!this.__d) + if (!this.__D) throw new Error('Missing private key'); - return wif.encode(this.network.wif, this.__d, this.compressed); + return wif.encode(this.network.wif, this.__D, this.compressed); } sign(hash) { - if (!this.__d) + if (!this.__D) throw new Error('Missing private key'); - return ecc.sign(hash, this.__d); + return ecc.sign(hash, this.__D); } verify(hash, signature) { return ecc.verify(hash, this.publicKey, signature); @@ -60,13 +60,13 @@ function fromPublicKey(buffer, options) { return new ECPair(undefined, buffer, options); } exports.fromPublicKey = fromPublicKey; -function fromWIF(string, network) { - const decoded = wif.decode(string); +function fromWIF(wifString, network) { + const decoded = wif.decode(wifString); const version = decoded.version; // list of networks? if (types.Array(network)) { network = network - .filter(function (x) { + .filter((x) => { return version === x.wif; }) .pop(); diff --git a/test/ecpair.js b/test/ecpair.js index 0ff0e8b..de5c485 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -238,7 +238,7 @@ describe('ECPair', function () { })) it('throws if no private key is found', function () { - delete keyPair.__d + delete keyPair.__D assert.throws(function () { keyPair.sign(hash) diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index 15ec00a..ec8dc13 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -33,7 +33,7 @@ export interface ECPairInterface { class ECPair implements ECPairInterface { compressed: boolean; network: Network; - private __d?: Buffer; + private __D?: Buffer; private __Q?: Buffer; constructor(d?: Buffer, Q?: Buffer, options?: ECPairOptions) { @@ -42,29 +42,29 @@ class ECPair implements ECPairInterface { options.compressed === undefined ? true : options.compressed; this.network = options.network || NETWORKS.bitcoin; - this.__d = undefined; + this.__D = undefined; this.__Q = undefined; - if (d !== undefined) this.__d = d; + if (d !== undefined) this.__D = d; if (Q !== undefined) this.__Q = ecc.pointCompress(Q, this.compressed); } get privateKey(): Buffer | undefined { - return this.__d; + return this.__D; } get publicKey(): Buffer | undefined { - if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed); + if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__D, this.compressed); return this.__Q; } toWIF(): string { - if (!this.__d) throw new Error('Missing private key'); - return wif.encode(this.network.wif, this.__d, this.compressed); + if (!this.__D) throw new Error('Missing private key'); + return wif.encode(this.network.wif, this.__D, this.compressed); } sign(hash: Buffer): Buffer { - if (!this.__d) throw new Error('Missing private key'); - return ecc.sign(hash, this.__d); + if (!this.__D) throw new Error('Missing private key'); + return ecc.sign(hash, this.__D); } verify(hash: Buffer, signature: Buffer): Buffer { @@ -78,26 +78,26 @@ function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair { throw new TypeError('Private key not in range [1, n)'); typeforce(isOptions, options); - return new ECPair(buffer, undefined, <ECPairOptions>options); + return new ECPair(buffer, undefined, options as ECPairOptions); } function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair { typeforce(ecc.isPoint, buffer); typeforce(isOptions, options); - return new ECPair(undefined, buffer, <ECPairOptions>options); + return new ECPair(undefined, buffer, options as ECPairOptions); } -function fromWIF(string: string, network?: Network | Array<Network>): ECPair { - const decoded = wif.decode(string); +function fromWIF(wifString: string, network?: Network | Network[]): ECPair { + const decoded = wif.decode(wifString); const version = decoded.version; // list of networks? if (types.Array(network)) { - network = <Network>(<Array<Network>>network) - .filter(function(x: Network) { + network = (network as Network[]) + .filter((x: Network) => { return version === x.wif; }) - .pop(); + .pop() as Network; if (!network) throw new Error('Unknown network version'); @@ -105,13 +105,13 @@ function fromWIF(string: string, network?: Network | Array<Network>): ECPair { } else { network = network || NETWORKS.bitcoin; - if (version !== (<Network>network).wif) + if (version !== (network as Network).wif) throw new Error('Invalid network version'); } return fromPrivateKey(decoded.privateKey, { compressed: decoded.compressed, - network: <Network>network, + network: network as Network, }); } diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index 58ea4be..674615b 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -18,7 +18,7 @@ export interface ECPairInterface { declare class ECPair implements ECPairInterface { compressed: boolean; network: Network; - private __d?; + private __D?; private __Q?; constructor(d?: Buffer, Q?: Buffer, options?: ECPairOptions); readonly privateKey: Buffer | undefined; @@ -29,6 +29,6 @@ declare class ECPair implements ECPairInterface { } declare function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair; declare function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair; -declare function fromWIF(string: string, network?: Network | Array<Network>): ECPair; +declare function fromWIF(wifString: string, network?: Network | Network[]): ECPair; declare function makeRandom(options?: ECPairOptions): ECPair; export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF }; From 3f34fe457abe6d19688416e566b6b340cfa959c8 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 12:11:15 +0900 Subject: [PATCH 240/568] Fix index.ts networks.ts lint --- src/index.js | 8 ++++---- ts_src/index.ts | 12 ++++++------ ts_src/networks.ts | 10 +++++----- types/index.d.ts | 12 ++++++------ types/networks.d.ts | 10 +++++----- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/index.js b/src/index.js index 7455d4a..73ac6f4 100644 --- a/src/index.js +++ b/src/index.js @@ -2,12 +2,12 @@ Object.defineProperty(exports, "__esModule", { value: true }); const bip32 = require("bip32"); exports.bip32 = bip32; -const ECPair = require("./ecpair"); -exports.ECPair = ECPair; const address = require("./address"); exports.address = address; const crypto = require("./crypto"); exports.crypto = crypto; +const ECPair = require("./ecpair"); +exports.ECPair = ECPair; const networks = require("./networks"); exports.networks = networks; const payments = require("./payments"); @@ -16,9 +16,9 @@ const script = require("./script"); exports.script = script; var block_1 = require("./block"); exports.Block = block_1.Block; +var script_1 = require("./script"); +exports.opcodes = script_1.OPS; var transaction_1 = require("./transaction"); exports.Transaction = transaction_1.Transaction; var transaction_builder_1 = require("./transaction_builder"); exports.TransactionBuilder = transaction_builder_1.TransactionBuilder; -var script_1 = require("./script"); -exports.opcodes = script_1.OPS; diff --git a/ts_src/index.ts b/ts_src/index.ts index fce7304..4e76c96 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -1,7 +1,7 @@ import * as bip32 from 'bip32'; -import * as ECPair from './ecpair'; import * as address from './address'; import * as crypto from './crypto'; +import * as ECPair from './ecpair'; import * as networks from './networks'; import * as payments from './payments'; import * as script from './script'; @@ -9,12 +9,12 @@ import * as script from './script'; export { ECPair, address, bip32, crypto, networks, payments, script }; export { Block } from './block'; +export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; -export { OPS as opcodes } from './script'; -export { Payment, PaymentOpts } from './payments'; -export { Input as TxInput, Output as TxOutput } from './transaction'; -export { Network } from './networks'; -export { OpCode } from './script'; export { BIP32Interface } from 'bip32'; +export { Network } from './networks'; +export { Payment, PaymentOpts } from './payments'; +export { OpCode } from './script'; +export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/ts_src/networks.ts b/ts_src/networks.ts index 0212a44..e66b08c 100644 --- a/ts_src/networks.ts +++ b/ts_src/networks.ts @@ -1,18 +1,18 @@ // https://en.bitcoin.it/wiki/List_of_address_prefixes // Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731 -export type Network = { +export interface Network { messagePrefix: string; bech32: string; - bip32: bip32; + bip32: Bip32; pubKeyHash: number; scriptHash: number; wif: number; -}; +} -type bip32 = { +interface Bip32 { public: number; private: number; -}; +} export const bitcoin: Network = { messagePrefix: '\x18Bitcoin Signed Message:\n', diff --git a/types/index.d.ts b/types/index.d.ts index 3512872..d21454a 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,17 +1,17 @@ import * as bip32 from 'bip32'; -import * as ECPair from './ecpair'; import * as address from './address'; import * as crypto from './crypto'; +import * as ECPair from './ecpair'; import * as networks from './networks'; import * as payments from './payments'; import * as script from './script'; export { ECPair, address, bip32, crypto, networks, payments, script }; export { Block } from './block'; +export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; -export { OPS as opcodes } from './script'; -export { Payment, PaymentOpts } from './payments'; -export { Input as TxInput, Output as TxOutput } from './transaction'; -export { Network } from './networks'; -export { OpCode } from './script'; export { BIP32Interface } from 'bip32'; +export { Network } from './networks'; +export { Payment, PaymentOpts } from './payments'; +export { OpCode } from './script'; +export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/types/networks.d.ts b/types/networks.d.ts index e402873..d5590fd 100644 --- a/types/networks.d.ts +++ b/types/networks.d.ts @@ -1,15 +1,15 @@ -export declare type Network = { +export interface Network { messagePrefix: string; bech32: string; - bip32: bip32; + bip32: Bip32; pubKeyHash: number; scriptHash: number; wif: number; -}; -declare type bip32 = { +} +interface Bip32 { public: number; private: number; -}; +} export declare const bitcoin: Network; export declare const regtest: Network; export declare const testnet: Network; From 389ec8cb33710d534d813a90cf7129b2f4e0dad4 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 12:29:24 +0900 Subject: [PATCH 241/568] Fix embed.ts and index.ts for payments lint --- src/payments/embed.js | 8 ++++---- ts_src/payments/embed.ts | 23 +++++++++++------------ ts_src/payments/index.ts | 11 +++++++---- types/payments/index.d.ts | 10 ++++++---- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/payments/embed.js b/src/payments/embed.js index 0e2a0ae..800c8de 100644 --- a/src/payments/embed.js +++ b/src/payments/embed.js @@ -1,14 +1,14 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +const networks_1 = require("../networks"); const bscript = require("../script"); const lazy = require("./lazy"); -const networks_1 = require("../networks"); const typef = require('typeforce'); const OPS = bscript.OPS; function stacksEqual(a, b) { if (a.length !== b.length) return false; - return a.every(function (x, i) { + return a.every((x, i) => { return x.equals(b[i]); }); } @@ -24,12 +24,12 @@ function p2data(a, opts) { }, a); const network = a.network || networks_1.bitcoin; const o = { network }; - lazy.prop(o, 'output', function () { + lazy.prop(o, 'output', () => { if (!a.data) return; return bscript.compile([OPS.OP_RETURN].concat(a.data)); }); - lazy.prop(o, 'data', function () { + lazy.prop(o, 'data', () => { if (!a.output) return; return bscript.decompile(a.output).slice(1); diff --git a/ts_src/payments/embed.ts b/ts_src/payments/embed.ts index d7a2b0f..38a8162 100644 --- a/ts_src/payments/embed.ts +++ b/ts_src/payments/embed.ts @@ -1,14 +1,15 @@ -import { Payment, PaymentOpts } from './index'; -import * as bscript from '../script'; -import * as lazy from './lazy'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; +import * as bscript from '../script'; +import { Payment, PaymentOpts, Stack } from './index'; +import * as lazy from './lazy'; + const typef = require('typeforce'); const OPS = bscript.OPS; -function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean { +function stacksEqual(a: Buffer[], b: Buffer[]): boolean { if (a.length !== b.length) return false; - return a.every(function(x, i) { + return a.every((x, i) => { return x.equals(b[i]); }); } @@ -28,15 +29,13 @@ export function p2data(a: Payment, opts?: PaymentOpts): Payment { ); const network = a.network || BITCOIN_NETWORK; - const o = <Payment>{ network }; + const o = { network } as Payment; - lazy.prop(o, 'output', function() { + lazy.prop(o, 'output', () => { if (!a.data) return; - return bscript.compile( - (<Array<Buffer | number>>[OPS.OP_RETURN]).concat(a.data), - ); + return bscript.compile(([OPS.OP_RETURN] as Stack).concat(a.data)); }); - lazy.prop(o, 'data', function() { + lazy.prop(o, 'data', () => { if (!a.output) return; return bscript.decompile(a.output)!.slice(1); }); @@ -50,7 +49,7 @@ export function p2data(a: Payment, opts?: PaymentOpts): Payment { if (!chunks!.slice(1).every(typef.Buffer)) throw new TypeError('Output is invalid'); - if (a.data && !stacksEqual(a.data, <Array<Buffer>>o.data)) + if (a.data && !stacksEqual(a.data, o.data as Buffer[])) throw new TypeError('Data mismatch'); } } diff --git a/ts_src/payments/index.ts b/ts_src/payments/index.ts index 6727eeb..4f6eb75 100644 --- a/ts_src/payments/index.ts +++ b/ts_src/payments/index.ts @@ -10,18 +10,18 @@ import { p2wsh } from './p2wsh'; export interface Payment { network?: Network; output?: Buffer; - data?: Array<Buffer>; + data?: Buffer[]; m?: number; n?: number; - pubkeys?: Array<Buffer>; + pubkeys?: Buffer[]; input?: Buffer; - signatures?: Array<Buffer>; + signatures?: Buffer[]; pubkey?: Buffer; signature?: Buffer; address?: string; hash?: Buffer; redeem?: Payment; - witness?: Array<Buffer>; + witness?: Buffer[]; } export interface PaymentOpts { @@ -29,6 +29,9 @@ export interface PaymentOpts { allowIncomplete?: boolean; } +export type StackElement = Buffer | number; +export type Stack = StackElement[]; + export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }; // TODO diff --git a/types/payments/index.d.ts b/types/payments/index.d.ts index 5afc847..3009bef 100644 --- a/types/payments/index.d.ts +++ b/types/payments/index.d.ts @@ -10,21 +10,23 @@ import { p2wsh } from './p2wsh'; export interface Payment { network?: Network; output?: Buffer; - data?: Array<Buffer>; + data?: Buffer[]; m?: number; n?: number; - pubkeys?: Array<Buffer>; + pubkeys?: Buffer[]; input?: Buffer; - signatures?: Array<Buffer>; + signatures?: Buffer[]; pubkey?: Buffer; signature?: Buffer; address?: string; hash?: Buffer; redeem?: Payment; - witness?: Array<Buffer>; + witness?: Buffer[]; } export interface PaymentOpts { validate?: boolean; allowIncomplete?: boolean; } +export declare type StackElement = Buffer | number; +export declare type Stack = StackElement[]; export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }; From db937f8110ad9e9f42a5b53922cb2e2d994719a0 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 12:33:47 +0900 Subject: [PATCH 242/568] Fix lazy.ts in payments lint --- src/payments/lazy.js | 24 ++++++++++++------------ ts_src/payments/lazy.ts | 24 ++++++++++++------------ types/payments/lazy.d.ts | 2 +- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/payments/lazy.js b/src/payments/lazy.js index 9eda8e8..d8494fd 100644 --- a/src/payments/lazy.js +++ b/src/payments/lazy.js @@ -4,16 +4,16 @@ function prop(object, name, f) { Object.defineProperty(object, name, { configurable: true, enumerable: true, - get: function () { - let value = f.call(this); - this[name] = value; - return value; + get() { + const _value = f.call(this); + this[name] = _value; + return _value; }, - set: function (value) { + set(_value) { Object.defineProperty(this, name, { configurable: true, enumerable: true, - value: value, + value: _value, writable: true, }); }, @@ -21,12 +21,12 @@ function prop(object, name, f) { } exports.prop = prop; function value(f) { - let value; - return function () { - if (value !== undefined) - return value; - value = f(); - return value; + let _value; + return () => { + if (_value !== undefined) + return _value; + _value = f(); + return _value; }; } exports.value = value; diff --git a/ts_src/payments/lazy.ts b/ts_src/payments/lazy.ts index 474c8e9..fe0fb6d 100644 --- a/ts_src/payments/lazy.ts +++ b/ts_src/payments/lazy.ts @@ -1,17 +1,17 @@ -export function prop(object: Object, name: string, f: () => any): void { +export function prop(object: {}, name: string, f: () => any): void { Object.defineProperty(object, name, { configurable: true, enumerable: true, - get: function() { - let value = f.call(this); - this[name] = value; - return value; + get() { + const _value = f.call(this); + this[name] = _value; + return _value; }, - set: function(value) { + set(_value) { Object.defineProperty(this, name, { configurable: true, enumerable: true, - value: value, + value: _value, writable: true, }); }, @@ -19,10 +19,10 @@ export function prop(object: Object, name: string, f: () => any): void { } export function value<T>(f: () => T): () => T { - let value: T; - return function(): T { - if (value !== undefined) return value; - value = f(); - return value; + let _value: T; + return (): T => { + if (_value !== undefined) return _value; + _value = f(); + return _value; }; } diff --git a/types/payments/lazy.d.ts b/types/payments/lazy.d.ts index 705f530..3463906 100644 --- a/types/payments/lazy.d.ts +++ b/types/payments/lazy.d.ts @@ -1,2 +1,2 @@ -export declare function prop(object: Object, name: string, f: () => any): void; +export declare function prop(object: {}, name: string, f: () => any): void; export declare function value<T>(f: () => T): () => T; From 4054f3ae879a8840933e622410224d7e5b0a868c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 12:40:18 +0900 Subject: [PATCH 243/568] Fix lint for p2ms payment --- src/payments/p2ms.js | 18 ++++++++-------- ts_src/payments/p2ms.ts | 46 ++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index 1e51faa..5e48432 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -1,8 +1,8 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +const networks_1 = require("../networks"); const bscript = require("../script"); const lazy = require("./lazy"); -const networks_1 = require("../networks"); const OPS = bscript.OPS; const typef = require('typeforce'); const ecc = require('tiny-secp256k1'); @@ -10,7 +10,7 @@ const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 function stacksEqual(a, b) { if (a.length !== b.length) return false; - return a.every(function (x, i) { + return a.every((x, i) => { return x.equals(b[i]); }); } @@ -49,7 +49,7 @@ function p2ms(a, opts) { o.n = chunks[chunks.length - 2] - OP_INT_BASE; o.pubkeys = chunks.slice(1, -2); } - lazy.prop(o, 'output', function () { + lazy.prop(o, 'output', () => { if (!a.m) return; if (!o.n) @@ -58,34 +58,34 @@ function p2ms(a, opts) { return; return bscript.compile([].concat(OP_INT_BASE + a.m, a.pubkeys, OP_INT_BASE + o.n, OPS.OP_CHECKMULTISIG)); }); - lazy.prop(o, 'm', function () { + lazy.prop(o, 'm', () => { if (!o.output) return; decode(o.output); return o.m; }); - lazy.prop(o, 'n', function () { + lazy.prop(o, 'n', () => { if (!o.pubkeys) return; return o.pubkeys.length; }); - lazy.prop(o, 'pubkeys', function () { + lazy.prop(o, 'pubkeys', () => { if (!a.output) return; decode(a.output); return o.pubkeys; }); - lazy.prop(o, 'signatures', function () { + lazy.prop(o, 'signatures', () => { if (!a.input) return; return bscript.decompile(a.input).slice(1); }); - lazy.prop(o, 'input', function () { + lazy.prop(o, 'input', () => { if (!a.signatures) return; return bscript.compile([OPS.OP_0].concat(a.signatures)); }); - lazy.prop(o, 'witness', function () { + lazy.prop(o, 'witness', () => { if (!o.input) return; return []; diff --git a/ts_src/payments/p2ms.ts b/ts_src/payments/p2ms.ts index 766f341..b8691c0 100644 --- a/ts_src/payments/p2ms.ts +++ b/ts_src/payments/p2ms.ts @@ -1,17 +1,17 @@ -import { Payment, PaymentOpts } from './index'; -import * as bscript from '../script'; -import * as lazy from './lazy'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; +import * as bscript from '../script'; +import { Payment, PaymentOpts, Stack } from './index'; +import * as lazy from './lazy'; const OPS = bscript.OPS; const typef = require('typeforce'); const ecc = require('tiny-secp256k1'); const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 -function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean { +function stacksEqual(a: Buffer[], b: Buffer[]): boolean { if (a.length !== b.length) return false; - return a.every(function(x, i) { + return a.every((x, i) => { return x.equals(b[i]); }); } @@ -30,8 +30,8 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { function isAcceptableSignature(x: Buffer | number) { return ( - bscript.isCanonicalScriptSignature(<Buffer>x) || - (opts!.allowIncomplete && <number>x === OPS.OP_0) !== undefined + bscript.isCanonicalScriptSignature(x as Buffer) || + (opts!.allowIncomplete && (x as number) === OPS.OP_0) !== undefined ); } @@ -52,23 +52,23 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { const network = a.network || BITCOIN_NETWORK; const o: Payment = { network }; - let chunks: Array<Buffer | number> = []; + let chunks: Stack = []; let decoded = false; - function decode(output: Buffer | Array<Buffer | number>): void { + function decode(output: Buffer | Stack): void { if (decoded) return; decoded = true; - chunks = <Array<Buffer | number>>bscript.decompile(output); - o.m = <number>chunks[0] - OP_INT_BASE; - o.n = <number>chunks[chunks.length - 2] - OP_INT_BASE; - o.pubkeys = <Array<Buffer>>chunks.slice(1, -2); + chunks = bscript.decompile(output) as Stack; + o.m = (chunks[0] as number) - OP_INT_BASE; + o.n = (chunks[chunks.length - 2] as number) - OP_INT_BASE; + o.pubkeys = chunks.slice(1, -2) as Buffer[]; } - lazy.prop(o, 'output', function() { + lazy.prop(o, 'output', () => { if (!a.m) return; if (!o.n) return; if (!a.pubkeys) return; return bscript.compile( - (<Array<Buffer | number>>[]).concat( + ([] as Stack).concat( OP_INT_BASE + a.m, a.pubkeys, OP_INT_BASE + o.n, @@ -76,31 +76,29 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { ), ); }); - lazy.prop(o, 'm', function() { + lazy.prop(o, 'm', () => { if (!o.output) return; decode(o.output); return o.m; }); - lazy.prop(o, 'n', function() { + lazy.prop(o, 'n', () => { if (!o.pubkeys) return; return o.pubkeys.length; }); - lazy.prop(o, 'pubkeys', function() { + lazy.prop(o, 'pubkeys', () => { if (!a.output) return; decode(a.output); return o.pubkeys; }); - lazy.prop(o, 'signatures', function() { + lazy.prop(o, 'signatures', () => { if (!a.input) return; return bscript.decompile(a.input)!.slice(1); }); - lazy.prop(o, 'input', function() { + lazy.prop(o, 'input', () => { if (!a.signatures) return; - return bscript.compile( - (<Array<Buffer | number>>[OPS.OP_0]).concat(a.signatures), - ); + return bscript.compile(([OPS.OP_0] as Stack).concat(a.signatures)); }); - lazy.prop(o, 'witness', function() { + lazy.prop(o, 'witness', () => { if (!o.input) return; return []; }); From 8d5d78431c8e8b6a5ffbfb167d66cedb63febfd6 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 12:47:26 +0900 Subject: [PATCH 244/568] Fix P2PK payment lint --- src/payments/p2pk.js | 14 +++++++------- ts_src/payments/index.ts | 1 + ts_src/payments/p2pk.ts | 22 +++++++++++----------- types/payments/index.d.ts | 1 + 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index 9c9318f..81fe427 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -1,8 +1,8 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +const networks_1 = require("../networks"); const bscript = require("../script"); const lazy = require("./lazy"); -const networks_1 = require("../networks"); const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); @@ -19,32 +19,32 @@ function p2pk(a, opts) { signature: typef.maybe(bscript.isCanonicalScriptSignature), input: typef.maybe(typef.Buffer), }, a); - const _chunks = lazy.value(function () { + const _chunks = lazy.value(() => { return bscript.decompile(a.input); }); const network = a.network || networks_1.bitcoin; const o = { network }; - lazy.prop(o, 'output', function () { + lazy.prop(o, 'output', () => { if (!a.pubkey) return; return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); }); - lazy.prop(o, 'pubkey', function () { + lazy.prop(o, 'pubkey', () => { if (!a.output) return; return a.output.slice(1, -1); }); - lazy.prop(o, 'signature', function () { + lazy.prop(o, 'signature', () => { if (!a.input) return; return _chunks()[0]; }); - lazy.prop(o, 'input', function () { + lazy.prop(o, 'input', () => { if (!a.signature) return; return bscript.compile([a.signature]); }); - lazy.prop(o, 'witness', function () { + lazy.prop(o, 'witness', () => { if (!o.input) return; return []; diff --git a/ts_src/payments/index.ts b/ts_src/payments/index.ts index 4f6eb75..0c70fba 100644 --- a/ts_src/payments/index.ts +++ b/ts_src/payments/index.ts @@ -31,6 +31,7 @@ export interface PaymentOpts { export type StackElement = Buffer | number; export type Stack = StackElement[]; +export type StackFunction = () => Stack; export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }; diff --git a/ts_src/payments/p2pk.ts b/ts_src/payments/p2pk.ts index 69fb404..d14aacf 100644 --- a/ts_src/payments/p2pk.ts +++ b/ts_src/payments/p2pk.ts @@ -1,7 +1,7 @@ -import { Payment, PaymentOpts } from './index'; -import * as bscript from '../script'; -import * as lazy from './lazy'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; +import * as bscript from '../script'; +import { Payment, PaymentOpts, StackFunction } from './index'; +import * as lazy from './lazy'; const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); @@ -25,30 +25,30 @@ export function p2pk(a: Payment, opts?: PaymentOpts): Payment { a, ); - const _chunks = <() => Array<Buffer | number>>lazy.value(function() { + const _chunks = lazy.value(() => { return bscript.decompile(a.input!); - }); + }) as StackFunction; const network = a.network || BITCOIN_NETWORK; const o: Payment = { network }; - lazy.prop(o, 'output', function() { + lazy.prop(o, 'output', () => { if (!a.pubkey) return; return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); }); - lazy.prop(o, 'pubkey', function() { + lazy.prop(o, 'pubkey', () => { if (!a.output) return; return a.output.slice(1, -1); }); - lazy.prop(o, 'signature', function() { + lazy.prop(o, 'signature', () => { if (!a.input) return; - return <Buffer>_chunks()[0]; + return _chunks()[0] as Buffer; }); - lazy.prop(o, 'input', function() { + lazy.prop(o, 'input', () => { if (!a.signature) return; return bscript.compile([a.signature]); }); - lazy.prop(o, 'witness', function() { + lazy.prop(o, 'witness', () => { if (!o.input) return; return []; }); diff --git a/types/payments/index.d.ts b/types/payments/index.d.ts index 3009bef..66654d2 100644 --- a/types/payments/index.d.ts +++ b/types/payments/index.d.ts @@ -29,4 +29,5 @@ export interface PaymentOpts { } export declare type StackElement = Buffer | number; export declare type Stack = StackElement[]; +export declare type StackFunction = () => Stack; export { embed, p2ms, p2pk, p2pkh, p2sh, p2wpkh, p2wsh }; From db0e3f1203ae50ba03a58a19f424a2fe7c75bf75 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 12:51:22 +0900 Subject: [PATCH 245/568] Fix lint payments p2pkh --- src/payments/p2pkh.js | 22 +++++++++++----------- ts_src/payments/p2pkh.ts | 38 +++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index 6b41a77..9f06bde 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -1,9 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../script"); const bcrypto = require("../crypto"); -const lazy = require("./lazy"); const networks_1 = require("../networks"); +const bscript = require("../script"); +const lazy = require("./lazy"); const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); @@ -23,18 +23,18 @@ function p2pkh(a, opts) { signature: typef.maybe(bscript.isCanonicalScriptSignature), input: typef.maybe(typef.Buffer), }, a); - const _address = lazy.value(function () { + const _address = lazy.value(() => { const payload = bs58check.decode(a.address); const version = payload.readUInt8(0); const hash = payload.slice(1); return { version, hash }; }); - const _chunks = lazy.value(function () { + const _chunks = lazy.value(() => { return bscript.decompile(a.input); }); const network = a.network || networks_1.bitcoin; const o = { network }; - lazy.prop(o, 'address', function () { + lazy.prop(o, 'address', () => { if (!o.hash) return; const payload = Buffer.allocUnsafe(21); @@ -42,7 +42,7 @@ function p2pkh(a, opts) { o.hash.copy(payload, 1); return bs58check.encode(payload); }); - lazy.prop(o, 'hash', function () { + lazy.prop(o, 'hash', () => { if (a.output) return a.output.slice(3, 23); if (a.address) @@ -50,7 +50,7 @@ function p2pkh(a, opts) { if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey); }); - lazy.prop(o, 'output', function () { + lazy.prop(o, 'output', () => { if (!o.hash) return; return bscript.compile([ @@ -61,24 +61,24 @@ function p2pkh(a, opts) { OPS.OP_CHECKSIG, ]); }); - lazy.prop(o, 'pubkey', function () { + lazy.prop(o, 'pubkey', () => { if (!a.input) return; return _chunks()[1]; }); - lazy.prop(o, 'signature', function () { + lazy.prop(o, 'signature', () => { if (!a.input) return; return _chunks()[0]; }); - lazy.prop(o, 'input', function () { + lazy.prop(o, 'input', () => { if (!a.pubkey) return; if (!a.signature) return; return bscript.compile([a.signature, a.pubkey]); }); - lazy.prop(o, 'witness', function () { + lazy.prop(o, 'witness', () => { if (!o.input) return; return []; diff --git a/ts_src/payments/p2pkh.ts b/ts_src/payments/p2pkh.ts index 63594ad..12c9473 100644 --- a/ts_src/payments/p2pkh.ts +++ b/ts_src/payments/p2pkh.ts @@ -1,8 +1,8 @@ -import { Payment, PaymentOpts } from './index'; -import * as bscript from '../script'; import * as bcrypto from '../crypto'; -import * as lazy from './lazy'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; +import * as bscript from '../script'; +import { Payment, PaymentOpts, StackFunction } from './index'; +import * as lazy from './lazy'; const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); @@ -30,20 +30,20 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { a, ); - const _address = lazy.value(function() { + const _address = lazy.value(() => { const payload = bs58check.decode(a.address); const version = payload.readUInt8(0); const hash = payload.slice(1); return { version, hash }; }); - const _chunks = <() => Array<Buffer | number>>lazy.value(function() { + const _chunks = lazy.value(() => { return bscript.decompile(a.input!); - }); + }) as StackFunction; const network = a.network || BITCOIN_NETWORK; const o: Payment = { network }; - lazy.prop(o, 'address', function() { + lazy.prop(o, 'address', () => { if (!o.hash) return; const payload = Buffer.allocUnsafe(21); @@ -51,12 +51,12 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { o.hash.copy(payload, 1); return bs58check.encode(payload); }); - lazy.prop(o, 'hash', function() { + lazy.prop(o, 'hash', () => { if (a.output) return a.output.slice(3, 23); if (a.address) return _address().hash; if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!); }); - lazy.prop(o, 'output', function() { + lazy.prop(o, 'output', () => { if (!o.hash) return; return bscript.compile([ OPS.OP_DUP, @@ -66,20 +66,20 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { OPS.OP_CHECKSIG, ]); }); - lazy.prop(o, 'pubkey', function() { + lazy.prop(o, 'pubkey', () => { if (!a.input) return; - return <Buffer>_chunks()[1]; + return _chunks()[1] as Buffer; }); - lazy.prop(o, 'signature', function() { + lazy.prop(o, 'signature', () => { if (!a.input) return; - return <Buffer>_chunks()[0]; + return _chunks()[0] as Buffer; }); - lazy.prop(o, 'input', function() { + lazy.prop(o, 'input', () => { if (!a.pubkey) return; if (!a.signature) return; return bscript.compile([a.signature, a.pubkey]); }); - lazy.prop(o, 'witness', function() { + lazy.prop(o, 'witness', () => { if (!o.input) return; return []; }); @@ -127,17 +127,17 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { if (a.input) { const chunks = _chunks(); if (chunks.length !== 2) throw new TypeError('Input is invalid'); - if (!bscript.isCanonicalScriptSignature(<Buffer>chunks[0])) + if (!bscript.isCanonicalScriptSignature(chunks[0] as Buffer)) throw new TypeError('Input has invalid signature'); if (!ecc.isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey'); - if (a.signature && !a.signature.equals(<Buffer>chunks[0])) + if (a.signature && !a.signature.equals(chunks[0] as Buffer)) throw new TypeError('Signature mismatch'); - if (a.pubkey && !a.pubkey.equals(<Buffer>chunks[1])) + if (a.pubkey && !a.pubkey.equals(chunks[1] as Buffer)) throw new TypeError('Pubkey mismatch'); - const pkh = bcrypto.hash160(<Buffer>chunks[1]); + const pkh = bcrypto.hash160(chunks[1] as Buffer); if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch'); } From fe62e130236624d84c7ee78e0679af4fcc7bd136 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 12:59:52 +0900 Subject: [PATCH 246/568] Fix lint payments p2sh --- src/payments/p2sh.js | 26 ++++++++-------- ts_src/payments/index.ts | 2 ++ ts_src/payments/p2sh.ts | 62 +++++++++++++++++++++------------------ types/payments/index.d.ts | 1 + 4 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index f98ef71..e419deb 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -1,8 +1,8 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +const bcrypto = require("../crypto"); const networks_1 = require("../networks"); const bscript = require("../script"); -const bcrypto = require("../crypto"); const lazy = require("./lazy"); const typef = require('typeforce'); const OPS = bscript.OPS; @@ -10,7 +10,7 @@ const bs58check = require('bs58check'); function stacksEqual(a, b) { if (a.length !== b.length) return false; - return a.every(function (x, i) { + return a.every((x, i) => { return x.equals(b[i]); }); } @@ -40,16 +40,16 @@ function p2sh(a, opts) { network = (a.redeem && a.redeem.network) || networks_1.bitcoin; } const o = { network }; - const _address = lazy.value(function () { + const _address = lazy.value(() => { const payload = bs58check.decode(a.address); const version = payload.readUInt8(0); const hash = payload.slice(1); return { version, hash }; }); - const _chunks = lazy.value(function () { + const _chunks = lazy.value(() => { return bscript.decompile(a.input); }); - const _redeem = lazy.value(function () { + const _redeem = lazy.value(() => { const chunks = _chunks(); return { network, @@ -59,7 +59,7 @@ function p2sh(a, opts) { }; }); // output dependents - lazy.prop(o, 'address', function () { + lazy.prop(o, 'address', () => { if (!o.hash) return; const payload = Buffer.allocUnsafe(21); @@ -67,7 +67,7 @@ function p2sh(a, opts) { o.hash.copy(payload, 1); return bs58check.encode(payload); }); - lazy.prop(o, 'hash', function () { + lazy.prop(o, 'hash', () => { // in order of least effort if (a.output) return a.output.slice(2, 22); @@ -76,23 +76,23 @@ function p2sh(a, opts) { if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output); }); - lazy.prop(o, 'output', function () { + lazy.prop(o, 'output', () => { if (!o.hash) return; return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); }); // input dependents - lazy.prop(o, 'redeem', function () { + lazy.prop(o, 'redeem', () => { if (!a.input) return; return _redeem(); }); - lazy.prop(o, 'input', function () { + lazy.prop(o, 'input', () => { if (!a.redeem || !a.redeem.input || !a.redeem.output) return; return bscript.compile([].concat(bscript.decompile(a.redeem.input), a.redeem.output)); }); - lazy.prop(o, 'witness', function () { + lazy.prop(o, 'witness', () => { if (o.redeem && o.redeem.witness) return o.redeem.witness; if (o.input) @@ -126,7 +126,7 @@ function p2sh(a, opts) { hash = hash2; } // inlined to prevent 'no-inner-declarations' failing - const checkRedeem = function (redeem) { + const checkRedeem = (redeem) => { // is the redeem output empty/invalid? if (redeem.output) { const decompile = bscript.decompile(redeem.output); @@ -147,7 +147,7 @@ function p2sh(a, opts) { if (hasInput && hasWitness) throw new TypeError('Input and witness provided'); if (hasInput) { - const richunks = (bscript.decompile(redeem.input)); + const richunks = bscript.decompile(redeem.input); if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig'); } diff --git a/ts_src/payments/index.ts b/ts_src/payments/index.ts index 0c70fba..acd6ddb 100644 --- a/ts_src/payments/index.ts +++ b/ts_src/payments/index.ts @@ -24,6 +24,8 @@ export interface Payment { witness?: Buffer[]; } +export type PaymentFunction = () => Payment; + export interface PaymentOpts { validate?: boolean; allowIncomplete?: boolean; diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index b7df1dc..46c11cc 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -1,17 +1,23 @@ -import { Payment, PaymentOpts } from './index'; +import * as bcrypto from '../crypto'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; -import * as bcrypto from '../crypto'; +import { + Payment, + PaymentFunction, + PaymentOpts, + Stack, + StackFunction, +} from './index'; import * as lazy from './lazy'; const typef = require('typeforce'); const OPS = bscript.OPS; const bs58check = require('bs58check'); -function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean { +function stacksEqual(a: Buffer[], b: Buffer[]): boolean { if (a.length !== b.length) return false; - return a.every(function(x, i) { + return a.every((x, i) => { return x.equals(b[i]); }); } @@ -51,27 +57,29 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { const o: Payment = { network }; - const _address = lazy.value(function() { + const _address = lazy.value(() => { const payload = bs58check.decode(a.address); const version = payload.readUInt8(0); const hash = payload.slice(1); return { version, hash }; }); - const _chunks = <() => Array<Buffer | number>>lazy.value(function() { + const _chunks = lazy.value(() => { return bscript.decompile(a.input!); - }); - const _redeem = lazy.value(function(): Payment { - const chunks = _chunks(); - return { - network, - output: <Buffer>chunks[chunks.length - 1], - input: bscript.compile(chunks.slice(0, -1)), - witness: a.witness || [], - }; - }); + }) as StackFunction; + const _redeem = lazy.value( + (): Payment => { + const chunks = _chunks(); + return { + network, + output: chunks[chunks.length - 1] as Buffer, + input: bscript.compile(chunks.slice(0, -1)), + witness: a.witness || [], + }; + }, + ) as PaymentFunction; // output dependents - lazy.prop(o, 'address', function() { + lazy.prop(o, 'address', () => { if (!o.hash) return; const payload = Buffer.allocUnsafe(21); @@ -79,32 +87,32 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { o.hash.copy(payload, 1); return bs58check.encode(payload); }); - lazy.prop(o, 'hash', function() { + lazy.prop(o, 'hash', () => { // in order of least effort if (a.output) return a.output.slice(2, 22); if (a.address) return _address().hash; if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output); }); - lazy.prop(o, 'output', function() { + lazy.prop(o, 'output', () => { if (!o.hash) return; return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); }); // input dependents - lazy.prop(o, 'redeem', function() { + lazy.prop(o, 'redeem', () => { if (!a.input) return; return _redeem(); }); - lazy.prop(o, 'input', function() { + lazy.prop(o, 'input', () => { if (!a.redeem || !a.redeem.input || !a.redeem.output) return; return bscript.compile( - (<Array<Buffer | number>>[]).concat( - <Array<Buffer | number>>bscript.decompile(a.redeem.input), + ([] as Stack).concat( + bscript.decompile(a.redeem.input) as Stack, a.redeem.output, ), ); }); - lazy.prop(o, 'witness', function() { + lazy.prop(o, 'witness', () => { if (o.redeem && o.redeem.witness) return o.redeem.witness; if (o.input) return []; }); @@ -140,7 +148,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { } // inlined to prevent 'no-inner-declarations' failing - const checkRedeem = function(redeem: Payment): void { + const checkRedeem = (redeem: Payment): void => { // is the redeem output empty/invalid? if (redeem.output) { const decompile = bscript.decompile(redeem.output); @@ -161,9 +169,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { if (hasInput && hasWitness) throw new TypeError('Input and witness provided'); if (hasInput) { - const richunks = <Array<Buffer | number>>( - bscript.decompile(redeem.input) - ); + const richunks = bscript.decompile(redeem.input) as Stack; if (!bscript.isPushOnly(richunks)) throw new TypeError('Non push-only scriptSig'); } diff --git a/types/payments/index.d.ts b/types/payments/index.d.ts index 66654d2..102f20a 100644 --- a/types/payments/index.d.ts +++ b/types/payments/index.d.ts @@ -23,6 +23,7 @@ export interface Payment { redeem?: Payment; witness?: Buffer[]; } +export declare type PaymentFunction = () => Payment; export interface PaymentOpts { validate?: boolean; allowIncomplete?: boolean; From 3ddb88168d8248f3fb86f5e6a8ced725864b5b70 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 13:01:40 +0900 Subject: [PATCH 247/568] Fix lint payments p2wpkh --- src/payments/p2wpkh.js | 20 ++++++++++---------- ts_src/payments/p2wpkh.ts | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index 9165f96..9e99610 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -1,9 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../script"); const bcrypto = require("../crypto"); -const lazy = require("./lazy"); const networks_1 = require("../networks"); +const bscript = require("../script"); +const lazy = require("./lazy"); const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); @@ -26,7 +26,7 @@ function p2wpkh(a, opts) { signature: typef.maybe(bscript.isCanonicalScriptSignature), witness: typef.maybe(typef.arrayOf(typef.Buffer)), }, a); - const _address = lazy.value(function () { + const _address = lazy.value(() => { const result = bech32.decode(a.address); const version = result.words.shift(); const data = bech32.fromWords(result.words); @@ -38,14 +38,14 @@ function p2wpkh(a, opts) { }); const network = a.network || networks_1.bitcoin; const o = { network }; - lazy.prop(o, 'address', function () { + lazy.prop(o, 'address', () => { if (!o.hash) return; const words = bech32.toWords(o.hash); words.unshift(0x00); return bech32.encode(network.bech32, words); }); - lazy.prop(o, 'hash', function () { + lazy.prop(o, 'hash', () => { if (a.output) return a.output.slice(2, 22); if (a.address) @@ -53,29 +53,29 @@ function p2wpkh(a, opts) { if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey); }); - lazy.prop(o, 'output', function () { + lazy.prop(o, 'output', () => { if (!o.hash) return; return bscript.compile([OPS.OP_0, o.hash]); }); - lazy.prop(o, 'pubkey', function () { + lazy.prop(o, 'pubkey', () => { if (a.pubkey) return a.pubkey; if (!a.witness) return; return a.witness[1]; }); - lazy.prop(o, 'signature', function () { + lazy.prop(o, 'signature', () => { if (!a.witness) return; return a.witness[0]; }); - lazy.prop(o, 'input', function () { + lazy.prop(o, 'input', () => { if (!o.witness) return; return EMPTY_BUFFER; }); - lazy.prop(o, 'witness', function () { + lazy.prop(o, 'witness', () => { if (!a.pubkey) return; if (!a.signature) diff --git a/ts_src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts index 04f0e92..7d2748c 100644 --- a/ts_src/payments/p2wpkh.ts +++ b/ts_src/payments/p2wpkh.ts @@ -1,8 +1,8 @@ -import { Payment, PaymentOpts } from './index'; -import * as bscript from '../script'; import * as bcrypto from '../crypto'; -import * as lazy from './lazy'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; +import * as bscript from '../script'; +import { Payment, PaymentOpts } from './index'; +import * as lazy from './lazy'; const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); @@ -33,7 +33,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { a, ); - const _address = lazy.value(function() { + const _address = lazy.value(() => { const result = bech32.decode(a.address); const version = result.words.shift(); const data = bech32.fromWords(result.words); @@ -47,36 +47,36 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { const network = a.network || BITCOIN_NETWORK; const o: Payment = { network }; - lazy.prop(o, 'address', function() { + lazy.prop(o, 'address', () => { if (!o.hash) return; const words = bech32.toWords(o.hash); words.unshift(0x00); return bech32.encode(network.bech32, words); }); - lazy.prop(o, 'hash', function() { + lazy.prop(o, 'hash', () => { if (a.output) return a.output.slice(2, 22); if (a.address) return _address().data; if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey! || o.pubkey!); }); - lazy.prop(o, 'output', function() { + lazy.prop(o, 'output', () => { if (!o.hash) return; return bscript.compile([OPS.OP_0, o.hash]); }); - lazy.prop(o, 'pubkey', function() { + lazy.prop(o, 'pubkey', () => { if (a.pubkey) return a.pubkey; if (!a.witness) return; return a.witness[1]; }); - lazy.prop(o, 'signature', function() { + lazy.prop(o, 'signature', () => { if (!a.witness) return; return a.witness[0]; }); - lazy.prop(o, 'input', function() { + lazy.prop(o, 'input', () => { if (!o.witness) return; return EMPTY_BUFFER; }); - lazy.prop(o, 'witness', function() { + lazy.prop(o, 'witness', () => { if (!a.pubkey) return; if (!a.signature) return; return [a.signature, a.pubkey]; From f058140ea8a0313b319d59b6c17c02d3fa1587cc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 13:05:04 +0900 Subject: [PATCH 248/568] Fix lint payments p2wsh --- src/payments/p2wsh.js | 20 ++++++++++---------- ts_src/payments/p2wsh.ts | 30 +++++++++++++++--------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index 7c71ac3..0def430 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -1,8 +1,8 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +const bcrypto = require("../crypto"); const networks_1 = require("../networks"); const bscript = require("../script"); -const bcrypto = require("../crypto"); const lazy = require("./lazy"); const typef = require('typeforce'); const OPS = bscript.OPS; @@ -11,7 +11,7 @@ const EMPTY_BUFFER = Buffer.alloc(0); function stacksEqual(a, b) { if (a.length !== b.length) return false; - return a.every(function (x, i) { + return a.every((x, i) => { return x.equals(b[i]); }); } @@ -36,7 +36,7 @@ function p2wsh(a, opts) { input: typef.maybe(typef.BufferN(0)), witness: typef.maybe(typef.arrayOf(typef.Buffer)), }, a); - const _address = lazy.value(function () { + const _address = lazy.value(() => { const result = bech32.decode(a.address); const version = result.words.shift(); const data = bech32.fromWords(result.words); @@ -46,7 +46,7 @@ function p2wsh(a, opts) { data: Buffer.from(data), }; }); - const _rchunks = lazy.value(function () { + const _rchunks = lazy.value(() => { return bscript.decompile(a.redeem.input); }); let network = a.network; @@ -54,14 +54,14 @@ function p2wsh(a, opts) { network = (a.redeem && a.redeem.network) || networks_1.bitcoin; } const o = { network }; - lazy.prop(o, 'address', function () { + lazy.prop(o, 'address', () => { if (!o.hash) return; const words = bech32.toWords(o.hash); words.unshift(0x00); return bech32.encode(network.bech32, words); }); - lazy.prop(o, 'hash', function () { + lazy.prop(o, 'hash', () => { if (a.output) return a.output.slice(2); if (a.address) @@ -69,12 +69,12 @@ function p2wsh(a, opts) { if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output); }); - lazy.prop(o, 'output', function () { + lazy.prop(o, 'output', () => { if (!o.hash) return; return bscript.compile([OPS.OP_0, o.hash]); }); - lazy.prop(o, 'redeem', function () { + lazy.prop(o, 'redeem', () => { if (!a.witness) return; return { @@ -83,12 +83,12 @@ function p2wsh(a, opts) { witness: a.witness.slice(0, -1), }; }); - lazy.prop(o, 'input', function () { + lazy.prop(o, 'input', () => { if (!o.witness) return; return EMPTY_BUFFER; }); - lazy.prop(o, 'witness', function () { + lazy.prop(o, 'witness', () => { // transform redeem input to witness stack? if (a.redeem && a.redeem.input && diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 28a235c..131de45 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -1,7 +1,7 @@ -import { Payment, PaymentOpts } from './index'; +import * as bcrypto from '../crypto'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; -import * as bcrypto from '../crypto'; +import { Payment, PaymentOpts, StackFunction } from './index'; import * as lazy from './lazy'; const typef = require('typeforce'); const OPS = bscript.OPS; @@ -10,10 +10,10 @@ const bech32 = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); -function stacksEqual(a: Array<Buffer>, b: Array<Buffer>): boolean { +function stacksEqual(a: Buffer[], b: Buffer[]): boolean { if (a.length !== b.length) return false; - return a.every(function(x, i) { + return a.every((x, i) => { return x.equals(b[i]); }); } @@ -46,7 +46,7 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { a, ); - const _address = lazy.value(function() { + const _address = lazy.value(() => { const result = bech32.decode(a.address); const version = result.words.shift(); const data = bech32.fromWords(result.words); @@ -56,9 +56,9 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { data: Buffer.from(data), }; }); - const _rchunks = <() => Array<Buffer | number>>lazy.value(function() { + const _rchunks = lazy.value(() => { return bscript.decompile(a.redeem!.input!); - }); + }) as StackFunction; let network = a.network; if (!network) { @@ -67,22 +67,22 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { const o: Payment = { network }; - lazy.prop(o, 'address', function() { + lazy.prop(o, 'address', () => { if (!o.hash) return; const words = bech32.toWords(o.hash); words.unshift(0x00); return bech32.encode(network!.bech32, words); }); - lazy.prop(o, 'hash', function() { + lazy.prop(o, 'hash', () => { if (a.output) return a.output.slice(2); if (a.address) return _address().data; if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output); }); - lazy.prop(o, 'output', function() { + lazy.prop(o, 'output', () => { if (!o.hash) return; return bscript.compile([OPS.OP_0, o.hash]); }); - lazy.prop(o, 'redeem', function() { + lazy.prop(o, 'redeem', () => { if (!a.witness) return; return { output: a.witness[a.witness.length - 1], @@ -90,11 +90,11 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { witness: a.witness.slice(0, -1), }; }); - lazy.prop(o, 'input', function() { + lazy.prop(o, 'input', () => { if (!o.witness) return; return EMPTY_BUFFER; }); - lazy.prop(o, 'witness', function() { + lazy.prop(o, 'witness', () => { // transform redeem input to witness stack? if ( a.redeem && @@ -108,13 +108,13 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { // assign, and blank the existing input o.redeem = Object.assign({ witness: stack }, a.redeem); o.redeem.input = EMPTY_BUFFER; - return (<Array<Buffer>>[]).concat(stack, a.redeem.output); + return ([] as Buffer[]).concat(stack, a.redeem.output); } if (!a.redeem) return; if (!a.redeem.output) return; if (!a.redeem.witness) return; - return (<Array<Buffer>>[]).concat(a.redeem.witness, a.redeem.output); + return ([] as Buffer[]).concat(a.redeem.witness, a.redeem.output); }); // extended validation From c2c650812e74a150220cdc05475d2c7e95b4d3fd Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 13:17:03 +0900 Subject: [PATCH 249/568] Fix lint script.ts --- src/script.js | 13 +++++++------ ts_src/script.ts | 40 +++++++++++++++++++--------------------- types/script.d.ts | 7 ++++--- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/script.js b/src/script.js index a114d1f..ac334f8 100644 --- a/src/script.js +++ b/src/script.js @@ -1,8 +1,8 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const types = require("./types"); const scriptNumber = require("./script_number"); const scriptSignature = require("./script_signature"); +const types = require("./types"); const bip66 = require('bip66'); const ecc = require('tiny-secp256k1'); const pushdata = require('pushdata-bitcoin'); @@ -47,7 +47,7 @@ function compile(chunks) { if (chunksIsBuffer(chunks)) return chunks; typeforce(types.Array, chunks); - const bufferSize = chunks.reduce(function (accum, chunk) { + const bufferSize = chunks.reduce((accum, chunk) => { // data chunk if (singleChunkIsBuffer(chunk)) { // adhere to BIP62.3, minimal push policy @@ -61,7 +61,7 @@ function compile(chunks) { }, 0.0); const buffer = Buffer.allocUnsafe(bufferSize); let offset = 0; - chunks.forEach(function (chunk) { + chunks.forEach(chunk => { // data chunk if (singleChunkIsBuffer(chunk)) { // adhere to BIP62.3, minimal push policy @@ -130,7 +130,7 @@ function toASM(chunks) { chunks = decompile(chunks); } return chunks - .map(function (chunk) { + .map(chunk => { // data? if (singleChunkIsBuffer(chunk)) { const op = asMinimalOP(chunk); @@ -146,7 +146,7 @@ function toASM(chunks) { exports.toASM = toASM; function fromASM(asm) { typeforce(types.String, asm); - return compile(asm.split(' ').map(function (chunkStr) { + return compile(asm.split(' ').map(chunkStr => { // opcode? if (exports.OPS[chunkStr] !== undefined) return exports.OPS[chunkStr]; @@ -159,7 +159,7 @@ exports.fromASM = fromASM; function toStack(chunks) { chunks = decompile(chunks); typeforce(isPushOnly, chunks); - return chunks.map(function (op) { + return chunks.map(op => { if (singleChunkIsBuffer(op)) return op; if (op === exports.OPS.OP_0) @@ -186,5 +186,6 @@ function isCanonicalScriptSignature(buffer) { return bip66.check(buffer.slice(0, -1)); } exports.isCanonicalScriptSignature = isCanonicalScriptSignature; +// tslint:disable-next-line variable-name exports.number = scriptNumber; exports.signature = scriptSignature; diff --git a/ts_src/script.ts b/ts_src/script.ts index 36fef95..1c705d3 100644 --- a/ts_src/script.ts +++ b/ts_src/script.ts @@ -1,15 +1,16 @@ -import * as types from './types'; +import { Stack } from './payments'; import * as scriptNumber from './script_number'; import * as scriptSignature from './script_signature'; +import * as types from './types'; const bip66 = require('bip66'); const ecc = require('tiny-secp256k1'); const pushdata = require('pushdata-bitcoin'); const typeforce = require('typeforce'); export type OpCode = number; -export const OPS = <{ [index: string]: OpCode }>require('bitcoin-ops'); +export const OPS = require('bitcoin-ops') as { [index: string]: OpCode }; -const REVERSE_OPS = <{ [index: number]: string }>require('bitcoin-ops/map'); +const REVERSE_OPS = require('bitcoin-ops/map') as { [index: number]: string }; const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 function isOPInt(value: number): boolean { @@ -22,10 +23,10 @@ function isOPInt(value: number): boolean { } function isPushOnlyChunk(value: number | Buffer): boolean { - return types.Buffer(value) || isOPInt(<number>value); + return types.Buffer(value) || isOPInt(value as number); } -export function isPushOnly(value: Array<number | Buffer>) { +export function isPushOnly(value: Stack) { return types.Array(value) && value.every(isPushOnlyChunk); } @@ -36,13 +37,11 @@ function asMinimalOP(buffer: Buffer): number | void { if (buffer[0] === 0x81) return OPS.OP_1NEGATE; } -function chunksIsBuffer(buf: Buffer | Array<number | Buffer>): buf is Buffer { +function chunksIsBuffer(buf: Buffer | Stack): buf is Buffer { return Buffer.isBuffer(buf); } -function chunksIsArray( - buf: Buffer | Array<number | Buffer>, -): buf is Array<number | Buffer> { +function chunksIsArray(buf: Buffer | Stack): buf is Stack { return types.Array(buf); } @@ -50,13 +49,13 @@ function singleChunkIsBuffer(buf: number | Buffer): buf is Buffer { return Buffer.isBuffer(buf); } -export function compile(chunks: Buffer | Array<number | Buffer>): Buffer { +export function compile(chunks: Buffer | Stack): Buffer { // TODO: remove me if (chunksIsBuffer(chunks)) return chunks; typeforce(types.Array, chunks); - const bufferSize = chunks.reduce(function(accum: number, chunk) { + const bufferSize = chunks.reduce((accum: number, chunk) => { // data chunk if (singleChunkIsBuffer(chunk)) { // adhere to BIP62.3, minimal push policy @@ -74,7 +73,7 @@ export function compile(chunks: Buffer | Array<number | Buffer>): Buffer { const buffer = Buffer.allocUnsafe(bufferSize); let offset = 0; - chunks.forEach(function(chunk) { + chunks.forEach(chunk => { // data chunk if (singleChunkIsBuffer(chunk)) { // adhere to BIP62.3, minimal push policy @@ -149,16 +148,16 @@ export function decompile( export function toASM(chunks: Buffer | Array<number | Buffer>): string { if (chunksIsBuffer(chunks)) { - chunks = <Array<number | Buffer>>decompile(chunks); + chunks = decompile(chunks) as Stack; } return chunks - .map(function(chunk) { + .map(chunk => { // data? if (singleChunkIsBuffer(chunk)) { const op = asMinimalOP(chunk); if (op === undefined) return chunk.toString('hex'); - chunk = <number>op; + chunk = op as number; } // opcode! @@ -171,7 +170,7 @@ export function fromASM(asm: string): Buffer { typeforce(types.String, asm); return compile( - asm.split(' ').map(function(chunkStr) { + asm.split(' ').map(chunkStr => { // opcode? if (OPS[chunkStr] !== undefined) return OPS[chunkStr]; typeforce(types.Hex, chunkStr); @@ -182,13 +181,11 @@ export function fromASM(asm: string): Buffer { ); } -export function toStack( - chunks: Buffer | Array<number | Buffer>, -): Array<Buffer> { - chunks = <Array<number | Buffer>>decompile(chunks); +export function toStack(chunks: Buffer | Array<number | Buffer>): Buffer[] { + chunks = decompile(chunks) as Stack; typeforce(isPushOnly, chunks); - return chunks.map(function(op) { + return chunks.map(op => { if (singleChunkIsBuffer(op)) return op; if (op === OPS.OP_0) return Buffer.allocUnsafe(0); @@ -214,5 +211,6 @@ export function isCanonicalScriptSignature(buffer: Buffer): boolean { return bip66.check(buffer.slice(0, -1)); } +// tslint:disable-next-line variable-name export const number = scriptNumber; export const signature = scriptSignature; diff --git a/types/script.d.ts b/types/script.d.ts index 702e76b..52ad4dd 100644 --- a/types/script.d.ts +++ b/types/script.d.ts @@ -1,16 +1,17 @@ /// <reference types="node" /> +import { Stack } from './payments'; import * as scriptNumber from './script_number'; import * as scriptSignature from './script_signature'; export declare type OpCode = number; export declare const OPS: { [index: string]: number; }; -export declare function isPushOnly(value: Array<number | Buffer>): boolean; -export declare function compile(chunks: Buffer | Array<number | Buffer>): Buffer; +export declare function isPushOnly(value: Stack): boolean; +export declare function compile(chunks: Buffer | Stack): Buffer; export declare function decompile(buffer: Buffer | Array<number | Buffer>): Array<number | Buffer> | null; export declare function toASM(chunks: Buffer | Array<number | Buffer>): string; export declare function fromASM(asm: string): Buffer; -export declare function toStack(chunks: Buffer | Array<number | Buffer>): Array<Buffer>; +export declare function toStack(chunks: Buffer | Array<number | Buffer>): Buffer[]; export declare function isCanonicalPubKey(buffer: Buffer): boolean; export declare function isDefinedHashType(hashType: number): boolean; export declare function isCanonicalScriptSignature(buffer: Buffer): boolean; From 94f334851937eed434acf3cf413257394f861f8d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 13:21:18 +0900 Subject: [PATCH 250/568] Fix lint for script_number.ts script_signature.ts --- src/script_number.js | 10 +++++----- src/script_signature.js | 12 +++++------- ts_src/script_number.ts | 10 +++++----- ts_src/script_signature.ts | 12 +++++------- types/script_number.d.ts | 2 +- 5 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/script_number.js b/src/script_number.js index a8c42d3..3a30a43 100644 --- a/src/script_number.js +++ b/src/script_number.js @@ -24,7 +24,7 @@ function decode(buffer, maxLength, minimal) { } // 32-bit / 24-bit / 16-bit / 8-bit let result = 0; - for (var i = 0; i < length; ++i) { + for (let i = 0; i < length; ++i) { result |= buffer[i] << (8 * i); } if (buffer[length - 1] & 0x80) @@ -45,12 +45,12 @@ function scriptNumSize(i) { ? 1 : 0; } -function encode(number) { - let value = Math.abs(number); +function encode(_number) { + let value = Math.abs(_number); const size = scriptNumSize(value); const buffer = Buffer.allocUnsafe(size); - const negative = number < 0; - for (var i = 0; i < size; ++i) { + const negative = _number < 0; + for (let i = 0; i < size; ++i) { buffer.writeUInt8(value & 0xff, i); value >>= 8; } diff --git a/src/script_signature.js b/src/script_signature.js index c185981..a1f2f22 100644 --- a/src/script_signature.js +++ b/src/script_signature.js @@ -29,13 +29,11 @@ function decode(buffer) { const hashTypeMod = hashType & ~0x80; if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType); - const decode = bip66.decode(buffer.slice(0, -1)); - const r = fromDER(decode.r); - const s = fromDER(decode.s); - return { - signature: Buffer.concat([r, s], 64), - hashType: hashType, - }; + const decoded = bip66.decode(buffer.slice(0, -1)); + const r = fromDER(decoded.r); + const s = fromDER(decoded.s); + const signature = Buffer.concat([r, s], 64); + return { signature, hashType }; } exports.decode = decode; function encode(signature, hashType) { diff --git a/ts_src/script_number.ts b/ts_src/script_number.ts index cd48373..a4c502f 100644 --- a/ts_src/script_number.ts +++ b/ts_src/script_number.ts @@ -27,7 +27,7 @@ export function decode( // 32-bit / 24-bit / 16-bit / 8-bit let result = 0; - for (var i = 0; i < length; ++i) { + for (let i = 0; i < length; ++i) { result |= buffer[i] << (8 * i); } @@ -50,13 +50,13 @@ function scriptNumSize(i: number): number { : 0; } -export function encode(number: number): Buffer { - let value = Math.abs(number); +export function encode(_number: number): Buffer { + let value = Math.abs(_number); const size = scriptNumSize(value); const buffer = Buffer.allocUnsafe(size); - const negative = number < 0; + const negative = _number < 0; - for (var i = 0; i < size; ++i) { + for (let i = 0; i < size; ++i) { buffer.writeUInt8(value & 0xff, i); value >>= 8; } diff --git a/ts_src/script_signature.ts b/ts_src/script_signature.ts index 1f11d00..af9930e 100644 --- a/ts_src/script_signature.ts +++ b/ts_src/script_signature.ts @@ -33,14 +33,12 @@ export function decode(buffer: Buffer): ScriptSignature { if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType); - const decode = bip66.decode(buffer.slice(0, -1)); - const r = fromDER(decode.r); - const s = fromDER(decode.s); + const decoded = bip66.decode(buffer.slice(0, -1)); + const r = fromDER(decoded.r); + const s = fromDER(decoded.s); + const signature = Buffer.concat([r, s], 64); - return { - signature: Buffer.concat([r, s], 64), - hashType: hashType, - }; + return { signature, hashType }; } export function encode(signature: Buffer, hashType: number): Buffer { diff --git a/types/script_number.d.ts b/types/script_number.d.ts index d0b87b1..015bb89 100644 --- a/types/script_number.d.ts +++ b/types/script_number.d.ts @@ -1,3 +1,3 @@ /// <reference types="node" /> export declare function decode(buffer: Buffer, maxLength?: number, minimal?: boolean): number; -export declare function encode(number: number): Buffer; +export declare function encode(_number: number): Buffer; From e6ea0389a2c22c096da0707455735ed7eb8ce36a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 13:40:23 +0900 Subject: [PATCH 251/568] Fix lint for transaction.ts --- src/transaction.js | 90 +++++++++++----------- ts_src/transaction.ts | 168 +++++++++++++++++++++-------------------- types/transaction.d.ts | 34 +++++---- 3 files changed, 148 insertions(+), 144 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index cfd31fe..c6c847a 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,11 +1,11 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const bcrypto = require("./crypto"); -const bscript = require("./script"); -const types = require("./types"); const bufferutils = require("./bufferutils"); const bufferutils_1 = require("./bufferutils"); +const bcrypto = require("./crypto"); +const bscript = require("./script"); const script_1 = require("./script"); +const types = require("./types"); const typeforce = require('typeforce'); const varuint = require('varuint-bitcoin'); function varSliceSize(someScript) { @@ -38,7 +38,7 @@ class Transaction { this.ins = []; this.outs = []; } - static fromBuffer(buffer, __noStrict) { + static fromBuffer(buffer, _NO_STRICT) { let offset = 0; function readSlice(n) { offset += n; @@ -70,7 +70,7 @@ class Transaction { function readVector() { const count = readVarInt(); const vector = []; - for (var i = 0; i < count; i++) + for (let i = 0; i < count; i++) vector.push(readVarSlice()); return vector; } @@ -85,7 +85,7 @@ class Transaction { hasWitnesses = true; } const vinLen = readVarInt(); - for (var i = 0; i < vinLen; ++i) { + for (let i = 0; i < vinLen; ++i) { tx.ins.push({ hash: readSlice(32), index: readUInt32(), @@ -95,14 +95,14 @@ class Transaction { }); } const voutLen = readVarInt(); - for (i = 0; i < voutLen; ++i) { + for (let i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), script: readVarSlice(), }); } if (hasWitnesses) { - for (i = 0; i < vinLen; ++i) { + for (let i = 0; i < vinLen; ++i) { tx.ins[i].witness = readVector(); } // was this pointless? @@ -110,7 +110,7 @@ class Transaction { throw new Error('Transaction has superfluous witness data'); } tx.locktime = readUInt32(); - if (__noStrict) + if (_NO_STRICT) return tx; if (offset !== buffer.length) throw new Error('Transaction has unexpected data'); @@ -121,7 +121,7 @@ class Transaction { } static isCoinbaseHash(buffer) { typeforce(types.Hash256bit, buffer); - for (var i = 0; i < 32; ++i) { + for (let i = 0; i < 32; ++i) { if (buffer[i] !== 0) return false; } @@ -137,8 +137,8 @@ class Transaction { } // Add the input and return the input's index return (this.ins.push({ - hash: hash, - index: index, + hash, + index, script: scriptSig || EMPTY_SCRIPT, sequence: sequence, witness: EMPTY_WITNESS, @@ -149,7 +149,7 @@ class Transaction { // Add the output and return the output's index return (this.outs.push({ script: scriptPubKey, - value: value, + value, }) - 1); } hasWitnesses() { @@ -168,23 +168,6 @@ class Transaction { byteLength() { return this.__byteLength(true); } - __byteLength(__allowWitness) { - const hasWitnesses = __allowWitness && this.hasWitnesses(); - return ((hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce((sum, input) => { - return sum + 40 + varSliceSize(input.script); - }, 0) + - this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script); - }, 0) + - (hasWitnesses - ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness); - }, 0) - : 0)); - } clone() { const newTx = new Transaction(); newTx.version = this.version; @@ -242,7 +225,7 @@ class Transaction { // truncate outputs after txTmp.outs.length = inIndex + 1; // "blank" outputs before - for (var i = 0; i < inIndex; i++) { + for (let i = 0; i < inIndex; i++) { txTmp.outs[i] = BLANK_OUTPUT; } // ignore sequence numbers (except at inIndex) @@ -365,9 +348,37 @@ class Transaction { toBuffer(buffer, initialOffset) { return this.__toBuffer(buffer, initialOffset, true); } - __toBuffer(buffer, initialOffset, __allowWitness) { + toHex() { + return this.toBuffer(undefined, undefined).toString('hex'); + } + setInputScript(index, scriptSig) { + typeforce(types.tuple(types.Number, types.Buffer), arguments); + this.ins[index].script = scriptSig; + } + setWitness(index, witness) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments); + this.ins[index].witness = witness; + } + __byteLength(_ALLOW_WITNESS) { + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); + return ((hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script); + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0) + + (hasWitnesses + ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) + : 0)); + } + __toBuffer(buffer, initialOffset, _ALLOW_WITNESS) { if (!buffer) - buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)); + buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS)); let offset = initialOffset || 0; function writeSlice(slice) { offset += slice.copy(buffer, offset); @@ -397,7 +408,7 @@ class Transaction { vector.forEach(writeVarSlice); } writeInt32(this.version); - const hasWitnesses = __allowWitness && this.hasWitnesses(); + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); if (hasWitnesses) { writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); @@ -430,17 +441,6 @@ class Transaction { return buffer.slice(initialOffset, offset); return buffer; } - toHex() { - return this.toBuffer(undefined, undefined).toString('hex'); - } - setInputScript(index, scriptSig) { - typeforce(types.tuple(types.Number, types.Buffer), arguments); - this.ins[index].script = scriptSig; - } - setWitness(index, witness) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments); - this.ins[index].witness = witness; - } } Transaction.DEFAULT_SEQUENCE = 0xffffffff; Transaction.SIGHASH_ALL = 0x01; diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index a456a32..995f1d7 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -1,9 +1,9 @@ -import * as bcrypto from './crypto'; -import * as bscript from './script'; -import * as types from './types'; import * as bufferutils from './bufferutils'; import { reverseBuffer } from './bufferutils'; +import * as bcrypto from './crypto'; +import * as bscript from './script'; import { OPS as opcodes } from './script'; +import * as types from './types'; const typeforce = require('typeforce'); const varuint = require('varuint-bitcoin'); @@ -14,7 +14,7 @@ function varSliceSize(someScript: Buffer): number { return varuint.encodingLength(length) + length; } -function vectorSize(someVector: Array<Buffer>): number { +function vectorSize(someVector: Buffer[]): number { const length = someVector.length; return ( @@ -26,7 +26,7 @@ function vectorSize(someVector: Array<Buffer>): number { } const EMPTY_SCRIPT: Buffer = Buffer.allocUnsafe(0); -const EMPTY_WITNESS: Array<Buffer> = []; +const EMPTY_WITNESS: Buffer[] = []; const ZERO: Buffer = Buffer.from( '0000000000000000000000000000000000000000000000000000000000000000', 'hex', @@ -42,33 +42,30 @@ const BLANK_OUTPUT: BlankOutput = { }; function isOutput(out: Output | BlankOutput): out is Output { - return (<Output>out).value !== undefined; + return (out as Output).value !== undefined; } -export type BlankOutput = { +export interface BlankOutput { script: Buffer; valueBuffer: Buffer; -}; +} -export type Output = { +export interface Output { script: Buffer; value: number; -}; +} -export type Input = { +type OpenOutput = Output | BlankOutput; + +export interface Input { hash: Buffer; index: number; script: Buffer; sequence: number; - witness: Array<Buffer>; -}; + witness: Buffer[]; +} export class Transaction { - version: number; - locktime: number; - ins: Array<Input>; - outs: Array<Output | BlankOutput>; - static readonly DEFAULT_SEQUENCE = 0xffffffff; static readonly SIGHASH_ALL = 0x01; static readonly SIGHASH_NONE = 0x02; @@ -77,14 +74,7 @@ export class Transaction { static readonly ADVANCED_TRANSACTION_MARKER = 0x00; static readonly ADVANCED_TRANSACTION_FLAG = 0x01; - constructor() { - this.version = 1; - this.locktime = 0; - this.ins = []; - this.outs = []; - } - - static fromBuffer(buffer: Buffer, __noStrict?: boolean): Transaction { + static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction { let offset: number = 0; function readSlice(n: number): Buffer { @@ -120,10 +110,10 @@ export class Transaction { return readSlice(readVarInt()); } - function readVector(): Array<Buffer> { + function readVector(): Buffer[] { const count = readVarInt(); - const vector: Array<Buffer> = []; - for (var i = 0; i < count; i++) vector.push(readVarSlice()); + const vector: Buffer[] = []; + for (let i = 0; i < count; i++) vector.push(readVarSlice()); return vector; } @@ -143,7 +133,7 @@ export class Transaction { } const vinLen = readVarInt(); - for (var i = 0; i < vinLen; ++i) { + for (let i = 0; i < vinLen; ++i) { tx.ins.push({ hash: readSlice(32), index: readUInt32(), @@ -154,7 +144,7 @@ export class Transaction { } const voutLen = readVarInt(); - for (i = 0; i < voutLen; ++i) { + for (let i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), script: readVarSlice(), @@ -162,7 +152,7 @@ export class Transaction { } if (hasWitnesses) { - for (i = 0; i < vinLen; ++i) { + for (let i = 0; i < vinLen; ++i) { tx.ins[i].witness = readVector(); } @@ -173,7 +163,7 @@ export class Transaction { tx.locktime = readUInt32(); - if (__noStrict) return tx; + if (_NO_STRICT) return tx; if (offset !== buffer.length) throw new Error('Transaction has unexpected data'); @@ -186,12 +176,24 @@ export class Transaction { static isCoinbaseHash(buffer: Buffer): boolean { typeforce(types.Hash256bit, buffer); - for (var i = 0; i < 32; ++i) { + for (let i = 0; i < 32; ++i) { if (buffer[i] !== 0) return false; } return true; } + version: number; + locktime: number; + ins: Input[]; + outs: OpenOutput[]; + + constructor() { + this.version = 1; + this.locktime = 0; + this.ins = []; + this.outs = []; + } + isCoinbase(): boolean { return ( this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) @@ -221,10 +223,10 @@ export class Transaction { // Add the input and return the input's index return ( this.ins.push({ - hash: hash, - index: index, + hash, + index, script: scriptSig || EMPTY_SCRIPT, - sequence: <number>sequence, + sequence: sequence as number, witness: EMPTY_WITNESS, }) - 1 ); @@ -237,7 +239,7 @@ export class Transaction { return ( this.outs.push({ script: scriptPubKey, - value: value, + value, }) - 1 ); } @@ -262,27 +264,6 @@ export class Transaction { return this.__byteLength(true); } - private __byteLength(__allowWitness: boolean): number { - const hasWitnesses = __allowWitness && this.hasWitnesses(); - - return ( - (hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce((sum, input) => { - return sum + 40 + varSliceSize(input.script); - }, 0) + - this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script); - }, 0) + - (hasWitnesses - ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness); - }, 0) - : 0) - ); - } - clone(): Transaction { const newTx = new Transaction(); newTx.version = this.version; @@ -301,7 +282,7 @@ export class Transaction { newTx.outs = this.outs.map(txOut => { return { script: txOut.script, - value: (<Output>txOut).value, + value: (txOut as Output).value, }; }); @@ -358,7 +339,7 @@ export class Transaction { txTmp.outs.length = inIndex + 1; // "blank" outputs before - for (var i = 0; i < inIndex; i++) { + for (let i = 0; i < inIndex; i++) { txTmp.outs[i] = BLANK_OUTPUT; } @@ -471,7 +452,7 @@ export class Transaction { toffset = 0; this.outs.forEach(out => { - writeUInt64((<Output>out).value); + writeUInt64((out as Output).value); writeVarSlice(out.script); }); @@ -484,7 +465,7 @@ export class Transaction { tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); toffset = 0; - writeUInt64((<Output>output).value); + writeUInt64((output as Output).value); writeVarSlice(output.script); hashOutputs = bcrypto.hash256(tbuffer); @@ -523,13 +504,50 @@ export class Transaction { return this.__toBuffer(buffer, initialOffset, true); } + toHex() { + return this.toBuffer(undefined, undefined).toString('hex'); + } + + setInputScript(index: number, scriptSig: Buffer) { + typeforce(types.tuple(types.Number, types.Buffer), arguments); + + this.ins[index].script = scriptSig; + } + + setWitness(index: number, witness: Buffer[]) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments); + + this.ins[index].witness = witness; + } + + private __byteLength(_ALLOW_WITNESS: boolean): number { + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); + + return ( + (hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script); + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0) + + (hasWitnesses + ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) + : 0) + ); + } + private __toBuffer( buffer?: Buffer, initialOffset?: number, - __allowWitness?: boolean, + _ALLOW_WITNESS?: boolean, ): Buffer { if (!buffer) - buffer = <Buffer>Buffer.allocUnsafe(this.__byteLength(__allowWitness!)); + buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS!)) as Buffer; let offset = initialOffset || 0; @@ -563,14 +581,14 @@ export class Transaction { writeSlice(slice); } - function writeVector(vector: Array<Buffer>) { + function writeVector(vector: Buffer[]) { writeVarInt(vector.length); vector.forEach(writeVarSlice); } writeInt32(this.version); - const hasWitnesses = __allowWitness && this.hasWitnesses(); + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); if (hasWitnesses) { writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); @@ -609,20 +627,4 @@ export class Transaction { if (initialOffset !== undefined) return buffer.slice(initialOffset, offset); return buffer; } - - toHex() { - return this.toBuffer(undefined, undefined).toString('hex'); - } - - setInputScript(index: number, scriptSig: Buffer) { - typeforce(types.tuple(types.Number, types.Buffer), arguments); - - this.ins[index].script = scriptSig; - } - - setWitness(index: number, witness: Array<Buffer>) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments); - - this.ins[index].witness = witness; - } } diff --git a/types/transaction.d.ts b/types/transaction.d.ts index 60bbf45..4f8a2b9 100644 --- a/types/transaction.d.ts +++ b/types/transaction.d.ts @@ -1,24 +1,21 @@ /// <reference types="node" /> -export declare type BlankOutput = { +export interface BlankOutput { script: Buffer; valueBuffer: Buffer; -}; -export declare type Output = { +} +export interface Output { script: Buffer; value: number; -}; -export declare type Input = { +} +declare type OpenOutput = Output | BlankOutput; +export interface Input { hash: Buffer; index: number; script: Buffer; sequence: number; - witness: Array<Buffer>; -}; + witness: Buffer[]; +} export declare class Transaction { - version: number; - locktime: number; - ins: Array<Input>; - outs: Array<Output | BlankOutput>; static readonly DEFAULT_SEQUENCE = 4294967295; static readonly SIGHASH_ALL = 1; static readonly SIGHASH_NONE = 2; @@ -26,10 +23,14 @@ export declare class Transaction { static readonly SIGHASH_ANYONECANPAY = 128; static readonly ADVANCED_TRANSACTION_MARKER = 0; static readonly ADVANCED_TRANSACTION_FLAG = 1; - constructor(); - static fromBuffer(buffer: Buffer, __noStrict?: boolean): Transaction; + static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction; static fromHex(hex: string): Transaction; static isCoinbaseHash(buffer: Buffer): boolean; + version: number; + locktime: number; + ins: Input[]; + outs: OpenOutput[]; + constructor(); isCoinbase(): boolean; addInput(hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number; addOutput(scriptPubKey: Buffer, value: number): number; @@ -37,7 +38,6 @@ export declare class Transaction { weight(): number; virtualSize(): number; byteLength(): number; - private __byteLength; clone(): Transaction; /** * Hash transaction for signing a specific input. @@ -52,8 +52,10 @@ export declare class Transaction { getHash(forWitness?: boolean): Buffer; getId(): string; toBuffer(buffer?: Buffer, initialOffset?: number): Buffer; - private __toBuffer; toHex(): string; setInputScript(index: number, scriptSig: Buffer): void; - setWitness(index: number, witness: Array<Buffer>): void; + setWitness(index: number, witness: Buffer[]): void; + private __byteLength; + private __toBuffer; } +export {}; From 512b03e28406a281b37fbe357c303ecfc16915c3 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 14:03:04 +0900 Subject: [PATCH 252/568] Fix lint transaction_builder.ts --- src/transaction_builder.js | 247 ++++++++-------- test/fixtures/transaction_builder.json | 2 +- test/transaction_builder.js | 26 +- ts_src/transaction_builder.ts | 377 +++++++++++++------------ types/transaction_builder.d.ts | 14 +- 5 files changed, 336 insertions(+), 330 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 540a17a..0b876a7 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -1,16 +1,16 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const networks = require("./networks"); -const bufferutils_1 = require("./bufferutils"); -const transaction_1 = require("./transaction"); -const ECPair = require("./ecpair"); -const types = require("./types"); const baddress = require("./address"); -const bcrypto = require("./crypto"); -const bscript = require("./script"); -const payments = require("./payments"); +const bufferutils_1 = require("./bufferutils"); const classify = require("./classify"); +const bcrypto = require("./crypto"); +const ECPair = require("./ecpair"); +const networks = require("./networks"); +const payments = require("./payments"); +const bscript = require("./script"); const script_1 = require("./script"); +const transaction_1 = require("./transaction"); +const types = require("./types"); const typeforce = require('typeforce'); const SCRIPT_TYPES = classify.types; function txIsString(tx) { @@ -20,15 +20,6 @@ function txIsTransaction(tx) { return tx instanceof transaction_1.Transaction; } class TransactionBuilder { - constructor(network, maximumFeeRate) { - this.__prevTxSet = {}; - this.network = network || networks.bitcoin; - // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) - this.maximumFeeRate = maximumFeeRate || 2500; - this.__inputs = []; - this.__tx = new transaction_1.Transaction(); - this.__tx.version = 2; - } static fromTransaction(transaction, network) { const txb = new TransactionBuilder(network); // Copy transaction fields @@ -47,33 +38,42 @@ class TransactionBuilder { }); }); // fix some things not possible through the public API - txb.__inputs.forEach((input, i) => { + txb.__INPUTS.forEach((input, i) => { fixMultisigOrder(input, transaction, i); }); return txb; } + constructor(network, maximumFeeRate) { + this.__PREV_TX_SET = {}; + this.network = network || networks.bitcoin; + // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) + this.maximumFeeRate = maximumFeeRate || 2500; + this.__INPUTS = []; + this.__TX = new transaction_1.Transaction(); + this.__TX.version = 2; + } setLockTime(locktime) { typeforce(types.UInt32, locktime); // if any signatures exist, throw - if (this.__inputs.some(input => { + if (this.__INPUTS.some(input => { if (!input.signatures) return false; return input.signatures.some(s => s !== undefined); })) { throw new Error('No, this would invalidate signatures'); } - this.__tx.locktime = locktime; + this.__TX.locktime = locktime; } setVersion(version) { typeforce(types.UInt32, version); // XXX: this might eventually become more complex depending on what the versions represent - this.__tx.version = version; + this.__TX.version = version; } addInput(txHash, vout, sequence, prevOutScript) { if (!this.__canModifyInputs()) { throw new Error('No, this would invalidate signatures'); } - let value = undefined; + let value; // is it a hex string? if (txIsString(txHash)) { // transaction hashs's are displayed in reverse order, un-reverse it @@ -87,17 +87,90 @@ class TransactionBuilder { txHash = txHash.getHash(false); } return this.__addInputUnsafe(txHash, vout, { - sequence: sequence, - prevOutScript: prevOutScript, - value: value, + sequence, + prevOutScript, + value, }); } + addOutput(scriptPubKey, value) { + if (!this.__canModifyOutputs()) { + throw new Error('No, this would invalidate signatures'); + } + // Attempt to get a script if it's a base58 or bech32 address string + if (typeof scriptPubKey === 'string') { + scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); + } + return this.__TX.addOutput(scriptPubKey, value); + } + build() { + return this.__build(false); + } + buildIncomplete() { + return this.__build(true); + } + sign(vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) { + // TODO: remove keyPair.network matching in 4.0.0 + if (keyPair.network && keyPair.network !== this.network) + throw new TypeError('Inconsistent network'); + if (!this.__INPUTS[vin]) + throw new Error('No input at index: ' + vin); + hashType = hashType || transaction_1.Transaction.SIGHASH_ALL; + if (this.__needsOutputs(hashType)) + throw new Error('Transaction needs outputs'); + const input = this.__INPUTS[vin]; + // if redeemScript was previously provided, enforce consistency + if (input.redeemScript !== undefined && + redeemScript && + !input.redeemScript.equals(redeemScript)) { + throw new Error('Inconsistent redeemScript'); + } + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey(); + if (!canSign(input)) { + if (witnessValue !== undefined) { + if (input.value !== undefined && input.value !== witnessValue) + throw new Error('Input did not match witnessValue'); + typeforce(types.Satoshi, witnessValue); + input.value = witnessValue; + } + if (!canSign(input)) { + const prepared = prepareInput(input, ourPubKey, redeemScript, witnessScript); + // updates inline + Object.assign(input, prepared); + } + if (!canSign(input)) + throw Error(input.prevOutType + ' not supported'); + } + // ready to sign + let signatureHash; + if (input.hasWitness) { + signatureHash = this.__TX.hashForWitnessV0(vin, input.signScript, input.value, hashType); + } + else { + signatureHash = this.__TX.hashForSignature(vin, input.signScript, hashType); + } + // enforce in order signing of public keys + const signed = input.pubkeys.some((pubKey, i) => { + if (!ourPubKey.equals(pubKey)) + return false; + if (input.signatures[i]) + throw new Error('Signature already exists'); + // TODO: add tests + if (ourPubKey.length !== 33 && input.hasWitness) { + throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH'); + } + const signature = keyPair.sign(signatureHash); + input.signatures[i] = bscript.signature.encode(signature, hashType); + return true; + }); + if (!signed) + throw new Error('Key pair cannot sign for this input'); + } __addInputUnsafe(txHash, vout, options) { if (transaction_1.Transaction.isCoinbaseHash(txHash)) { throw new Error('coinbase inputs not supported'); } const prevTxOut = txHash.toString('hex') + ':' + vout; - if (this.__prevTxSet[prevTxOut] !== undefined) + if (this.__PREV_TX_SET[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut); let input = {}; // derive what we can from the scriptSig @@ -122,37 +195,21 @@ class TransactionBuilder { input.prevOutScript = options.prevOutScript; input.prevOutType = prevOutType || classify.output(options.prevOutScript); } - const vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig); - this.__inputs[vin] = input; - this.__prevTxSet[prevTxOut] = true; + const vin = this.__TX.addInput(txHash, vout, options.sequence, options.scriptSig); + this.__INPUTS[vin] = input; + this.__PREV_TX_SET[prevTxOut] = true; return vin; } - addOutput(scriptPubKey, value) { - if (!this.__canModifyOutputs()) { - throw new Error('No, this would invalidate signatures'); - } - // Attempt to get a script if it's a base58 or bech32 address string - if (typeof scriptPubKey === 'string') { - scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); - } - return this.__tx.addOutput(scriptPubKey, value); - } - build() { - return this.__build(false); - } - buildIncomplete() { - return this.__build(true); - } __build(allowIncomplete) { if (!allowIncomplete) { - if (!this.__tx.ins.length) + if (!this.__TX.ins.length) throw new Error('Transaction has no inputs'); - if (!this.__tx.outs.length) + if (!this.__TX.outs.length) throw new Error('Transaction has no outputs'); } - const tx = this.__tx.clone(); + const tx = this.__TX.clone(); // create script signatures from inputs - this.__inputs.forEach((input, i) => { + this.__INPUTS.forEach((input, i) => { if (!input.prevOutType && !allowIncomplete) throw new Error('Transaction is not complete'); const result = build(input.prevOutType, input, allowIncomplete); @@ -174,65 +231,8 @@ class TransactionBuilder { } return tx; } - sign(vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) { - // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== this.network) - throw new TypeError('Inconsistent network'); - if (!this.__inputs[vin]) - throw new Error('No input at index: ' + vin); - hashType = hashType || transaction_1.Transaction.SIGHASH_ALL; - if (this.__needsOutputs(hashType)) - throw new Error('Transaction needs outputs'); - const input = this.__inputs[vin]; - // if redeemScript was previously provided, enforce consistency - if (input.redeemScript !== undefined && - redeemScript && - !input.redeemScript.equals(redeemScript)) { - throw new Error('Inconsistent redeemScript'); - } - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey(); - if (!canSign(input)) { - if (witnessValue !== undefined) { - if (input.value !== undefined && input.value !== witnessValue) - throw new Error("Input didn't match witnessValue"); - typeforce(types.Satoshi, witnessValue); - input.value = witnessValue; - } - if (!canSign(input)) { - const prepared = prepareInput(input, ourPubKey, redeemScript, witnessScript); - // updates inline - Object.assign(input, prepared); - } - if (!canSign(input)) - throw Error(input.prevOutType + ' not supported'); - } - // ready to sign - let signatureHash; - if (input.hasWitness) { - signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType); - } - else { - signatureHash = this.__tx.hashForSignature(vin, input.signScript, hashType); - } - // enforce in order signing of public keys - const signed = input.pubkeys.some((pubKey, i) => { - if (!ourPubKey.equals(pubKey)) - return false; - if (input.signatures[i]) - throw new Error('Signature already exists'); - // TODO: add tests - if (ourPubKey.length !== 33 && input.hasWitness) { - throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH'); - } - const signature = keyPair.sign(signatureHash); - input.signatures[i] = bscript.signature.encode(signature, hashType); - return true; - }); - if (!signed) - throw new Error('Key pair cannot sign for this input'); - } __canModifyInputs() { - return this.__inputs.every(input => { + return this.__INPUTS.every(input => { if (!input.signatures) return true; return input.signatures.every(signature => { @@ -247,12 +247,12 @@ class TransactionBuilder { } __needsOutputs(signingHashType) { if (signingHashType === transaction_1.Transaction.SIGHASH_ALL) { - return this.__tx.outs.length === 0; + return this.__TX.outs.length === 0; } // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs // .build() will fail, but .buildIncomplete() is OK - return (this.__tx.outs.length === 0 && - this.__inputs.some(input => { + return (this.__TX.outs.length === 0 && + this.__INPUTS.some(input => { if (!input.signatures) return false; return input.signatures.some(signature => { @@ -266,9 +266,9 @@ class TransactionBuilder { })); } __canModifyOutputs() { - const nInputs = this.__tx.ins.length; - const nOutputs = this.__tx.outs.length; - return this.__inputs.every(input => { + const nInputs = this.__TX.ins.length; + const nOutputs = this.__TX.outs.length; + return this.__INPUTS.every(input => { if (input.signatures === undefined) return true; return input.signatures.every(signature => { @@ -290,10 +290,10 @@ class TransactionBuilder { } __overMaximumFees(bytes) { // not all inputs will have .value defined - const incoming = this.__inputs.reduce((a, x) => a + (x.value >>> 0), 0); + const incoming = this.__INPUTS.reduce((a, x) => a + (x.value >>> 0), 0); // but all outputs do, and if we have any input value // we can immediately determine if the outputs are too small - const outgoing = this.__tx.outs.reduce((a, x) => a + x.value, 0); + const outgoing = this.__TX.outs.reduce((a, x) => a + x.value, 0); const fee = incoming - outgoing; const feeRate = fee / bytes; return feeRate > this.maximumFeeRate; @@ -350,8 +350,8 @@ function expandInput(scriptSig, witnessStack, type, scriptPubKey) { }, { allowIncomplete: true }); return { prevOutType: SCRIPT_TYPES.P2MS, - pubkeys: pubkeys, - signatures: signatures, + pubkeys, + signatures, maxSignatures: m, }; } @@ -488,7 +488,9 @@ function expandOutput(script, ourPubKey) { } function prepareInput(input, ourPubKey, redeemScript, witnessScript) { if (redeemScript && witnessScript) { - const p2wsh = (payments.p2wsh({ redeem: { output: witnessScript } })); + const p2wsh = payments.p2wsh({ + redeem: { output: witnessScript }, + }); const p2wshAlt = payments.p2wsh({ output: redeemScript }); const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); const p2shAlt = payments.p2sh({ redeem: p2wsh }); @@ -506,7 +508,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures; } - let signScript = witnessScript; + const signScript = witnessScript; if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure'); return { @@ -579,7 +581,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { if (input.signatures && input.signatures.some(x => x !== undefined)) { expanded.signatures = input.signatures; } - let signScript = witnessScript; + const signScript = witnessScript; if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2WSH(P2WPKH) is a consensus failure'); return { @@ -614,7 +616,8 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { } let signScript = input.prevOutScript; if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = (payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output); + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }) + .output; } return { prevOutType: expanded.type, @@ -630,7 +633,7 @@ function prepareInput(input, ourPubKey, redeemScript, witnessScript) { const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output; return { prevOutType: SCRIPT_TYPES.P2PKH, - prevOutScript: prevOutScript, + prevOutScript, hasWitness: false, signScript: prevOutScript, signType: SCRIPT_TYPES.P2PKH, diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index a2ca5d7..24be3ba 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -1976,7 +1976,7 @@ "sign": [ { "description": "Transaction w/ witness value mismatch", - "exception": "Input didn\\'t match witnessValue", + "exception": "Input did not match witnessValue", "network": "testnet", "inputs": [ { diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 574b669..3f84b19 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -164,7 +164,7 @@ describe('TransactionBuilder', function () { const tx = Transaction.fromHex(fixtures.valid.classification.hex) const txb = TransactionBuilder.fromTransaction(tx) - txb.__inputs.forEach(function (i) { + txb.__INPUTS.forEach(function (i) { assert.strictEqual(i.prevOutType, 'scripthash') assert.strictEqual(i.redeemScriptType, 'multisig') }) @@ -191,22 +191,22 @@ describe('TransactionBuilder', function () { const vin = txb.addInput(txHash, 1, 54) assert.strictEqual(vin, 0) - const txIn = txb.__tx.ins[0] + const txIn = txb.__TX.ins[0] assert.strictEqual(txIn.hash, txHash) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__inputs[0].prevOutScript, undefined) + assert.strictEqual(txb.__INPUTS[0].prevOutScript, undefined) }) it('accepts a txHash, index [, sequence number and scriptPubKey]', function () { const vin = txb.addInput(txHash, 1, 54, scripts[1]) assert.strictEqual(vin, 0) - const txIn = txb.__tx.ins[0] + const txIn = txb.__TX.ins[0] assert.strictEqual(txIn.hash, txHash) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__inputs[0].prevOutScript, scripts[1]) + assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) }) it('accepts a prevTx, index [and sequence number]', function () { @@ -217,11 +217,11 @@ describe('TransactionBuilder', function () { const vin = txb.addInput(prevTx, 1, 54) assert.strictEqual(vin, 0) - const txIn = txb.__tx.ins[0] + const txIn = txb.__TX.ins[0] assert.deepEqual(txIn.hash, prevTx.getHash()) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__inputs[0].prevOutScript, scripts[1]) + assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) }) it('returns the input index', function () { @@ -251,7 +251,7 @@ describe('TransactionBuilder', function () { const vout = txb.addOutput(address, 1000) assert.strictEqual(vout, 0) - const txout = txb.__tx.outs[0] + const txout = txb.__TX.outs[0] assert.deepEqual(txout.script, scripts[0]) assert.strictEqual(txout.value, 1000) }) @@ -260,7 +260,7 @@ describe('TransactionBuilder', function () { const vout = txb.addOutput(scripts[0], 1000) assert.strictEqual(vout, 0) - const txout = txb.__tx.outs[0] + const txout = txb.__TX.outs[0] assert.deepEqual(txout.script, scripts[0]) assert.strictEqual(txout.value, 1000) }) @@ -533,10 +533,10 @@ describe('TransactionBuilder', function () { '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000' const txb = TransactionBuilder.fromTransaction(Transaction.fromHex(rawtx)) - txb.__inputs[0].value = 241530 - txb.__inputs[1].value = 241530 - txb.__inputs[2].value = 248920 - txb.__inputs[3].value = 248920 + txb.__INPUTS[0].value = 241530 + txb.__INPUTS[1].value = 241530 + txb.__INPUTS[2].value = 248920 + txb.__INPUTS[3].value = 248920 assert.throws(function () { txb.build() diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index a3bd9e6..4c939e1 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -1,24 +1,25 @@ -import { Network } from './networks'; -import * as networks from './networks'; +import * as baddress from './address'; import { reverseBuffer } from './bufferutils'; -import { Transaction, Output } from './transaction'; +import * as classify from './classify'; +import * as bcrypto from './crypto'; import { ECPairInterface } from './ecpair'; import * as ECPair from './ecpair'; -import * as types from './types'; -import * as baddress from './address'; -import * as bcrypto from './crypto'; -import * as bscript from './script'; +import { Network } from './networks'; +import * as networks from './networks'; import { Payment } from './payments'; import * as payments from './payments'; -import * as classify from './classify'; +import * as bscript from './script'; import { OPS as ops } from './script'; +import { Output, Transaction } from './transaction'; +import * as types from './types'; const typeforce = require('typeforce'); const SCRIPT_TYPES = classify.types; -type TxbSignatures = Array<Buffer> | Array<Buffer | undefined>; -type TxbPubkeys = Array<Buffer | undefined>; -type TxbWitness = Array<Buffer>; +type MaybeBuffer = Buffer | undefined; +type TxbSignatures = Buffer[] | MaybeBuffer[]; +type TxbPubkeys = MaybeBuffer[]; +type TxbWitness = Buffer[]; type TxbScriptType = string; type TxbScript = Buffer; @@ -58,24 +59,6 @@ function txIsTransaction(tx: Buffer | string | Transaction): tx is Transaction { } export class TransactionBuilder { - network: Network; - maximumFeeRate: number; - private __prevTxSet: { [index: string]: boolean }; - private __inputs: Array<TxbInput>; - private __tx: Transaction; - - constructor(network?: Network, maximumFeeRate?: number) { - this.__prevTxSet = {}; - this.network = network || networks.bitcoin; - - // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) - this.maximumFeeRate = maximumFeeRate || 2500; - - this.__inputs = []; - this.__tx = new Transaction(); - this.__tx.version = 2; - } - static fromTransaction( transaction: Transaction, network?: Network, @@ -88,7 +71,7 @@ export class TransactionBuilder { // Copy outputs (done first to avoid signature invalidation) transaction.outs.forEach(txOut => { - txb.addOutput(txOut.script, (<Output>txOut).value); + txb.addOutput(txOut.script, (txOut as Output).value); }); // Copy inputs @@ -101,19 +84,37 @@ export class TransactionBuilder { }); // fix some things not possible through the public API - txb.__inputs.forEach((input, i) => { + txb.__INPUTS.forEach((input, i) => { fixMultisigOrder(input, transaction, i); }); return txb; } + network: Network; + maximumFeeRate: number; + private __PREV_TX_SET: { [index: string]: boolean }; + private __INPUTS: TxbInput[]; + private __TX: Transaction; + + constructor(network?: Network, maximumFeeRate?: number) { + this.__PREV_TX_SET = {}; + this.network = network || networks.bitcoin; + + // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) + this.maximumFeeRate = maximumFeeRate || 2500; + + this.__INPUTS = []; + this.__TX = new Transaction(); + this.__TX.version = 2; + } + setLockTime(locktime: number): void { typeforce(types.UInt32, locktime); // if any signatures exist, throw if ( - this.__inputs.some(input => { + this.__INPUTS.some(input => { if (!input.signatures) return false; return input.signatures.some(s => s !== undefined); @@ -122,14 +123,14 @@ export class TransactionBuilder { throw new Error('No, this would invalidate signatures'); } - this.__tx.locktime = locktime; + this.__TX.locktime = locktime; } setVersion(version: number): void { typeforce(types.UInt32, version); // XXX: this might eventually become more complex depending on what the versions represent - this.__tx.version = version; + this.__TX.version = version; } addInput( @@ -142,7 +143,7 @@ export class TransactionBuilder { throw new Error('No, this would invalidate signatures'); } - let value: number | undefined = undefined; + let value: number | undefined; // is it a hex string? if (txIsString(txHash)) { @@ -153,18 +154,128 @@ export class TransactionBuilder { } else if (txIsTransaction(txHash)) { const txOut = txHash.outs[vout]; prevOutScript = txOut.script; - value = (<Output>txOut).value; + value = (txOut as Output).value; - txHash = <Buffer>txHash.getHash(false); + txHash = txHash.getHash(false) as Buffer; } return this.__addInputUnsafe(txHash, vout, { - sequence: sequence, - prevOutScript: prevOutScript, - value: value, + sequence, + prevOutScript, + value, }); } + addOutput(scriptPubKey: string | Buffer, value: number): number { + if (!this.__canModifyOutputs()) { + throw new Error('No, this would invalidate signatures'); + } + + // Attempt to get a script if it's a base58 or bech32 address string + if (typeof scriptPubKey === 'string') { + scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); + } + + return this.__TX.addOutput(scriptPubKey, value); + } + + build(): Transaction { + return this.__build(false); + } + + buildIncomplete(): Transaction { + return this.__build(true); + } + + sign( + vin: number, + keyPair: ECPairInterface, + redeemScript: Buffer, + hashType: number, + witnessValue: number, + witnessScript: Buffer, + ) { + // TODO: remove keyPair.network matching in 4.0.0 + if (keyPair.network && keyPair.network !== this.network) + throw new TypeError('Inconsistent network'); + if (!this.__INPUTS[vin]) throw new Error('No input at index: ' + vin); + + hashType = hashType || Transaction.SIGHASH_ALL; + if (this.__needsOutputs(hashType)) + throw new Error('Transaction needs outputs'); + + const input = this.__INPUTS[vin]; + + // if redeemScript was previously provided, enforce consistency + if ( + input.redeemScript !== undefined && + redeemScript && + !input.redeemScript.equals(redeemScript) + ) { + throw new Error('Inconsistent redeemScript'); + } + + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!(); + if (!canSign(input)) { + if (witnessValue !== undefined) { + if (input.value !== undefined && input.value !== witnessValue) + throw new Error('Input did not match witnessValue'); + typeforce(types.Satoshi, witnessValue); + input.value = witnessValue; + } + + if (!canSign(input)) { + const prepared = prepareInput( + input, + ourPubKey, + redeemScript, + witnessScript, + ); + + // updates inline + Object.assign(input, prepared); + } + + if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); + } + + // ready to sign + let signatureHash: Buffer; + if (input.hasWitness) { + signatureHash = this.__TX.hashForWitnessV0( + vin, + input.signScript as Buffer, + input.value as number, + hashType, + ); + } else { + signatureHash = this.__TX.hashForSignature( + vin, + input.signScript as Buffer, + hashType, + ); + } + + // enforce in order signing of public keys + const signed = input.pubkeys!.some((pubKey, i) => { + if (!ourPubKey.equals(pubKey!)) return false; + if (input.signatures![i]) throw new Error('Signature already exists'); + + // TODO: add tests + if (ourPubKey.length !== 33 && input.hasWitness) { + throw new Error( + 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', + ); + } + + const signature = keyPair.sign(signatureHash); + input.signatures![i] = bscript.signature.encode(signature, hashType); + return true; + }); + + if (!signed) throw new Error('Key pair cannot sign for this input'); + } + private __addInputUnsafe( txHash: Buffer, vout: number, @@ -175,10 +286,10 @@ export class TransactionBuilder { } const prevTxOut = txHash.toString('hex') + ':' + vout; - if (this.__prevTxSet[prevTxOut] !== undefined) + if (this.__PREV_TX_SET[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut); - let input = <TxbInput>{}; + let input: TxbInput = {}; // derive what we can from the scriptSig if (options.script !== undefined) { @@ -208,48 +319,27 @@ export class TransactionBuilder { input.prevOutType = prevOutType || classify.output(options.prevOutScript); } - const vin = this.__tx.addInput( + const vin = this.__TX.addInput( txHash, vout, options.sequence, options.scriptSig, ); - this.__inputs[vin] = input; - this.__prevTxSet[prevTxOut] = true; + this.__INPUTS[vin] = input; + this.__PREV_TX_SET[prevTxOut] = true; return vin; } - addOutput(scriptPubKey: string | Buffer, value: number): number { - if (!this.__canModifyOutputs()) { - throw new Error('No, this would invalidate signatures'); - } - - // Attempt to get a script if it's a base58 or bech32 address string - if (typeof scriptPubKey === 'string') { - scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); - } - - return this.__tx.addOutput(scriptPubKey, value); - } - - build(): Transaction { - return this.__build(false); - } - - buildIncomplete(): Transaction { - return this.__build(true); - } - private __build(allowIncomplete?: boolean): Transaction { if (!allowIncomplete) { - if (!this.__tx.ins.length) throw new Error('Transaction has no inputs'); - if (!this.__tx.outs.length) throw new Error('Transaction has no outputs'); + if (!this.__TX.ins.length) throw new Error('Transaction has no inputs'); + if (!this.__TX.outs.length) throw new Error('Transaction has no outputs'); } - const tx = this.__tx.clone(); + const tx = this.__TX.clone(); // create script signatures from inputs - this.__inputs.forEach((input, i) => { + this.__INPUTS.forEach((input, i) => { if (!input.prevOutType && !allowIncomplete) throw new Error('Transaction is not complete'); @@ -275,97 +365,8 @@ export class TransactionBuilder { return tx; } - sign( - vin: number, - keyPair: ECPairInterface, - redeemScript: Buffer, - hashType: number, - witnessValue: number, - witnessScript: Buffer, - ) { - // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== this.network) - throw new TypeError('Inconsistent network'); - if (!this.__inputs[vin]) throw new Error('No input at index: ' + vin); - - hashType = hashType || Transaction.SIGHASH_ALL; - if (this.__needsOutputs(hashType)) - throw new Error('Transaction needs outputs'); - - const input = this.__inputs[vin]; - - // if redeemScript was previously provided, enforce consistency - if ( - input.redeemScript !== undefined && - redeemScript && - !input.redeemScript.equals(redeemScript) - ) { - throw new Error('Inconsistent redeemScript'); - } - - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!(); - if (!canSign(input)) { - if (witnessValue !== undefined) { - if (input.value !== undefined && input.value !== witnessValue) - throw new Error("Input didn't match witnessValue"); - typeforce(types.Satoshi, witnessValue); - input.value = witnessValue; - } - - if (!canSign(input)) { - const prepared = prepareInput( - input, - ourPubKey, - redeemScript, - witnessScript, - ); - - // updates inline - Object.assign(input, prepared); - } - - if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); - } - - // ready to sign - let signatureHash: Buffer; - if (input.hasWitness) { - signatureHash = this.__tx.hashForWitnessV0( - vin, - <Buffer>input.signScript, - <number>input.value, - hashType, - ); - } else { - signatureHash = this.__tx.hashForSignature( - vin, - <Buffer>input.signScript, - hashType, - ); - } - - // enforce in order signing of public keys - const signed = input.pubkeys!.some((pubKey, i) => { - if (!ourPubKey.equals(pubKey!)) return false; - if (input.signatures![i]) throw new Error('Signature already exists'); - - // TODO: add tests - if (ourPubKey.length !== 33 && input.hasWitness) { - throw new Error( - 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', - ); - } - - const signature = keyPair.sign(signatureHash); - input.signatures![i] = bscript.signature.encode(signature, hashType); - return true; - }); - - if (!signed) throw new Error('Key pair cannot sign for this input'); - } - private __canModifyInputs(): boolean { - return this.__inputs.every(input => { + return this.__INPUTS.every(input => { if (!input.signatures) return true; return input.signatures.every(signature => { @@ -381,14 +382,14 @@ export class TransactionBuilder { private __needsOutputs(signingHashType: number): boolean { if (signingHashType === Transaction.SIGHASH_ALL) { - return this.__tx.outs.length === 0; + return this.__TX.outs.length === 0; } // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs // .build() will fail, but .buildIncomplete() is OK return ( - this.__tx.outs.length === 0 && - this.__inputs.some(input => { + this.__TX.outs.length === 0 && + this.__INPUTS.some(input => { if (!input.signatures) return false; return input.signatures.some(signature => { @@ -402,10 +403,10 @@ export class TransactionBuilder { } private __canModifyOutputs(): boolean { - const nInputs = this.__tx.ins.length; - const nOutputs = this.__tx.outs.length; + const nInputs = this.__TX.ins.length; + const nOutputs = this.__TX.outs.length; - return this.__inputs.every(input => { + return this.__INPUTS.every(input => { if (input.signatures === undefined) return true; return input.signatures.every(signature => { @@ -427,11 +428,14 @@ export class TransactionBuilder { private __overMaximumFees(bytes: number): boolean { // not all inputs will have .value defined - const incoming = this.__inputs.reduce((a, x) => a + (x.value! >>> 0), 0); + const incoming = this.__INPUTS.reduce((a, x) => a + (x.value! >>> 0), 0); // but all outputs do, and if we have any input value // we can immediately determine if the outputs are too small - const outgoing = this.__tx.outs.reduce((a, x) => a + (<Output>x).value, 0); + const outgoing = this.__TX.outs.reduce( + (a, x) => a + (x as Output).value, + 0, + ); const fee = incoming - outgoing; const feeRate = fee / bytes; @@ -441,7 +445,7 @@ export class TransactionBuilder { function expandInput( scriptSig: Buffer, - witnessStack: Array<Buffer>, + witnessStack: Buffer[], type?: string, scriptPubKey?: Buffer, ): TxbInput { @@ -502,8 +506,8 @@ function expandInput( return { prevOutType: SCRIPT_TYPES.P2MS, - pubkeys: pubkeys, - signatures: signatures, + pubkeys, + signatures, maxSignatures: m, }; } @@ -681,12 +685,12 @@ function prepareInput( witnessScript: Buffer, ): TxbInput { if (redeemScript && witnessScript) { - const p2wsh = <Payment>( - payments.p2wsh({ redeem: { output: witnessScript } }) - ); - const p2wshAlt = <Payment>payments.p2wsh({ output: redeemScript }); - const p2sh = <Payment>payments.p2sh({ redeem: { output: redeemScript } }); - const p2shAlt = <Payment>payments.p2sh({ redeem: p2wsh }); + const p2wsh = payments.p2wsh({ + redeem: { output: witnessScript }, + }) as Payment; + const p2wshAlt = payments.p2wsh({ output: redeemScript }) as Payment; + const p2sh = payments.p2sh({ redeem: { output: redeemScript } }) as Payment; + const p2shAlt = payments.p2sh({ redeem: p2wsh }) as Payment; // enforces P2SH(P2WSH(...)) if (!p2wsh.hash!.equals(p2wshAlt.hash!)) @@ -706,7 +710,7 @@ function prepareInput( expanded.signatures = input.signatures; } - let signScript = witnessScript; + const signScript = witnessScript; if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure'); @@ -731,12 +735,12 @@ function prepareInput( } if (redeemScript) { - const p2sh = <Payment>payments.p2sh({ redeem: { output: redeemScript } }); + const p2sh = payments.p2sh({ redeem: { output: redeemScript } }) as Payment; if (input.prevOutScript) { let p2shAlt; try { - p2shAlt = <Payment>payments.p2sh({ output: input.prevOutScript }); + p2shAlt = payments.p2sh({ output: input.prevOutScript }) as Payment; } catch (e) { throw new Error('PrevOutScript must be P2SH'); } @@ -799,7 +803,7 @@ function prepareInput( expanded.signatures = input.signatures; } - let signScript = witnessScript; + const signScript = witnessScript; if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2WSH(P2WPKH) is a consensus failure'); @@ -846,9 +850,8 @@ function prepareInput( let signScript = input.prevOutScript; if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = <Buffer>( - payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output - ); + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }) + .output as Buffer; } return { @@ -868,7 +871,7 @@ function prepareInput( const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output; return { prevOutType: SCRIPT_TYPES.P2PKH, - prevOutScript: prevOutScript, + prevOutScript, hasWitness: false, signScript: prevOutScript, @@ -884,8 +887,8 @@ function build( input: TxbInput, allowIncomplete?: boolean, ): Payment | undefined { - const pubkeys = <Array<Buffer>>(input.pubkeys || []); - let signatures = <Array<Buffer>>(input.signatures || []); + const pubkeys = (input.pubkeys || []) as Buffer[]; + let signatures = (input.signatures || []) as Buffer[]; switch (type) { case SCRIPT_TYPES.P2PKH: { diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts index 4be5968..b3dedb3 100644 --- a/types/transaction_builder.d.ts +++ b/types/transaction_builder.d.ts @@ -1,24 +1,24 @@ /// <reference types="node" /> +import { ECPairInterface } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; -import { ECPairInterface } from './ecpair'; export declare class TransactionBuilder { + static fromTransaction(transaction: Transaction, network?: Network): TransactionBuilder; network: Network; maximumFeeRate: number; - private __prevTxSet; - private __inputs; - private __tx; + private __PREV_TX_SET; + private __INPUTS; + private __TX; constructor(network?: Network, maximumFeeRate?: number); - static fromTransaction(transaction: Transaction, network?: Network): TransactionBuilder; setLockTime(locktime: number): void; setVersion(version: number): void; addInput(txHash: Buffer | string | Transaction, vout: number, sequence: number, prevOutScript: Buffer): number; - private __addInputUnsafe; addOutput(scriptPubKey: string | Buffer, value: number): number; build(): Transaction; buildIncomplete(): Transaction; - private __build; sign(vin: number, keyPair: ECPairInterface, redeemScript: Buffer, hashType: number, witnessValue: number, witnessScript: Buffer): void; + private __addInputUnsafe; + private __build; private __canModifyInputs; private __needsOutputs; private __canModifyOutputs; From 08c4d6ac7dae7a8d61ded59f64bab68ad7f39f92 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 14:16:45 +0900 Subject: [PATCH 253/568] Fix lint for templates --- src/templates/multisig/input.js | 2 +- src/templates/multisig/output.js | 4 ++-- src/templates/nulldata.js | 2 +- src/templates/pubkey/input.js | 5 +++-- src/templates/pubkey/output.js | 2 +- src/templates/pubkeyhash/input.js | 2 +- src/templates/pubkeyhash/output.js | 2 +- src/templates/scripthash/input.js | 2 +- src/templates/scripthash/output.js | 2 +- src/templates/witnesscommitment/output.js | 4 ++-- src/templates/witnesspubkeyhash/input.js | 2 +- src/templates/witnesspubkeyhash/output.js | 2 +- src/templates/witnessscripthash/input.js | 2 +- src/templates/witnessscripthash/output.js | 2 +- ts_src/templates/multisig/input.ts | 11 ++++++----- ts_src/templates/multisig/output.ts | 15 ++++++++------- ts_src/templates/nulldata.ts | 2 +- ts_src/templates/pubkey/input.ts | 10 ++++++---- ts_src/templates/pubkey/output.ts | 9 +++++---- ts_src/templates/pubkeyhash/input.ts | 11 ++++++----- ts_src/templates/pubkeyhash/output.ts | 2 +- ts_src/templates/scripthash/input.ts | 2 +- ts_src/templates/scripthash/output.ts | 2 +- ts_src/templates/witnesscommitment/output.ts | 6 +++--- ts_src/templates/witnesspubkeyhash/input.ts | 11 ++++++----- ts_src/templates/witnesspubkeyhash/output.ts | 2 +- ts_src/templates/witnessscripthash/input.ts | 7 ++----- ts_src/templates/witnessscripthash/output.ts | 2 +- types/templates/multisig/input.d.ts | 3 ++- types/templates/multisig/output.d.ts | 3 ++- types/templates/pubkey/input.d.ts | 3 ++- types/templates/pubkey/output.d.ts | 3 ++- types/templates/pubkeyhash/input.d.ts | 3 ++- types/templates/witnesspubkeyhash/input.d.ts | 3 ++- types/templates/witnessscripthash/input.d.ts | 2 +- 35 files changed, 79 insertions(+), 68 deletions(-) diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js index 24a9710..4b4f395 100644 --- a/src/templates/multisig/input.js +++ b/src/templates/multisig/input.js @@ -18,6 +18,6 @@ function check(script, allowIncomplete) { return chunks.slice(1).every(bscript.isCanonicalScriptSignature); } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'multisig input'; }; diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.js index 64d07bc..c79fe9b 100644 --- a/src/templates/multisig/output.js +++ b/src/templates/multisig/output.js @@ -2,8 +2,8 @@ // m [pubKeys ...] n OP_CHECKMULTISIG Object.defineProperty(exports, "__esModule", { value: true }); const bscript = require("../../script"); -const types = require("../../types"); const script_1 = require("../../script"); +const types = require("../../types"); const OP_INT_BASE = script_1.OPS.OP_RESERVED; // OP_1 - 1 function check(script, allowIncomplete) { const chunks = bscript.decompile(script); @@ -31,6 +31,6 @@ function check(script, allowIncomplete) { return keys.every(bscript.isCanonicalPubKey); } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'multi-sig output'; }; diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js index b5ffdce..29bee7a 100644 --- a/src/templates/nulldata.js +++ b/src/templates/nulldata.js @@ -8,7 +8,7 @@ function check(script) { return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'null data output'; }; const output = { check }; diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js index 10d3c07..479fdc5 100644 --- a/src/templates/pubkey/input.js +++ b/src/templates/pubkey/input.js @@ -4,9 +4,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); const bscript = require("../../script"); function check(script) { const chunks = bscript.decompile(script); - return (chunks.length === 1 && bscript.isCanonicalScriptSignature(chunks[0])); + return (chunks.length === 1 && + bscript.isCanonicalScriptSignature(chunks[0])); } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'pubKey input'; }; diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.js index e2f87c2..1f17990 100644 --- a/src/templates/pubkey/output.js +++ b/src/templates/pubkey/output.js @@ -10,6 +10,6 @@ function check(script) { chunks[1] === script_1.OPS.OP_CHECKSIG); } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'pubKey output'; }; diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js index f03d391..7de30ec 100644 --- a/src/templates/pubkeyhash/input.js +++ b/src/templates/pubkeyhash/input.js @@ -9,6 +9,6 @@ function check(script) { bscript.isCanonicalPubKey(chunks[1])); } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'pubKeyHash input'; }; diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.js index 222244b..5ee692b 100644 --- a/src/templates/pubkeyhash/output.js +++ b/src/templates/pubkeyhash/output.js @@ -13,6 +13,6 @@ function check(script) { buffer[24] === script_1.OPS.OP_CHECKSIG); } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'pubKeyHash output'; }; diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js index 5d2b576..488b931 100644 --- a/src/templates/scripthash/input.js +++ b/src/templates/scripthash/input.js @@ -39,6 +39,6 @@ function check(script, allowIncomplete) { return false; } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'scriptHash input'; }; diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.js index 5fd2f65..bf1246a 100644 --- a/src/templates/scripthash/output.js +++ b/src/templates/scripthash/output.js @@ -11,6 +11,6 @@ function check(script) { buffer[22] === script_1.OPS.OP_EQUAL); } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'scriptHash output'; }; diff --git a/src/templates/witnesscommitment/output.js b/src/templates/witnesscommitment/output.js index 38f622a..f4d6af0 100644 --- a/src/templates/witnesscommitment/output.js +++ b/src/templates/witnesscommitment/output.js @@ -2,9 +2,9 @@ // OP_RETURN {aa21a9ed} {commitment} Object.defineProperty(exports, "__esModule", { value: true }); const bscript = require("../../script"); +const script_1 = require("../../script"); const types = require("../../types"); const typeforce = require('typeforce'); -const script_1 = require("../../script"); const HEADER = Buffer.from('aa21a9ed', 'hex'); function check(script) { const buffer = bscript.compile(script); @@ -14,7 +14,7 @@ function check(script) { buffer.slice(2, 6).equals(HEADER)); } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'Witness commitment output'; }; function encode(commitment) { diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.js index 4585623..3d589e9 100644 --- a/src/templates/witnesspubkeyhash/input.js +++ b/src/templates/witnesspubkeyhash/input.js @@ -12,6 +12,6 @@ function check(script) { isCompressedCanonicalPubKey(chunks[1])); } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'witnessPubKeyHash input'; }; diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.js index 9c508a0..69aab11 100644 --- a/src/templates/witnesspubkeyhash/output.js +++ b/src/templates/witnesspubkeyhash/output.js @@ -8,6 +8,6 @@ function check(script) { return buffer.length === 22 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x14; } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'Witness pubKeyHash output'; }; diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js index ca8c8b6..3f5c002 100644 --- a/src/templates/witnessscripthash/input.js +++ b/src/templates/witnessscripthash/input.js @@ -31,6 +31,6 @@ function check(chunks, allowIncomplete) { return false; } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'witnessScriptHash input'; }; diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.js index f283b86..a6d4d95 100644 --- a/src/templates/witnessscripthash/output.js +++ b/src/templates/witnessscripthash/output.js @@ -8,6 +8,6 @@ function check(script) { return buffer.length === 34 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x20; } exports.check = check; -check.toJSON = function () { +check.toJSON = () => { return 'Witness scriptHash output'; }; diff --git a/ts_src/templates/multisig/input.ts b/ts_src/templates/multisig/input.ts index 222ff10..57b73d2 100644 --- a/ts_src/templates/multisig/input.ts +++ b/ts_src/templates/multisig/input.ts @@ -1,19 +1,20 @@ // OP_0 [signatures ...] +import { Stack } from '../../payments'; import * as bscript from '../../script'; import { OPS } from '../../script'; function partialSignature(value: number | Buffer): boolean { return ( - value === OPS.OP_0 || bscript.isCanonicalScriptSignature(<Buffer>value) + value === OPS.OP_0 || bscript.isCanonicalScriptSignature(value as Buffer) ); } export function check( - script: Buffer | Array<number | Buffer>, + script: Buffer | Stack, allowIncomplete?: boolean, ): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script); + const chunks = bscript.decompile(script) as Stack; if (chunks.length < 2) return false; if (chunks[0] !== OPS.OP_0) return false; @@ -21,10 +22,10 @@ export function check( return chunks.slice(1).every(partialSignature); } - return (<Array<Buffer>>chunks.slice(1)).every( + return (chunks.slice(1) as Buffer[]).every( bscript.isCanonicalScriptSignature, ); } -check.toJSON = function() { +check.toJSON = () => { return 'multisig input'; }; diff --git a/ts_src/templates/multisig/output.ts b/ts_src/templates/multisig/output.ts index 040649b..3ee0820 100644 --- a/ts_src/templates/multisig/output.ts +++ b/ts_src/templates/multisig/output.ts @@ -1,22 +1,23 @@ // m [pubKeys ...] n OP_CHECKMULTISIG +import { Stack } from '../../payments'; import * as bscript from '../../script'; -import * as types from '../../types'; import { OPS } from '../../script'; +import * as types from '../../types'; const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 export function check( - script: Buffer | Array<number | Buffer>, + script: Buffer | Stack, allowIncomplete?: boolean, ): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script); + const chunks = bscript.decompile(script) as Stack; if (chunks.length < 4) return false; if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false; if (!types.Number(chunks[0])) return false; if (!types.Number(chunks[chunks.length - 2])) return false; - const m = <number>chunks[0] - OP_INT_BASE; - const n = <number>chunks[chunks.length - 2] - OP_INT_BASE; + const m = (chunks[0] as number) - OP_INT_BASE; + const n = (chunks[chunks.length - 2] as number) - OP_INT_BASE; if (m <= 0) return false; if (n > 16) return false; @@ -24,9 +25,9 @@ export function check( if (n !== chunks.length - 3) return false; if (allowIncomplete) return true; - const keys = <Array<Buffer>>chunks.slice(1, -2); + const keys = chunks.slice(1, -2) as Buffer[]; return keys.every(bscript.isCanonicalPubKey); } -check.toJSON = function() { +check.toJSON = () => { return 'multi-sig output'; }; diff --git a/ts_src/templates/nulldata.ts b/ts_src/templates/nulldata.ts index f0694a3..bafe4a4 100644 --- a/ts_src/templates/nulldata.ts +++ b/ts_src/templates/nulldata.ts @@ -7,7 +7,7 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; } -check.toJSON = function() { +check.toJSON = () => { return 'null data output'; }; diff --git a/ts_src/templates/pubkey/input.ts b/ts_src/templates/pubkey/input.ts index e9ea1be..7e07a94 100644 --- a/ts_src/templates/pubkey/input.ts +++ b/ts_src/templates/pubkey/input.ts @@ -1,14 +1,16 @@ // {signature} +import { Stack } from '../../payments'; import * as bscript from '../../script'; -export function check(script: Buffer | Array<number | Buffer>): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script); +export function check(script: Buffer | Stack): boolean { + const chunks = bscript.decompile(script) as Stack; return ( - chunks.length === 1 && bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) + chunks.length === 1 && + bscript.isCanonicalScriptSignature(chunks[0] as Buffer) ); } -check.toJSON = function() { +check.toJSON = () => { return 'pubKey input'; }; diff --git a/ts_src/templates/pubkey/output.ts b/ts_src/templates/pubkey/output.ts index 8378870..d57422d 100644 --- a/ts_src/templates/pubkey/output.ts +++ b/ts_src/templates/pubkey/output.ts @@ -1,17 +1,18 @@ // {pubKey} OP_CHECKSIG +import { Stack } from '../../payments'; import * as bscript from '../../script'; import { OPS } from '../../script'; -export function check(script: Buffer | Array<number | Buffer>): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script); +export function check(script: Buffer | Stack): boolean { + const chunks = bscript.decompile(script) as Stack; return ( chunks.length === 2 && - bscript.isCanonicalPubKey(<Buffer>chunks[0]) && + bscript.isCanonicalPubKey(chunks[0] as Buffer) && chunks[1] === OPS.OP_CHECKSIG ); } -check.toJSON = function() { +check.toJSON = () => { return 'pubKey output'; }; diff --git a/ts_src/templates/pubkeyhash/input.ts b/ts_src/templates/pubkeyhash/input.ts index c091764..83da475 100644 --- a/ts_src/templates/pubkeyhash/input.ts +++ b/ts_src/templates/pubkeyhash/input.ts @@ -1,16 +1,17 @@ // {signature} {pubKey} +import { Stack } from '../../payments'; import * as bscript from '../../script'; -export function check(script: Buffer | Array<number | Buffer>): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script); +export function check(script: Buffer | Stack): boolean { + const chunks = bscript.decompile(script) as Stack; return ( chunks.length === 2 && - bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) && - bscript.isCanonicalPubKey(<Buffer>chunks[1]) + bscript.isCanonicalScriptSignature(chunks[0] as Buffer) && + bscript.isCanonicalPubKey(chunks[1] as Buffer) ); } -check.toJSON = function() { +check.toJSON = () => { return 'pubKeyHash input'; }; diff --git a/ts_src/templates/pubkeyhash/output.ts b/ts_src/templates/pubkeyhash/output.ts index a5c5c9b..37070a3 100644 --- a/ts_src/templates/pubkeyhash/output.ts +++ b/ts_src/templates/pubkeyhash/output.ts @@ -15,6 +15,6 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { buffer[24] === OPS.OP_CHECKSIG ); } -check.toJSON = function() { +check.toJSON = () => { return 'pubKeyHash output'; }; diff --git a/ts_src/templates/scripthash/input.ts b/ts_src/templates/scripthash/input.ts index 0b86d63..1e3f97d 100644 --- a/ts_src/templates/scripthash/input.ts +++ b/ts_src/templates/scripthash/input.ts @@ -56,6 +56,6 @@ export function check( return false; } -check.toJSON = function() { +check.toJSON = () => { return 'scriptHash input'; }; diff --git a/ts_src/templates/scripthash/output.ts b/ts_src/templates/scripthash/output.ts index 6ff138a..7eac30d 100644 --- a/ts_src/templates/scripthash/output.ts +++ b/ts_src/templates/scripthash/output.ts @@ -13,6 +13,6 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { buffer[22] === OPS.OP_EQUAL ); } -check.toJSON = function() { +check.toJSON = () => { return 'scriptHash output'; }; diff --git a/ts_src/templates/witnesscommitment/output.ts b/ts_src/templates/witnesscommitment/output.ts index 29beb39..9439e4c 100644 --- a/ts_src/templates/witnesscommitment/output.ts +++ b/ts_src/templates/witnesscommitment/output.ts @@ -1,10 +1,10 @@ // OP_RETURN {aa21a9ed} {commitment} import * as bscript from '../../script'; +import { OPS } from '../../script'; import * as types from '../../types'; const typeforce = require('typeforce'); -import { OPS } from '../../script'; const HEADER: Buffer = Buffer.from('aa21a9ed', 'hex'); @@ -19,7 +19,7 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { ); } -check.toJSON = function() { +check.toJSON = () => { return 'Witness commitment output'; }; @@ -36,5 +36,5 @@ export function encode(commitment: Buffer): Buffer { export function decode(buffer: Buffer): Buffer { typeforce(check, buffer); - return (<Buffer>bscript.decompile(buffer)![1]).slice(4, 36); + return (bscript.decompile(buffer)![1] as Buffer).slice(4, 36); } diff --git a/ts_src/templates/witnesspubkeyhash/input.ts b/ts_src/templates/witnesspubkeyhash/input.ts index 22fc2cf..aa3bef8 100644 --- a/ts_src/templates/witnesspubkeyhash/input.ts +++ b/ts_src/templates/witnesspubkeyhash/input.ts @@ -1,20 +1,21 @@ // {signature} {pubKey} +import { Stack } from '../../payments'; import * as bscript from '../../script'; function isCompressedCanonicalPubKey(pubKey: Buffer): boolean { return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; } -export function check(script: Buffer | Array<number | Buffer>): boolean { - const chunks = <Array<number | Buffer>>bscript.decompile(script); +export function check(script: Buffer | Stack): boolean { + const chunks = bscript.decompile(script) as Stack; return ( chunks.length === 2 && - bscript.isCanonicalScriptSignature(<Buffer>chunks[0]) && - isCompressedCanonicalPubKey(<Buffer>chunks[1]) + bscript.isCanonicalScriptSignature(chunks[0] as Buffer) && + isCompressedCanonicalPubKey(chunks[1] as Buffer) ); } -check.toJSON = function() { +check.toJSON = () => { return 'witnessPubKeyHash input'; }; diff --git a/ts_src/templates/witnesspubkeyhash/output.ts b/ts_src/templates/witnesspubkeyhash/output.ts index 09c49d0..0e9432c 100644 --- a/ts_src/templates/witnesspubkeyhash/output.ts +++ b/ts_src/templates/witnesspubkeyhash/output.ts @@ -8,6 +8,6 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { return buffer.length === 22 && buffer[0] === OPS.OP_0 && buffer[1] === 0x14; } -check.toJSON = function() { +check.toJSON = () => { return 'Witness pubKeyHash output'; }; diff --git a/ts_src/templates/witnessscripthash/input.ts b/ts_src/templates/witnessscripthash/input.ts index 6cf35bf..42a13ac 100644 --- a/ts_src/templates/witnessscripthash/input.ts +++ b/ts_src/templates/witnessscripthash/input.ts @@ -7,10 +7,7 @@ import * as p2ms from '../multisig'; import * as p2pk from '../pubkey'; import * as p2pkh from '../pubkeyhash'; -export function check( - chunks: Array<Buffer>, - allowIncomplete?: boolean, -): boolean { +export function check(chunks: Buffer[], allowIncomplete?: boolean): boolean { typeforce(typeforce.Array, chunks); if (chunks.length < 1) return false; @@ -45,6 +42,6 @@ export function check( return false; } -check.toJSON = function() { +check.toJSON = () => { return 'witnessScriptHash input'; }; diff --git a/ts_src/templates/witnessscripthash/output.ts b/ts_src/templates/witnessscripthash/output.ts index 430cc4f..85034d1 100644 --- a/ts_src/templates/witnessscripthash/output.ts +++ b/ts_src/templates/witnessscripthash/output.ts @@ -8,6 +8,6 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { return buffer.length === 34 && buffer[0] === OPS.OP_0 && buffer[1] === 0x20; } -check.toJSON = function() { +check.toJSON = () => { return 'Witness scriptHash output'; }; diff --git a/types/templates/multisig/input.d.ts b/types/templates/multisig/input.d.ts index a04d03c..a207dd6 100644 --- a/types/templates/multisig/input.d.ts +++ b/types/templates/multisig/input.d.ts @@ -1,5 +1,6 @@ /// <reference types="node" /> -export declare function check(script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean; +import { Stack } from '../../payments'; +export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean; export declare namespace check { var toJSON: () => string; } diff --git a/types/templates/multisig/output.d.ts b/types/templates/multisig/output.d.ts index a04d03c..a207dd6 100644 --- a/types/templates/multisig/output.d.ts +++ b/types/templates/multisig/output.d.ts @@ -1,5 +1,6 @@ /// <reference types="node" /> -export declare function check(script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean; +import { Stack } from '../../payments'; +export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean; export declare namespace check { var toJSON: () => string; } diff --git a/types/templates/pubkey/input.d.ts b/types/templates/pubkey/input.d.ts index 091758f..c4ffeab 100644 --- a/types/templates/pubkey/input.d.ts +++ b/types/templates/pubkey/input.d.ts @@ -1,5 +1,6 @@ /// <reference types="node" /> -export declare function check(script: Buffer | Array<number | Buffer>): boolean; +import { Stack } from '../../payments'; +export declare function check(script: Buffer | Stack): boolean; export declare namespace check { var toJSON: () => string; } diff --git a/types/templates/pubkey/output.d.ts b/types/templates/pubkey/output.d.ts index 091758f..c4ffeab 100644 --- a/types/templates/pubkey/output.d.ts +++ b/types/templates/pubkey/output.d.ts @@ -1,5 +1,6 @@ /// <reference types="node" /> -export declare function check(script: Buffer | Array<number | Buffer>): boolean; +import { Stack } from '../../payments'; +export declare function check(script: Buffer | Stack): boolean; export declare namespace check { var toJSON: () => string; } diff --git a/types/templates/pubkeyhash/input.d.ts b/types/templates/pubkeyhash/input.d.ts index 091758f..c4ffeab 100644 --- a/types/templates/pubkeyhash/input.d.ts +++ b/types/templates/pubkeyhash/input.d.ts @@ -1,5 +1,6 @@ /// <reference types="node" /> -export declare function check(script: Buffer | Array<number | Buffer>): boolean; +import { Stack } from '../../payments'; +export declare function check(script: Buffer | Stack): boolean; export declare namespace check { var toJSON: () => string; } diff --git a/types/templates/witnesspubkeyhash/input.d.ts b/types/templates/witnesspubkeyhash/input.d.ts index 091758f..c4ffeab 100644 --- a/types/templates/witnesspubkeyhash/input.d.ts +++ b/types/templates/witnesspubkeyhash/input.d.ts @@ -1,5 +1,6 @@ /// <reference types="node" /> -export declare function check(script: Buffer | Array<number | Buffer>): boolean; +import { Stack } from '../../payments'; +export declare function check(script: Buffer | Stack): boolean; export declare namespace check { var toJSON: () => string; } diff --git a/types/templates/witnessscripthash/input.d.ts b/types/templates/witnessscripthash/input.d.ts index 7786731..b2a6e8a 100644 --- a/types/templates/witnessscripthash/input.d.ts +++ b/types/templates/witnessscripthash/input.d.ts @@ -1,5 +1,5 @@ /// <reference types="node" /> -export declare function check(chunks: Array<Buffer>, allowIncomplete?: boolean): boolean; +export declare function check(chunks: Buffer[], allowIncomplete?: boolean): boolean; export declare namespace check { var toJSON: () => string; } From 7fb859b1f70f0aab4fe68fe60f85f4aa62689637 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 14:21:11 +0900 Subject: [PATCH 254/568] Fix lint types.ts --- src/types.js | 8 ++++---- ts_src/types.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/types.js b/src/types.js index 76b98cf..dbe7ca5 100644 --- a/src/types.js +++ b/src/types.js @@ -10,7 +10,7 @@ function BIP32Path(value) { return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); } exports.BIP32Path = BIP32Path; -BIP32Path.toJSON = function () { +BIP32Path.toJSON = () => { return 'BIP32 derivation path'; }; const SATOSHI_MAX = 21 * 1e14; @@ -34,10 +34,10 @@ exports.Network = typeforce.compile({ exports.Buffer256bit = typeforce.BufferN(32); exports.Hash160bit = typeforce.BufferN(20); exports.Hash256bit = typeforce.BufferN(32); -exports.Number = typeforce.Number; +exports.Number = typeforce.Number; // tslint:disable-line variable-name exports.Array = typeforce.Array; -exports.Boolean = typeforce.Boolean; -exports.String = typeforce.String; +exports.Boolean = typeforce.Boolean; // tslint:disable-line variable-name +exports.String = typeforce.String; // tslint:disable-line variable-name exports.Buffer = typeforce.Buffer; exports.Hex = typeforce.Hex; exports.maybe = typeforce.maybe; diff --git a/ts_src/types.ts b/ts_src/types.ts index 42ddb40..033e62a 100644 --- a/ts_src/types.ts +++ b/ts_src/types.ts @@ -8,7 +8,7 @@ export function UInt31(value: number): boolean { export function BIP32Path(value: string): boolean { return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); } -BIP32Path.toJSON = function() { +BIP32Path.toJSON = () => { return 'BIP32 derivation path'; }; @@ -35,10 +35,10 @@ export const Network = typeforce.compile({ export const Buffer256bit = typeforce.BufferN(32); export const Hash160bit = typeforce.BufferN(20); export const Hash256bit = typeforce.BufferN(32); -export const Number = typeforce.Number; +export const Number = typeforce.Number; // tslint:disable-line variable-name export const Array = typeforce.Array; -export const Boolean = typeforce.Boolean; -export const String = typeforce.String; +export const Boolean = typeforce.Boolean; // tslint:disable-line variable-name +export const String = typeforce.String; // tslint:disable-line variable-name export const Buffer = typeforce.Buffer; export const Hex = typeforce.Hex; export const maybe = typeforce.maybe; From cb21cdb74c398a95b183cc2ed9abb3ffc17c0ac3 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 15:54:58 +0900 Subject: [PATCH 255/568] Specify version --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 898fac8..ae28889 100644 --- a/package.json +++ b/package.json @@ -69,9 +69,9 @@ "minimaldata": "^1.0.2", "mocha": "^5.2.0", "nyc": "^11.8.0", - "prettier": "^1.16.4", + "prettier": "1.16.4", "proxyquire": "^2.0.1", - "tslint": "^5.13.1", + "tslint": "5.13.1", "typescript": "3.2.2" }, "license": "MIT" From 64aa7bacc3fc237cf4c0cb62c336bdfd063d123c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 16:43:12 +0900 Subject: [PATCH 256/568] Add TypeScript section to CONTRIBUTING --- CONTRIBUTING.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index efc9cae..2c72633 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,22 @@ The length of time required for peer review is unpredictable and will vary from Refer to the [Git manual](https://git-scm.com/doc) for any information about `git`. +## Regarding TypeScript +This library is written in TypeScript with tslint, prettier, and the tsc transpiler. These tools will help during testing to notice improper logic before committing and sending a pull request. + +Some rules regarding TypeScript: + +* Modify the typescript source code in an IDE that will give you warnings for transpile/lint errors. +* Once you are done with the modifications, run `npm run format` then `npm test` +* Running the tests will transpile the ts files into js and d.ts files. +* Use `git diff` or other tools to verify that the ts and js are changing the same parts. +* Commit all changes to ts, js, and d.ts files. +* Add tests where necessary. +* Submit your pull request. + +Using TypeScript is for preventing bugs while writing code, as well as automatically generating type definitions. However, the JS file diffs must be verified, and any unverified JS will not be published to npm. + + ## We adhere to Bitcoin-Core policy Bitcoin script payment/script templates are based on community consensus, but typically adhere to bitcoin-core node policy by default. From 32721c723e232936676121052fe26d02a6519bf9 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 7 Mar 2019 16:46:17 +0900 Subject: [PATCH 257/568] Modify typescript portion of README --- README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/README.md b/README.md index b8f70f2..458693f 100644 --- a/README.md +++ b/README.md @@ -78,16 +78,7 @@ If you're familiar with how to use browserify, ignore this and carry on, otherwi **WARNING**: iOS devices have [problems](https://github.com/feross/buffer/issues/136), use atleast [buffer@5.0.5](https://github.com/feross/buffer/pull/155) or greater, and enforce the test suites (for `Buffer`, and any other dependency) pass before use. ### Typescript or VSCode users -Type declarations for Typescript [are available](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/0897921174860ec3d5318992d2323b3ae8100a68/types/bitcoinjs-lib) for version `^3.0.0` of the library. - -``` bash -npm install @types/bitcoinjs-lib -``` - -For VSCode (and other editors), it is advised to install the type declarations, as Intellisense uses that information to help you code (autocompletion, static analysis). - -**WARNING**: These Typescript definitions are not maintained by the maintainers of this repository, and are instead maintained at [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). -Please report any issues or problems there. +Type declarations for Typescript are included in this library. Normal installation should include all the needed type information. ### Flow From 0426c6638971dbb4748e948342416fb7dafedf0d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 14 Mar 2019 17:32:45 +0900 Subject: [PATCH 258/568] Remove prepare script and checkMerkleRoot method on Block --- package.json | 1 - src/block.js | 5 ----- ts_src/block.ts | 8 -------- types/block.d.ts | 1 - 4 files changed, 15 deletions(-) diff --git a/package.json b/package.json index ae28889..e62819a 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", "nobuild:integration": "mocha --timeout 50000 test/integration/", "nobuild:unit": "mocha", - "prepare": "npm run build", "prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore", "test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" diff --git a/src/block.js b/src/block.js index 9419989..913ad9a 100644 --- a/src/block.js +++ b/src/block.js @@ -199,11 +199,6 @@ class Block { return (this.__checkMerkleRoot() && (hasWitnessCommit ? this.__checkWitnessCommit() : true)); } - checkMerkleRoot() { - console.warn('Deprecation Warning: Block method checkMerkleRoot will be ' + - 'deprecated in v5. Please use checkTxRoots instead.'); - return this.checkTxRoots(); - } checkProofOfWork() { const hash = bufferutils_1.reverseBuffer(this.getHash()); const target = Block.calculateTarget(this.bits); diff --git a/ts_src/block.ts b/ts_src/block.ts index 888337d..9a9bd45 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -269,14 +269,6 @@ export class Block { ); } - checkMerkleRoot(): boolean { - console.warn( - 'Deprecation Warning: Block method checkMerkleRoot will be ' + - 'deprecated in v5. Please use checkTxRoots instead.', - ); - return this.checkTxRoots(); - } - checkProofOfWork(): boolean { const hash: Buffer = reverseBuffer(this.getHash()); const target = Block.calculateTarget(this.bits); diff --git a/types/block.d.ts b/types/block.d.ts index 6f2d5b9..6b6596c 100644 --- a/types/block.d.ts +++ b/types/block.d.ts @@ -24,7 +24,6 @@ export declare class Block { toBuffer(headersOnly: boolean): Buffer; toHex(headersOnly: boolean): string; checkTxRoots(): boolean; - checkMerkleRoot(): boolean; checkProofOfWork(): boolean; private __checkMerkleRoot; private __checkWitnessCommit; From fabd1d6c9d78046bdeb27da52ef7340ea6f24db8 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 19 Mar 2019 12:57:27 +0900 Subject: [PATCH 259/568] Update bip32 and nyc to latest --- package-lock.json | 2431 ++++++++++----------------------------------- package.json | 4 +- 2 files changed, 514 insertions(+), 1921 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9de2ff5..3b11623 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,138 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/generator": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.4.tgz", + "integrity": "sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg==", + "dev": true, + "requires": { + "@babel/types": "^7.3.4", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@babel/parser": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.4.tgz", + "integrity": "sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==", + "dev": true + }, + "@babel/template": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2" + } + }, + "@babel/traverse": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.3.4.tgz", + "integrity": "sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.3.4", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.3.4", + "@babel/types": "^7.3.4", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.11" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.4.tgz", + "integrity": "sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + } + }, "@types/node": { "version": "10.12.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", @@ -90,9 +222,11 @@ } }, "bip32": { - "version": "git+https://github.com/junderw/bip32.git#0df550ad047210fe5cfcc189bb78db645f2f1a89", - "from": "git+https://github.com/junderw/bip32.git#typeScript", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.0.tgz", + "integrity": "sha512-dYuSVcH43KXLsmiOlud62m5ZP3KG8UBUAbP+wMq8VKuhPvxOOM8VOXRs6ppcJB4iUJtYjmg7VuzTlr8waMusfg==", "requires": { + "@types/node": "10.12.18", "bs58check": "^2.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", @@ -363,6 +497,12 @@ "path-is-absolute": "^1.0.0" } }, + "globals": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "dev": true + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -445,6 +585,27 @@ "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", "dev": true }, + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", + "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "dev": true, + "requires": { + "@babel/generator": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "istanbul-lib-coverage": "^2.0.3", + "semver": "^5.5.0" + } + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -461,6 +622,18 @@ "esprima": "^4.0.0" } }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -563,72 +736,48 @@ "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" }, "nyc": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.9.0.tgz", - "integrity": "sha512-w8OdJAhXL5izerzZMdqzYKMj/pgHJyY3qEPYBjLLxrhcVoHEY9pU5ENIiZyCgG9OR7x3VcUMoD40o6PtVpfR4g==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-13.3.0.tgz", + "integrity": "sha512-P+FwIuro2aFG6B0Esd9ZDWUd51uZrAEoGutqZxzrVmYl3qSfkLgcQpBPBjtDFsUQLFY1dvTQJPOyeqr8S9GF8w==", "dev": true, "requires": { "archy": "^1.0.0", "arrify": "^1.0.1", - "caching-transform": "^1.0.0", - "convert-source-map": "^1.5.1", - "debug-log": "^1.0.1", - "default-require-extensions": "^1.0.0", - "find-cache-dir": "^0.1.1", - "find-up": "^2.1.0", - "foreground-child": "^1.5.3", - "glob": "^7.0.6", - "istanbul-lib-coverage": "^1.1.2", - "istanbul-lib-hook": "^1.1.0", - "istanbul-lib-instrument": "^1.10.0", - "istanbul-lib-report": "^1.1.3", - "istanbul-lib-source-maps": "^1.2.3", - "istanbul-reports": "^1.4.0", - "md5-hex": "^1.2.0", + "caching-transform": "^3.0.1", + "convert-source-map": "^1.6.0", + "find-cache-dir": "^2.0.0", + "find-up": "^3.0.0", + "foreground-child": "^1.5.6", + "glob": "^7.1.3", + "istanbul-lib-coverage": "^2.0.3", + "istanbul-lib-hook": "^2.0.3", + "istanbul-lib-instrument": "^3.1.0", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.2", + "istanbul-reports": "^2.1.1", + "make-dir": "^1.3.0", "merge-source-map": "^1.1.0", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.0", - "resolve-from": "^2.0.0", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.1", + "resolve-from": "^4.0.0", + "rimraf": "^2.6.3", + "signal-exit": "^3.0.2", "spawn-wrap": "^1.4.2", - "test-exclude": "^4.2.0", - "yargs": "11.1.0", - "yargs-parser": "^8.0.0" + "test-exclude": "^5.1.0", + "uuid": "^3.3.2", + "yargs": "^12.0.5", + "yargs-parser": "^11.1.1" }, "dependencies": { - "align-text": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, - "amdefine": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", + "version": "3.0.0", "bundled": true, "dev": true }, "append-transform": { - "version": "0.4.0", + "version": "1.0.0", "bundled": true, "dev": true, "requires": { - "default-require-extensions": "^1.0.0" + "default-require-extensions": "^2.0.0" } }, "archy": { @@ -636,197 +785,24 @@ "bundled": true, "dev": true }, - "arr-diff": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "bundled": true, - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "bundled": true, - "dev": true - }, "arrify": { "version": "1.0.1", "bundled": true, "dev": true }, - "assign-symbols": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "async": { - "version": "1.5.2", - "bundled": true, - "dev": true - }, - "atob": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "babel-code-frame": { - "version": "6.26.0", + "version": "2.6.2", "bundled": true, "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "lodash": "^4.17.11" } }, - "babel-generator": { - "version": "6.26.1", - "bundled": true, - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "bundled": true, - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "bundled": true, - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "babel-template": { - "version": "6.26.0", - "bundled": true, - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "bundled": true, - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "babel-types": { - "version": "6.26.0", - "bundled": true, - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "bundled": true, - "dev": true - }, "balanced-match": { "version": "1.0.0", "bundled": true, "dev": true }, - "base": { - "version": "0.11.2", - "bundled": true, - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, "brace-expansion": { "version": "1.1.11", "bundled": true, @@ -836,142 +812,30 @@ "concat-map": "0.0.1" } }, - "braces": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "builtin-modules": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "cache-base": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, "caching-transform": { - "version": "1.0.1", + "version": "3.0.1", "bundled": true, "dev": true, "requires": { - "md5-hex": "^1.2.0", - "mkdirp": "^0.5.1", - "write-file-atomic": "^1.1.4" + "hasha": "^3.0.0", + "make-dir": "^1.3.0", + "package-hash": "^3.0.0", + "write-file-atomic": "^2.3.0" } }, "camelcase": { - "version": "1.2.1", + "version": "5.0.0", "bundled": true, - "dev": true, - "optional": true - }, - "center-align": { - "version": "0.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, - "chalk": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "class-utils": { - "version": "0.3.6", - "bundled": true, - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } + "dev": true }, "cliui": { - "version": "2.1.0", + "version": "4.1.0", "bundled": true, "dev": true, - "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "bundled": true, - "dev": true, - "optional": true - } + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } }, "code-point-at": { @@ -979,44 +843,29 @@ "bundled": true, "dev": true }, - "collection-visit": { - "version": "1.0.0", + "commander": { + "version": "2.17.1", "bundled": true, "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } + "optional": true }, "commondir": { "version": "1.0.1", "bundled": true, "dev": true }, - "component-emitter": { - "version": "1.2.1", - "bundled": true, - "dev": true - }, "concat-map": { "version": "0.0.1", "bundled": true, "dev": true }, "convert-source-map": { - "version": "1.5.1", + "version": "1.6.0", "bundled": true, - "dev": true - }, - "copy-descriptor": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "core-js": { - "version": "2.5.6", - "bundled": true, - "dev": true + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } }, "cross-spawn": { "version": "4.0.2", @@ -1028,116 +877,54 @@ } }, "debug": { - "version": "2.6.9", + "version": "4.1.1", "bundled": true, "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "debug-log": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, "decamelize": { "version": "1.2.0", "bundled": true, "dev": true }, - "decode-uri-component": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, "default-require-extensions": { - "version": "1.0.0", + "version": "2.0.0", "bundled": true, "dev": true, "requires": { - "strip-bom": "^2.0.0" + "strip-bom": "^3.0.0" } }, - "define-property": { - "version": "2.0.2", + "end-of-stream": { + "version": "1.4.1", "bundled": true, "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, - "detect-indent": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "repeating": "^2.0.0" + "once": "^1.4.0" } }, "error-ex": { - "version": "1.3.1", + "version": "1.3.2", "bundled": true, "dev": true, "requires": { "is-arrayish": "^0.2.1" } }, - "escape-string-regexp": { - "version": "1.0.5", - "bundled": true, - "dev": true - }, - "esutils": { - "version": "2.0.2", + "es6-error": { + "version": "4.1.1", "bundled": true, "dev": true }, "execa": { - "version": "0.7.0", + "version": "1.0.0", "bundled": true, "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", @@ -1146,176 +933,37 @@ }, "dependencies": { "cross-spawn": { - "version": "5.1.0", + "version": "6.0.5", "bundled": true, "dev": true, "requires": { - "lru-cache": "^4.0.1", + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } } } }, - "expand-brackets": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, - "fill-range": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "find-cache-dir": { - "version": "0.1.1", + "version": "2.0.0", "bundled": true, "dev": true, "requires": { "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" + "make-dir": "^1.0.0", + "pkg-dir": "^3.0.0" } }, "find-up": { - "version": "2.1.0", + "version": "3.0.0", "bundled": true, "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "^3.0.0" } }, - "for-in": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, "foreground-child": { "version": "1.5.6", "bundled": true, @@ -1325,36 +973,26 @@ "signal-exit": "^3.0.0" } }, - "fragment-cache": { - "version": "0.2.1", - "bundled": true, - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, "fs.realpath": { "version": "1.0.0", "bundled": true, "dev": true }, "get-caller-file": { - "version": "1.0.2", + "version": "1.0.3", "bundled": true, "dev": true }, "get-stream": { - "version": "3.0.0", + "version": "4.1.0", "bundled": true, - "dev": true - }, - "get-value": { - "version": "2.0.6", - "bundled": true, - "dev": true + "dev": true, + "requires": { + "pump": "^3.0.0" + } }, "glob": { - "version": "7.1.2", + "version": "7.1.3", "bundled": true, "dev": true, "requires": { @@ -1366,106 +1004,44 @@ "path-is-absolute": "^1.0.0" } }, - "globals": { - "version": "9.18.0", - "bundled": true, - "dev": true - }, "graceful-fs": { - "version": "4.1.11", + "version": "4.1.15", "bundled": true, "dev": true }, "handlebars": { - "version": "4.0.11", + "version": "4.1.0", "bundled": true, "dev": true, "requires": { - "async": "^1.4.0", + "async": "^2.5.0", "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" }, "dependencies": { "source-map": { - "version": "0.4.4", - "bundled": true, - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "has-ansi": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "has-value": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", + "version": "0.6.1", "bundled": true, "dev": true } } }, - "has-values": { - "version": "1.0.0", + "has-flag": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "hasha": { + "version": "3.0.0", "bundled": true, "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "is-stream": "^1.0.1" } }, "hosted-git-info": { - "version": "2.6.0", + "version": "2.7.1", "bundled": true, "dev": true }, @@ -1488,315 +1064,136 @@ "bundled": true, "dev": true }, - "invariant": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, "invert-kv": { - "version": "1.0.0", + "version": "2.0.0", "bundled": true, "dev": true }, - "is-accessor-descriptor": { - "version": "0.1.6", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, "is-arrayish": { "version": "0.2.1", "bundled": true, "dev": true }, - "is-buffer": { - "version": "1.1.6", - "bundled": true, - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "bundled": true, - "dev": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-fullwidth-code-point": { "version": "2.0.0", "bundled": true, "dev": true }, - "is-number": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-odd": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "bundled": true, - "dev": true - } - } - }, - "is-plain-object": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, "is-stream": { "version": "1.1.0", "bundled": true, "dev": true }, - "is-utf8": { - "version": "0.2.1", - "bundled": true, - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "isexe": { "version": "2.0.0", "bundled": true, "dev": true }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, "istanbul-lib-coverage": { - "version": "1.2.0", + "version": "2.0.3", "bundled": true, "dev": true }, "istanbul-lib-hook": { - "version": "1.1.0", + "version": "2.0.3", "bundled": true, "dev": true, "requires": { - "append-transform": "^0.4.0" - } - }, - "istanbul-lib-instrument": { - "version": "1.10.1", - "bundled": true, - "dev": true, - "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.0", - "semver": "^5.3.0" + "append-transform": "^1.0.0" } }, "istanbul-lib-report": { - "version": "1.1.3", + "version": "2.0.4", "bundled": true, "dev": true, "requires": { - "istanbul-lib-coverage": "^1.1.2", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "supports-color": "^6.0.0" }, "dependencies": { "supports-color": { - "version": "3.2.3", + "version": "6.1.0", "bundled": true, "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "^3.0.0" } } } }, "istanbul-lib-source-maps": { - "version": "1.2.3", - "bundled": true, - "dev": true, - "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.1.2", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "istanbul-reports": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "handlebars": "^4.0.3" - } - }, - "js-tokens": { "version": "3.0.2", "bundled": true, - "dev": true - }, - "jsesc": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "bundled": true, "dev": true, "requires": { - "is-buffer": "^1.1.5" - } - }, - "lazy-cache": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "optional": true - }, - "lcid": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "invert-kv": "^1.0.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "rimraf": "^2.6.2", + "source-map": "^0.6.1" }, "dependencies": { - "path-exists": { - "version": "3.0.0", + "source-map": { + "version": "0.6.1", "bundled": true, "dev": true } } }, - "lodash": { - "version": "4.17.10", - "bundled": true, - "dev": true - }, - "longest": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "loose-envify": { - "version": "1.3.1", + "istanbul-reports": { + "version": "2.1.1", "bundled": true, "dev": true, "requires": { - "js-tokens": "^3.0.0" + "handlebars": "^4.1.0" } }, + "json-parse-better-errors": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "lcid": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "bundled": true, + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "bundled": true, + "dev": true + }, "lru-cache": { - "version": "4.1.3", + "version": "4.1.5", "bundled": true, "dev": true, "requires": { @@ -1804,38 +1201,30 @@ "yallist": "^2.1.2" } }, - "map-cache": { - "version": "0.2.2", - "bundled": true, - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "md5-hex": { + "make-dir": { "version": "1.3.0", "bundled": true, "dev": true, "requires": { - "md5-o-matic": "^0.1.1" + "pify": "^3.0.0" } }, - "md5-o-matic": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "mem": { - "version": "1.1.0", + "map-age-cleaner": { + "version": "0.1.3", "bundled": true, "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "p-defer": "^1.0.0" + } + }, + "mem": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" } }, "merge-source-map": { @@ -1853,33 +1242,6 @@ } } }, - "micromatch": { - "version": "3.1.10", - "bundled": true, - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, "mimic-fn": { "version": "1.2.0", "bundled": true, @@ -1894,85 +1256,42 @@ } }, "minimist": { - "version": "0.0.8", + "version": "0.0.10", "bundled": true, "dev": true }, - "mixin-deep": { - "version": "1.3.1", - "bundled": true, - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, "requires": { "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "nanomatch": { - "version": "1.2.9", - "bundled": true, - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-odd": "^2.0.0", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" }, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", + "minimist": { + "version": "0.0.8", "bundled": true, "dev": true } } }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, "normalize-package-data": { - "version": "2.4.0", + "version": "2.5.0", "bundled": true, "dev": true, "requires": { "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", + "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } @@ -1990,61 +1309,6 @@ "bundled": true, "dev": true }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "bundled": true, - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, - "object.pick": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, "once": { "version": "1.4.0", "bundled": true, @@ -2068,62 +1332,76 @@ "dev": true }, "os-locale": { - "version": "2.1.0", + "version": "3.1.0", "bundled": true, "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" } }, + "p-defer": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, "p-finally": { "version": "1.0.0", "bundled": true, "dev": true }, - "p-limit": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { + "p-is-promise": { "version": "2.0.0", "bundled": true, - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "bundled": true, "dev": true }, - "parse-json": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "pascalcase": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "path-exists": { + "p-limit": { "version": "2.1.0", "bundled": true, "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "p-try": "^2.0.0" } }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "package-hash": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^3.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "bundled": true, @@ -2135,127 +1413,70 @@ "dev": true }, "path-parse": { - "version": "1.0.5", + "version": "1.0.6", "bundled": true, "dev": true }, "path-type": { - "version": "1.1.0", + "version": "3.0.0", "bundled": true, "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "pify": "^3.0.0" } }, "pify": { - "version": "2.3.0", + "version": "3.0.0", "bundled": true, "dev": true }, - "pinkie": { - "version": "2.0.4", - "bundled": true, - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, "pkg-dir": { - "version": "1.0.0", + "version": "3.0.0", "bundled": true, "dev": true, "requires": { - "find-up": "^1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } + "find-up": "^3.0.0" } }, - "posix-character-classes": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, "pseudomap": { "version": "1.0.2", "bundled": true, "dev": true }, - "read-pkg": { - "version": "1.1.0", + "pump": { + "version": "3.0.0", "bundled": true, "dev": true, "requires": { - "load-json-file": "^1.0.0", + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "path-type": "^3.0.0" } }, "read-pkg-up": { - "version": "1.0.1", + "version": "4.0.0", "bundled": true, "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" } }, - "regenerator-runtime": { - "version": "0.11.1", - "bundled": true, - "dev": true - }, - "regex-not": { - "version": "1.0.2", + "release-zalgo": { + "version": "1.0.0", "bundled": true, "dev": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "repeat-element": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "bundled": true, - "dev": true - }, - "repeating": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-finite": "^1.0.0" + "es6-error": "^4.0.1" } }, "require-directory": { @@ -2268,48 +1489,34 @@ "bundled": true, "dev": true }, - "resolve-from": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "bundled": true, - "dev": true - }, - "ret": { - "version": "0.1.15", - "bundled": true, - "dev": true - }, - "right-align": { - "version": "0.1.3", + "resolve": { + "version": "1.10.0", "bundled": true, "dev": true, - "optional": true, "requires": { - "align-text": "^0.1.1" + "path-parse": "^1.0.6" } }, + "resolve-from": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, "rimraf": { - "version": "2.6.2", + "version": "2.6.3", "bundled": true, "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "^7.1.3" } }, - "safe-regex": { - "version": "1.1.0", + "safe-buffer": { + "version": "5.1.2", "bundled": true, - "dev": true, - "requires": { - "ret": "~0.1.10" - } + "dev": true }, "semver": { - "version": "5.5.0", + "version": "5.6.0", "bundled": true, "dev": true }, @@ -2318,27 +1525,6 @@ "bundled": true, "dev": true }, - "set-value": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "shebang-command": { "version": "1.2.0", "bundled": true, @@ -2357,130 +1543,6 @@ "bundled": true, "dev": true }, - "slide": { - "version": "1.1.6", - "bundled": true, - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "bundled": true, - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.2.0" - } - }, - "source-map": { - "version": "0.5.7", - "bundled": true, - "dev": true - }, - "source-map-resolve": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "atob": "^2.0.0", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "bundled": true, - "dev": true - }, "spawn-wrap": { "version": "1.4.2", "bundled": true, @@ -2495,7 +1557,7 @@ } }, "spdx-correct": { - "version": "3.0.0", + "version": "3.1.0", "bundled": true, "dev": true, "requires": { @@ -2504,7 +1566,7 @@ } }, "spdx-exceptions": { - "version": "2.1.0", + "version": "2.2.0", "bundled": true, "dev": true }, @@ -2518,37 +1580,10 @@ } }, "spdx-license-ids": { - "version": "3.0.0", + "version": "3.0.3", "bundled": true, "dev": true }, - "split-string": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "static-extend": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, "string-width": { "version": "2.1.1", "bundled": true, @@ -2556,485 +1591,62 @@ "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } } }, "strip-ansi": { - "version": "3.0.1", + "version": "4.0.0", "bundled": true, "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^3.0.0" } }, "strip-bom": { - "version": "2.0.0", + "version": "3.0.0", "bundled": true, - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } + "dev": true }, "strip-eof": { "version": "1.0.0", "bundled": true, "dev": true }, - "supports-color": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, "test-exclude": { - "version": "4.2.1", + "version": "5.1.0", "bundled": true, "dev": true, "requires": { "arrify": "^1.0.1", - "micromatch": "^3.1.8", - "object-assign": "^4.1.0", - "read-pkg-up": "^1.0.1", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", "require-main-filename": "^1.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "bundled": true, - "dev": true - }, - "braces": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "bundled": true, - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "bundled": true, - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "bundled": true, - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } } }, - "to-fast-properties": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "to-regex": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - } - } - }, - "trim-right": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, "uglify-js": { - "version": "2.8.29", + "version": "3.4.9", "bundled": true, "dev": true, "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "commander": "~2.17.1", + "source-map": "~0.6.1" }, "dependencies": { - "yargs": { - "version": "3.10.0", + "source-map": { + "version": "0.6.1", "bundled": true, "dev": true, - "optional": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } + "optional": true } } }, - "uglify-to-browserify": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "union-value": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "bundled": true, - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } - } - }, - "unset-value": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "bundled": true, - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "isobject": { - "version": "3.0.1", - "bundled": true, - "dev": true - } - } - }, - "urix": { - "version": "0.1.0", + "uuid": { + "version": "3.3.2", "bundled": true, "dev": true }, - "use": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "bundled": true, - "dev": true - } - } - }, "validate-npm-package-license": { - "version": "3.0.3", + "version": "3.0.4", "bundled": true, "dev": true, "requires": { @@ -3043,7 +1655,7 @@ } }, "which": { - "version": "1.3.0", + "version": "1.3.1", "bundled": true, "dev": true, "requires": { @@ -3055,12 +1667,6 @@ "bundled": true, "dev": true }, - "window-size": { - "version": "0.1.0", - "bundled": true, - "dev": true, - "optional": true - }, "wordwrap": { "version": "0.0.3", "bundled": true, @@ -3075,6 +1681,11 @@ "strip-ansi": "^3.0.1" }, "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, @@ -3092,6 +1703,14 @@ "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } } } }, @@ -3101,17 +1720,17 @@ "dev": true }, "write-file-atomic": { - "version": "1.3.4", + "version": "2.4.2", "bundled": true, "dev": true, "requires": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "slide": "^1.1.5" + "signal-exit": "^3.0.2" } }, "y18n": { - "version": "3.2.1", + "version": "4.0.0", "bundled": true, "dev": true }, @@ -3121,75 +1740,31 @@ "dev": true }, "yargs": { - "version": "11.1.0", + "version": "12.0.5", "bundled": true, "dev": true, "requires": { "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", + "os-locale": "^3.0.0", "require-directory": "^2.1.1", "require-main-filename": "^1.0.1", "set-blocking": "^2.0.0", "string-width": "^2.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "cliui": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "yargs-parser": { - "version": "9.0.2", - "bundled": true, - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" } }, "yargs-parser": { - "version": "8.1.0", + "version": "11.1.1", "bundled": true, "dev": true, "requires": { - "camelcase": "^4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "bundled": true, - "dev": true - } + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } @@ -3299,6 +1874,12 @@ "safe-buffer": "^5.0.1" } }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -3341,6 +1922,18 @@ "nan": "^2.10.0" } }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", diff --git a/package.json b/package.json index e62819a..4ec4077 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip32": "git+https://github.com/junderw/bip32.git#typeScript", + "bip32": "^2.0.0", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", "bs58check": "^2.0.0", @@ -67,7 +67,7 @@ "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^5.2.0", - "nyc": "^11.8.0", + "nyc": "^13.3.0", "prettier": "1.16.4", "proxyquire": "^2.0.1", "tslint": "5.13.1", From 74375bfedf702cccadc426c086d53e9334110ec9 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 20 Mar 2019 15:25:48 +0900 Subject: [PATCH 260/568] Fix class constructors --- src/block.js | 56 +++++++++++----------- src/ecpair.js | 12 ++--- src/transaction_builder.js | 19 ++++---- ts_src/block.ts | 87 +++++++++++++++------------------- ts_src/ecpair.ts | 13 +++-- ts_src/transaction.ts | 15 ++---- ts_src/transaction_builder.ts | 14 +++--- types/block.d.ts | 1 - types/ecpair.d.ts | 6 +-- types/transaction.d.ts | 1 - types/transaction_builder.d.ts | 2 +- 11 files changed, 101 insertions(+), 125 deletions(-) diff --git a/src/block.js b/src/block.js index 913ad9a..da17193 100644 --- a/src/block.js +++ b/src/block.js @@ -9,25 +9,17 @@ const typeforce = require('typeforce'); const varuint = require('varuint-bitcoin'); const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions'); const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block'); -function txesHaveWitnessCommit(transactions) { - return (transactions instanceof Array && - transactions[0] && - transactions[0].ins && - transactions[0].ins instanceof Array && - transactions[0].ins[0] && - transactions[0].ins[0].witness && - transactions[0].ins[0].witness instanceof Array && - transactions[0].ins[0].witness.length > 0); -} -function anyTxHasWitness(transactions) { - return (transactions instanceof Array && - transactions.some(tx => typeof tx === 'object' && - tx.ins instanceof Array && - tx.ins.some(input => typeof input === 'object' && - input.witness instanceof Array && - input.witness.length > 0))); -} class Block { + constructor() { + this.version = 1; + this.prevHash = undefined; + this.merkleRoot = undefined; + this.timestamp = 0; + this.witnessCommit = undefined; + this.bits = 0; + this.nonce = 0; + this.transactions = undefined; + } static fromBuffer(buffer) { if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); @@ -99,16 +91,6 @@ class Block { ? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) : rootHash; } - constructor() { - this.version = 1; - this.timestamp = 0; - this.bits = 0; - this.nonce = 0; - this.prevHash = undefined; - this.merkleRoot = undefined; - this.witnessCommit = undefined; - this.transactions = undefined; - } getWitnessCommit() { if (!txesHaveWitnessCommit(this.transactions)) return null; @@ -220,3 +202,21 @@ class Block { } } exports.Block = Block; +function txesHaveWitnessCommit(transactions) { + return (transactions instanceof Array && + transactions[0] && + transactions[0].ins && + transactions[0].ins instanceof Array && + transactions[0].ins[0] && + transactions[0].ins[0].witness && + transactions[0].ins[0].witness instanceof Array && + transactions[0].ins[0].witness.length > 0); +} +function anyTxHasWitness(transactions) { + return (transactions instanceof Array && + transactions.some(tx => typeof tx === 'object' && + tx.ins instanceof Array && + tx.ins.some(input => typeof input === 'object' && + input.witness instanceof Array && + input.witness.length > 0))); +} diff --git a/src/ecpair.js b/src/ecpair.js index 1335014..2026c63 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -11,18 +11,16 @@ const isOptions = typeforce.maybe(typeforce.compile({ network: types.maybe(types.Network), })); class ECPair { - constructor(d, Q, options) { + constructor(__D, __Q, options) { + this.__D = __D; + this.__Q = __Q; if (options === undefined) options = {}; this.compressed = options.compressed === undefined ? true : options.compressed; this.network = options.network || NETWORKS.bitcoin; - this.__D = undefined; - this.__Q = undefined; - if (d !== undefined) - this.__D = d; - if (Q !== undefined) - this.__Q = ecc.pointCompress(Q, this.compressed); + if (__Q !== undefined) + this.__Q = ecc.pointCompress(__Q, this.compressed); } get privateKey() { return this.__D; diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 0b876a7..d3b2673 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -20,6 +20,16 @@ function txIsTransaction(tx) { return tx instanceof transaction_1.Transaction; } class TransactionBuilder { + // WARNING: maximumFeeRate is __NOT__ to be relied on, + // it's just another potential safety mechanism (safety in-depth) + constructor(network = networks.bitcoin, maximumFeeRate = 2500) { + this.network = network; + this.maximumFeeRate = maximumFeeRate; + this.__PREV_TX_SET = {}; + this.__INPUTS = []; + this.__TX = new transaction_1.Transaction(); + this.__TX.version = 2; + } static fromTransaction(transaction, network) { const txb = new TransactionBuilder(network); // Copy transaction fields @@ -43,15 +53,6 @@ class TransactionBuilder { }); return txb; } - constructor(network, maximumFeeRate) { - this.__PREV_TX_SET = {}; - this.network = network || networks.bitcoin; - // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) - this.maximumFeeRate = maximumFeeRate || 2500; - this.__INPUTS = []; - this.__TX = new transaction_1.Transaction(); - this.__TX.version = 2; - } setLockTime(locktime) { typeforce(types.UInt32, locktime); // if any signatures exist, throw diff --git a/ts_src/block.ts b/ts_src/block.ts index 9a9bd45..820c075 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -14,36 +14,6 @@ const errorWitnessNotSegwit = new TypeError( 'Cannot compute witness commit for non-segwit block', ); -function txesHaveWitnessCommit(transactions: Transaction[]): boolean { - return ( - transactions instanceof Array && - transactions[0] && - transactions[0].ins && - transactions[0].ins instanceof Array && - transactions[0].ins[0] && - transactions[0].ins[0].witness && - transactions[0].ins[0].witness instanceof Array && - transactions[0].ins[0].witness.length > 0 - ); -} - -function anyTxHasWitness(transactions: Transaction[]): boolean { - return ( - transactions instanceof Array && - transactions.some( - tx => - typeof tx === 'object' && - tx.ins instanceof Array && - tx.ins.some( - input => - typeof input === 'object' && - input.witness instanceof Array && - input.witness.length > 0, - ), - ) - ); -} - export class Block { static fromBuffer(buffer: Buffer): Block { if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); @@ -137,25 +107,14 @@ export class Block { : rootHash; } - version: number; - prevHash?: Buffer; - merkleRoot?: Buffer; - timestamp: number; - witnessCommit?: Buffer; - bits: number; - nonce: number; - transactions?: Transaction[]; - - constructor() { - this.version = 1; - this.timestamp = 0; - this.bits = 0; - this.nonce = 0; - this.prevHash = undefined; - this.merkleRoot = undefined; - this.witnessCommit = undefined; - this.transactions = undefined; - } + version: number = 1; + prevHash?: Buffer = undefined; + merkleRoot?: Buffer = undefined; + timestamp: number = 0; + witnessCommit?: Buffer = undefined; + bits: number = 0; + nonce: number = 0; + transactions?: Transaction[] = undefined; getWitnessCommit(): Buffer | null { if (!txesHaveWitnessCommit(this.transactions!)) return null; @@ -294,3 +253,33 @@ export class Block { return this.witnessCommit!.compare(actualWitnessCommit) === 0; } } + +function txesHaveWitnessCommit(transactions: Transaction[]): boolean { + return ( + transactions instanceof Array && + transactions[0] && + transactions[0].ins && + transactions[0].ins instanceof Array && + transactions[0].ins[0] && + transactions[0].ins[0].witness && + transactions[0].ins[0].witness instanceof Array && + transactions[0].ins[0].witness.length > 0 + ); +} + +function anyTxHasWitness(transactions: Transaction[]): boolean { + return ( + transactions instanceof Array && + transactions.some( + tx => + typeof tx === 'object' && + tx.ins instanceof Array && + tx.ins.some( + input => + typeof input === 'object' && + input.witness instanceof Array && + input.witness.length > 0, + ), + ) + ); +} diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index ec8dc13..9e56919 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -33,19 +33,18 @@ export interface ECPairInterface { class ECPair implements ECPairInterface { compressed: boolean; network: Network; - private __D?: Buffer; - private __Q?: Buffer; - constructor(d?: Buffer, Q?: Buffer, options?: ECPairOptions) { + constructor( + private __D?: Buffer, + private __Q?: Buffer, + options?: ECPairOptions, + ) { if (options === undefined) options = {}; this.compressed = options.compressed === undefined ? true : options.compressed; this.network = options.network || NETWORKS.bitcoin; - this.__D = undefined; - this.__Q = undefined; - if (d !== undefined) this.__D = d; - if (Q !== undefined) this.__Q = ecc.pointCompress(Q, this.compressed); + if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed); } get privateKey(): Buffer | undefined { diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index 995f1d7..b788d91 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -182,17 +182,10 @@ export class Transaction { return true; } - version: number; - locktime: number; - ins: Input[]; - outs: OpenOutput[]; - - constructor() { - this.version = 1; - this.locktime = 0; - this.ins = []; - this.outs = []; - } + version: number = 1; + locktime: number = 0; + ins: Input[] = []; + outs: OpenOutput[] = []; isCoinbase(): boolean { return ( diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 4c939e1..575fbc2 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -91,19 +91,17 @@ export class TransactionBuilder { return txb; } - network: Network; - maximumFeeRate: number; private __PREV_TX_SET: { [index: string]: boolean }; private __INPUTS: TxbInput[]; private __TX: Transaction; - constructor(network?: Network, maximumFeeRate?: number) { + // WARNING: maximumFeeRate is __NOT__ to be relied on, + // it's just another potential safety mechanism (safety in-depth) + constructor( + public network: Network = networks.bitcoin, + public maximumFeeRate: number = 2500, + ) { this.__PREV_TX_SET = {}; - this.network = network || networks.bitcoin; - - // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth) - this.maximumFeeRate = maximumFeeRate || 2500; - this.__INPUTS = []; this.__TX = new Transaction(); this.__TX.version = 2; diff --git a/types/block.d.ts b/types/block.d.ts index 6b6596c..dd4fc55 100644 --- a/types/block.d.ts +++ b/types/block.d.ts @@ -13,7 +13,6 @@ export declare class Block { bits: number; nonce: number; transactions?: Transaction[]; - constructor(); getWitnessCommit(): Buffer | null; hasWitnessCommit(): boolean; hasWitness(): boolean; diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index 674615b..33535bd 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -16,11 +16,11 @@ export interface ECPairInterface { getPublicKey?(): Buffer; } declare class ECPair implements ECPairInterface { - compressed: boolean; - network: Network; private __D?; private __Q?; - constructor(d?: Buffer, Q?: Buffer, options?: ECPairOptions); + compressed: boolean; + network: Network; + constructor(__D?: Buffer | undefined, __Q?: Buffer | undefined, options?: ECPairOptions); readonly privateKey: Buffer | undefined; readonly publicKey: Buffer | undefined; toWIF(): string; diff --git a/types/transaction.d.ts b/types/transaction.d.ts index 4f8a2b9..9bdba19 100644 --- a/types/transaction.d.ts +++ b/types/transaction.d.ts @@ -30,7 +30,6 @@ export declare class Transaction { locktime: number; ins: Input[]; outs: OpenOutput[]; - constructor(); isCoinbase(): boolean; addInput(hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number; addOutput(scriptPubKey: Buffer, value: number): number; diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts index b3dedb3..16adee6 100644 --- a/types/transaction_builder.d.ts +++ b/types/transaction_builder.d.ts @@ -3,9 +3,9 @@ import { ECPairInterface } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; export declare class TransactionBuilder { - static fromTransaction(transaction: Transaction, network?: Network): TransactionBuilder; network: Network; maximumFeeRate: number; + static fromTransaction(transaction: Transaction, network?: Network): TransactionBuilder; private __PREV_TX_SET; private __INPUTS; private __TX; From 0cdd7b1e2c478f4403d3dcd0acaa821433a57b6b Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Fri, 22 Mar 2019 00:15:37 +0900 Subject: [PATCH 261/568] ci: add a tslint rule to require type definitions --- ts_src/payments/lazy.ts | 4 ++-- ts_src/payments/p2ms.ts | 2 +- ts_src/script.ts | 2 +- ts_src/templates/multisig/input.ts | 2 +- ts_src/templates/multisig/output.ts | 2 +- ts_src/templates/nulldata.ts | 2 +- ts_src/templates/pubkey/input.ts | 2 +- ts_src/templates/pubkey/output.ts | 2 +- ts_src/templates/pubkeyhash/input.ts | 2 +- ts_src/templates/pubkeyhash/output.ts | 2 +- ts_src/templates/scripthash/input.ts | 2 +- ts_src/templates/scripthash/output.ts | 2 +- ts_src/templates/witnesscommitment/output.ts | 2 +- ts_src/templates/witnesspubkeyhash/input.ts | 2 +- ts_src/templates/witnesspubkeyhash/output.ts | 2 +- ts_src/templates/witnessscripthash/input.ts | 2 +- ts_src/templates/witnessscripthash/output.ts | 2 +- ts_src/transaction.ts | 20 ++++++++++---------- ts_src/transaction_builder.ts | 2 +- ts_src/types.ts | 2 +- tslint.json | 6 ++++++ 21 files changed, 36 insertions(+), 30 deletions(-) diff --git a/ts_src/payments/lazy.ts b/ts_src/payments/lazy.ts index fe0fb6d..1df181f 100644 --- a/ts_src/payments/lazy.ts +++ b/ts_src/payments/lazy.ts @@ -2,12 +2,12 @@ export function prop(object: {}, name: string, f: () => any): void { Object.defineProperty(object, name, { configurable: true, enumerable: true, - get() { + get(): any { const _value = f.call(this); this[name] = _value; return _value; }, - set(_value) { + set(_value: any): void { Object.defineProperty(this, name, { configurable: true, enumerable: true, diff --git a/ts_src/payments/p2ms.ts b/ts_src/payments/p2ms.ts index b8691c0..bac8b83 100644 --- a/ts_src/payments/p2ms.ts +++ b/ts_src/payments/p2ms.ts @@ -28,7 +28,7 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); - function isAcceptableSignature(x: Buffer | number) { + function isAcceptableSignature(x: Buffer | number): boolean { return ( bscript.isCanonicalScriptSignature(x as Buffer) || (opts!.allowIncomplete && (x as number) === OPS.OP_0) !== undefined diff --git a/ts_src/script.ts b/ts_src/script.ts index 1c705d3..951f48b 100644 --- a/ts_src/script.ts +++ b/ts_src/script.ts @@ -26,7 +26,7 @@ function isPushOnlyChunk(value: number | Buffer): boolean { return types.Buffer(value) || isOPInt(value as number); } -export function isPushOnly(value: Stack) { +export function isPushOnly(value: Stack): boolean { return types.Array(value) && value.every(isPushOnlyChunk); } diff --git a/ts_src/templates/multisig/input.ts b/ts_src/templates/multisig/input.ts index 57b73d2..31fe416 100644 --- a/ts_src/templates/multisig/input.ts +++ b/ts_src/templates/multisig/input.ts @@ -26,6 +26,6 @@ export function check( bscript.isCanonicalScriptSignature, ); } -check.toJSON = () => { +check.toJSON = (): string => { return 'multisig input'; }; diff --git a/ts_src/templates/multisig/output.ts b/ts_src/templates/multisig/output.ts index 3ee0820..20c2162 100644 --- a/ts_src/templates/multisig/output.ts +++ b/ts_src/templates/multisig/output.ts @@ -28,6 +28,6 @@ export function check( const keys = chunks.slice(1, -2) as Buffer[]; return keys.every(bscript.isCanonicalPubKey); } -check.toJSON = () => { +check.toJSON = (): string => { return 'multi-sig output'; }; diff --git a/ts_src/templates/nulldata.ts b/ts_src/templates/nulldata.ts index bafe4a4..99b5c44 100644 --- a/ts_src/templates/nulldata.ts +++ b/ts_src/templates/nulldata.ts @@ -7,7 +7,7 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; } -check.toJSON = () => { +check.toJSON = (): string => { return 'null data output'; }; diff --git a/ts_src/templates/pubkey/input.ts b/ts_src/templates/pubkey/input.ts index 7e07a94..745e6d9 100644 --- a/ts_src/templates/pubkey/input.ts +++ b/ts_src/templates/pubkey/input.ts @@ -11,6 +11,6 @@ export function check(script: Buffer | Stack): boolean { bscript.isCanonicalScriptSignature(chunks[0] as Buffer) ); } -check.toJSON = () => { +check.toJSON = (): string => { return 'pubKey input'; }; diff --git a/ts_src/templates/pubkey/output.ts b/ts_src/templates/pubkey/output.ts index d57422d..1b2c391 100644 --- a/ts_src/templates/pubkey/output.ts +++ b/ts_src/templates/pubkey/output.ts @@ -13,6 +13,6 @@ export function check(script: Buffer | Stack): boolean { chunks[1] === OPS.OP_CHECKSIG ); } -check.toJSON = () => { +check.toJSON = (): string => { return 'pubKey output'; }; diff --git a/ts_src/templates/pubkeyhash/input.ts b/ts_src/templates/pubkeyhash/input.ts index 83da475..de93968 100644 --- a/ts_src/templates/pubkeyhash/input.ts +++ b/ts_src/templates/pubkeyhash/input.ts @@ -12,6 +12,6 @@ export function check(script: Buffer | Stack): boolean { bscript.isCanonicalPubKey(chunks[1] as Buffer) ); } -check.toJSON = () => { +check.toJSON = (): string => { return 'pubKeyHash input'; }; diff --git a/ts_src/templates/pubkeyhash/output.ts b/ts_src/templates/pubkeyhash/output.ts index 37070a3..248c210 100644 --- a/ts_src/templates/pubkeyhash/output.ts +++ b/ts_src/templates/pubkeyhash/output.ts @@ -15,6 +15,6 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { buffer[24] === OPS.OP_CHECKSIG ); } -check.toJSON = () => { +check.toJSON = (): string => { return 'pubKeyHash output'; }; diff --git a/ts_src/templates/scripthash/input.ts b/ts_src/templates/scripthash/input.ts index 1e3f97d..3ef8aab 100644 --- a/ts_src/templates/scripthash/input.ts +++ b/ts_src/templates/scripthash/input.ts @@ -56,6 +56,6 @@ export function check( return false; } -check.toJSON = () => { +check.toJSON = (): string => { return 'scriptHash input'; }; diff --git a/ts_src/templates/scripthash/output.ts b/ts_src/templates/scripthash/output.ts index 7eac30d..aea8e24 100644 --- a/ts_src/templates/scripthash/output.ts +++ b/ts_src/templates/scripthash/output.ts @@ -13,6 +13,6 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { buffer[22] === OPS.OP_EQUAL ); } -check.toJSON = () => { +check.toJSON = (): string => { return 'scriptHash output'; }; diff --git a/ts_src/templates/witnesscommitment/output.ts b/ts_src/templates/witnesscommitment/output.ts index 9439e4c..482798f 100644 --- a/ts_src/templates/witnesscommitment/output.ts +++ b/ts_src/templates/witnesscommitment/output.ts @@ -19,7 +19,7 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { ); } -check.toJSON = () => { +check.toJSON = (): string => { return 'Witness commitment output'; }; diff --git a/ts_src/templates/witnesspubkeyhash/input.ts b/ts_src/templates/witnesspubkeyhash/input.ts index aa3bef8..26fe63d 100644 --- a/ts_src/templates/witnesspubkeyhash/input.ts +++ b/ts_src/templates/witnesspubkeyhash/input.ts @@ -16,6 +16,6 @@ export function check(script: Buffer | Stack): boolean { isCompressedCanonicalPubKey(chunks[1] as Buffer) ); } -check.toJSON = () => { +check.toJSON = (): string => { return 'witnessPubKeyHash input'; }; diff --git a/ts_src/templates/witnesspubkeyhash/output.ts b/ts_src/templates/witnesspubkeyhash/output.ts index 0e9432c..bfd6690 100644 --- a/ts_src/templates/witnesspubkeyhash/output.ts +++ b/ts_src/templates/witnesspubkeyhash/output.ts @@ -8,6 +8,6 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { return buffer.length === 22 && buffer[0] === OPS.OP_0 && buffer[1] === 0x14; } -check.toJSON = () => { +check.toJSON = (): string => { return 'Witness pubKeyHash output'; }; diff --git a/ts_src/templates/witnessscripthash/input.ts b/ts_src/templates/witnessscripthash/input.ts index 42a13ac..0f63330 100644 --- a/ts_src/templates/witnessscripthash/input.ts +++ b/ts_src/templates/witnessscripthash/input.ts @@ -42,6 +42,6 @@ export function check(chunks: Buffer[], allowIncomplete?: boolean): boolean { return false; } -check.toJSON = () => { +check.toJSON = (): string => { return 'witnessScriptHash input'; }; diff --git a/ts_src/templates/witnessscripthash/output.ts b/ts_src/templates/witnessscripthash/output.ts index 85034d1..7d4f33a 100644 --- a/ts_src/templates/witnessscripthash/output.ts +++ b/ts_src/templates/witnessscripthash/output.ts @@ -8,6 +8,6 @@ export function check(script: Buffer | Array<number | Buffer>): boolean { return buffer.length === 34 && buffer[0] === OPS.OP_0 && buffer[1] === 0x20; } -check.toJSON = () => { +check.toJSON = (): string => { return 'Witness scriptHash output'; }; diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index b788d91..218d004 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -497,17 +497,17 @@ export class Transaction { return this.__toBuffer(buffer, initialOffset, true); } - toHex() { + toHex(): string { return this.toBuffer(undefined, undefined).toString('hex'); } - setInputScript(index: number, scriptSig: Buffer) { + setInputScript(index: number, scriptSig: Buffer): void { typeforce(types.tuple(types.Number, types.Buffer), arguments); this.ins[index].script = scriptSig; } - setWitness(index: number, witness: Buffer[]) { + setWitness(index: number, witness: Buffer[]): void { typeforce(types.tuple(types.Number, [types.Buffer]), arguments); this.ins[index].witness = witness; @@ -548,33 +548,33 @@ export class Transaction { offset += slice.copy(buffer!, offset); } - function writeUInt8(i: number) { + function writeUInt8(i: number): void { offset = buffer!.writeUInt8(i, offset); } - function writeUInt32(i: number) { + function writeUInt32(i: number): void { offset = buffer!.writeUInt32LE(i, offset); } - function writeInt32(i: number) { + function writeInt32(i: number): void { offset = buffer!.writeInt32LE(i, offset); } - function writeUInt64(i: number) { + function writeUInt64(i: number): void { offset = bufferutils.writeUInt64LE(buffer!, i, offset); } - function writeVarInt(i: number) { + function writeVarInt(i: number): void { varuint.encode(i, buffer, offset); offset += varuint.encode.bytes; } - function writeVarSlice(slice: Buffer) { + function writeVarSlice(slice: Buffer): void { writeVarInt(slice.length); writeSlice(slice); } - function writeVector(vector: Buffer[]) { + function writeVector(vector: Buffer[]): void { writeVarInt(vector.length); vector.forEach(writeVarSlice); } diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 575fbc2..2367250 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -192,7 +192,7 @@ export class TransactionBuilder { hashType: number, witnessValue: number, witnessScript: Buffer, - ) { + ): void { // TODO: remove keyPair.network matching in 4.0.0 if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network'); diff --git a/ts_src/types.ts b/ts_src/types.ts index 033e62a..06c247d 100644 --- a/ts_src/types.ts +++ b/ts_src/types.ts @@ -8,7 +8,7 @@ export function UInt31(value: number): boolean { export function BIP32Path(value: string): boolean { return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); } -BIP32Path.toJSON = () => { +BIP32Path.toJSON = (): string => { return 'BIP32 derivation path'; }; diff --git a/tslint.json b/tslint.json index 9708e8b..1ba998d 100644 --- a/tslint.json +++ b/tslint.json @@ -22,6 +22,12 @@ "no-unused-expression": false, "object-literal-sort-keys": false, "quotemark": [true, "single"], + "typedef": [ + true, + "call-signature", + "arrow-call-signature", + "property-declaration" + ], "variable-name": [ true, "ban-keywords", From 2d5307f06f4d72234779455d400b52276d37e8dc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 22 Mar 2019 12:33:33 +0900 Subject: [PATCH 262/568] Add tslint to travis --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 90088bb..dde4713 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,14 @@ sudo: false language: node_js node_js: + - "8" - "lts/*" - - "9" - - "10" matrix: include: - node_js: "lts/*" env: TEST_SUITE=format:ci + - node_js: "lts/*" + env: TEST_SUITE=lint - node_js: "lts/*" env: TEST_SUITE=coverage env: From d4dc26fb339e311e7f37bbeb6f7e3a0f81ea070c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 1 Apr 2019 16:22:47 +0900 Subject: [PATCH 263/568] Prevent JS/TS diff in Travis --- .travis.yml | 2 ++ package.json | 1 + 2 files changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index dde4713..5c1cfd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,8 @@ matrix: include: - node_js: "lts/*" env: TEST_SUITE=format:ci + - node_js: "lts/*" + env: TEST_SUITE=gitdiff:ci - node_js: "lts/*" env: TEST_SUITE=lint - node_js: "lts/*" diff --git a/package.json b/package.json index 4ec4077..491a24a 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "coverage": "npm run build && npm run nobuild:coverage", "format": "npm run prettier -- --write", "format:ci": "npm run prettier -- --check", + "gitdiff:ci": "npm run build && git diff --exit-code", "integration": "npm run build && npm run nobuild:integration", "lint": "tslint -p tsconfig.json -c tslint.json", "nobuild:coverage-report": "nyc report --reporter=lcov", From 335ed99a1cfae5029948e9f7bd20be8502c74319 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 2 Apr 2019 18:57:07 +0900 Subject: [PATCH 264/568] Fix error for lack of rmd160 in Electron v4 --- src/crypto.js | 13 ++++++++++--- ts_src/crypto.ts | 12 +++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/crypto.js b/src/crypto.js index d494b86..38ec4f9 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -2,9 +2,16 @@ Object.defineProperty(exports, "__esModule", { value: true }); const createHash = require('create-hash'); function ripemd160(buffer) { - return createHash('rmd160') - .update(buffer) - .digest(); + try { + return createHash('rmd160') + .update(buffer) + .digest(); + } + catch (err) { + return createHash('ripemd160') + .update(buffer) + .digest(); + } } exports.ripemd160 = ripemd160; function sha1(buffer) { diff --git a/ts_src/crypto.ts b/ts_src/crypto.ts index 6d92e60..1cb5a69 100644 --- a/ts_src/crypto.ts +++ b/ts_src/crypto.ts @@ -1,9 +1,15 @@ const createHash = require('create-hash'); export function ripemd160(buffer: Buffer): Buffer { - return createHash('rmd160') - .update(buffer) - .digest(); + try { + return createHash('rmd160') + .update(buffer) + .digest(); + } catch (err) { + return createHash('ripemd160') + .update(buffer) + .digest(); + } } export function sha1(buffer: Buffer): Buffer { From af7c308465768550f725cf414dba92c11141d9e0 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 5 Apr 2019 18:15:35 +0900 Subject: [PATCH 265/568] Update README and CHANGELOG for v5 (typescript) --- CHANGELOG.md | 12 ++++++++++++ README.md | 20 +++----------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f31645b..25c9b3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 5.0.0 +__added__ +- TypeScript support (#1319) +- `Block.prototype.checkTxRoots` will check the merkleRoot and witnessCommit if it exists against the transactions array. (e52abec) (0426c66) + +__changed__ +- `Transaction.prototype.getHash` now has `forWitness?: boolean` which when true returns the hash for wtxid (a652d04) +- `Block.calculateMerkleRoot` now has `forWitness?: boolean` which when true returns the witness commit (a652d04) + +__removed__ +- `Block.prototype.checkMerkleRoot` was removed, please use `checkTxRoots` (0426c66) + # 4.0.3 __fixed__ - Fixed `TransactionBuilder` to require that the Transaction has outputs before signing (#1151) diff --git a/README.md b/README.md index 458693f..27b336c 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [](https://travis-ci.org/bitcoinjs/bitcoinjs-lib) [](https://www.npmjs.org/package/bitcoinjs-lib) -[](https://github.com/feross/standard) +[](https://github.com/prettier/prettier) -A javascript Bitcoin library for node.js and browsers. +A javascript Bitcoin library for node.js and browsers. Written in TypeScript, but committing the JS files to verify. Released under the terms of the [MIT LICENSE](LICENSE). @@ -23,7 +23,7 @@ Mistakes and bugs happen, but with your help in resolving and reporting [issues] - Easy to audit and verify, - Tested, with test coverage >95%, - Advanced and feature rich, -- Standardized, using [standard](https://github.com/standard/standard) and Node `Buffer`'s throughout, and +- Standardized, using [prettier](https://github.com/prettier/prettier) and Node `Buffer`'s throughout, and - Friendly, with a strong and helpful community, ready to answer questions. @@ -80,20 +80,6 @@ If you're familiar with how to use browserify, ignore this and carry on, otherwi ### Typescript or VSCode users Type declarations for Typescript are included in this library. Normal installation should include all the needed type information. - -### Flow -[Flow-type](https://flowtype.org/) definitions for are available in the [flow-*typed* repository](https://github.com/flowtype/flow-typed/tree/master/definitions/npm/bitcoinjs-lib_v2.x.x) for version `^2.0.0` of the library. - -You can [download them directly](https://github.com/flowtype/flow-typed/blob/master/definitions/npm/bitcoinjs-lib_v2.x.x/flow_v0.17.x-/bitcoinjs-lib_v2.x.x.js), or using the flow-typed CLI: - -``` bash -npm install -g flow-typed -flow-typed install -f 0.27 bitcoinjs-lib@2.2.0 -``` - -**WARNING**: These flow-typed definitions are not maintained by the maintainers of this repository. - - ## Examples The below examples are implemented as integration tests, they should be very easy to understand. Otherwise, pull requests are appreciated. From 8a65e85915fcb8ee7dfc7b95018e59d7e69ca179 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 5 Apr 2019 18:17:39 +0900 Subject: [PATCH 266/568] 5.0.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3b11623..e56b8d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "4.0.3", + "version": "5.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 491a24a..69d704f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "4.0.3", + "version": "5.0.0", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From fcde97769e138b3d2d0c288825e014939adf84de Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 6 Apr 2019 14:34:14 +0900 Subject: [PATCH 267/568] Amend CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25c9b3c..cd856e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ __changed__ __removed__ - `Block.prototype.checkMerkleRoot` was removed, please use `checkTxRoots` (0426c66) +# 4.0.5 +__fixed__ +- Fixed bug where Angular apps break due to lack of crypto at build time. Reverted #1373 and added (6bead5d). + +# 4.0.4 +__fixed__ +- Fixed bug where Electron v4 breaks due to lack of `'rmd160'` alias for ripemd160 hash. (#1373) + # 4.0.3 __fixed__ - Fixed `TransactionBuilder` to require that the Transaction has outputs before signing (#1151) From 8ec1911a269a5a3839fd51b45dec38b2df43b38f Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sun, 7 Apr 2019 22:27:16 +0900 Subject: [PATCH 268/568] Fix tests Missing Input --- test/integration/_regtest.js | 143 +++++++++-------- test/integration/addresses.js | 19 +-- test/integration/bip32.js | 18 +-- test/integration/cltv.js | 260 ++++++++++++++----------------- test/integration/csv.js | 144 ++++++++--------- test/integration/payments.js | 44 +++--- test/integration/transactions.js | 228 ++++++++++++--------------- 7 files changed, 397 insertions(+), 459 deletions(-) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index f392a8a..650a79c 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -1,99 +1,109 @@ const assert = require('assert') const bitcoin = require('../../') -const dhttp = require('dhttp/200') +const dhttpCallback = require('dhttp/200') +// use Promises +const dhttp = options => new Promise((resolve, reject) => { + return dhttpCallback(options, (err, data) => { + if (err) return reject(err) + else return resolve(data) + }) +}) const APIPASS = process.env.APIPASS || 'satoshi' const APIURL = 'https://regtest.bitbank.cc/1' const NETWORK = bitcoin.networks.testnet -function broadcast (txHex, callback) { - dhttp({ +function broadcast (txHex) { + return dhttp({ method: 'POST', url: APIURL + '/t/push', body: txHex - }, callback) -} - -function mine (count, callback) { - dhttp({ - method: 'POST', - url: APIURL + '/r/generate?count=' + count + '&key=' + APIPASS - }, callback) -} - -function height (callback) { - dhttp({ - method: 'GET', - url: APIURL + '/b/best/height' - }, callback) -} - -function faucet (address, value, callback) { - dhttp({ - method: 'POST', - url: APIURL + '/r/faucet?address=' + address + '&value=' + value + '&key=' + APIPASS - }, function (err, txId) { - if (err) return callback(err) - - unspents(address, function (err, results) { - if (err) return callback(err) - - const unspents = results.filter(x => x.txId === txId) - if (unspents.length === 0) return callback(new Error('Missing unspent')) - - callback(null, unspents.pop()) - }) }) } -function faucetComplex (output, value, callback) { +function mine (count) { + return dhttp({ + method: 'POST', + url: APIURL + '/r/generate?count=' + count + '&key=' + APIPASS + }) +} + +function height () { + return dhttp({ + method: 'GET', + url: APIURL + '/b/best/height' + }) +} + +async function faucet (address, value) { + let count = 0 + let _unspents = [] + const sleep = ms => new Promise(r => setTimeout(r, ms)) + do { + if (count > 0) { + if (count >= 5) throw new Error('Missing Inputs') + console.log('Missing Inputs, retry #' + count) + await sleep(200) + } + + const txId = await dhttp({ + method: 'POST', + url: APIURL + '/r/faucet?address=' + address + '&value=' + value + '&key=' + APIPASS + }) + + await sleep(100) + + const results = await unspents(address) + + _unspents = results.filter(x => x.txId === txId) + + count++ + } while (_unspents.length === 0) + + return _unspents.pop() +} + +async function faucetComplex (output, value) { const keyPair = bitcoin.ECPair.makeRandom({ network: NETWORK }) const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: NETWORK }) - faucet(p2pkh.address, value * 2, (err, unspent) => { - if (err) return callback(err) + const unspent = await faucet(p2pkh.address, value * 2) - const txvb = new bitcoin.TransactionBuilder(NETWORK) - txvb.addInput(unspent.txId, unspent.vout, null, p2pkh.output) - txvb.addOutput(output, value) - txvb.sign(0, keyPair) - const txv = txvb.build() + const txvb = new bitcoin.TransactionBuilder(NETWORK) + txvb.addInput(unspent.txId, unspent.vout, null, p2pkh.output) + txvb.addOutput(output, value) + txvb.sign(0, keyPair) + const txv = txvb.build() - broadcast(txv.toHex(), function (err) { - if (err) return callback(err) + await broadcast(txv.toHex()) - return callback(null, { - txId: txv.getId(), - vout: 0, - value - }) - }) - }) + return { + txId: txv.getId(), + vout: 0, + value + } } -function fetch (txId, callback) { - dhttp({ +function fetch (txId) { + return dhttp({ method: 'GET', url: APIURL + '/t/' + txId + '/json' - }, callback) + }) } -function unspents (address, callback) { - dhttp({ +function unspents (address) { + return dhttp({ method: 'GET', url: APIURL + '/a/' + address + '/unspents' - }, callback) + }) } -function verify (txo, callback) { - fetch(txo.txId, function (err, tx) { - if (err) return callback(err) +async function verify (txo) { + const tx = await fetch(txo.txId) - const txoActual = tx.outs[txo.vout] - if (txo.address) assert.strictEqual(txoActual.address, txo.address) - if (txo.value) assert.strictEqual(txoActual.value, txo.value) - callback() - }) + const txoActual = tx.outs[txo.vout] + if (txo.address) assert.strictEqual(txoActual.address, txo.address) + if (txo.value) assert.strictEqual(txoActual.value, txo.value) } function getAddress (node, network) { @@ -108,6 +118,7 @@ function randomAddress () { module.exports = { broadcast, + dhttp, faucet, faucetComplex, fetch, diff --git a/test/integration/addresses.js b/test/integration/addresses.js index 7cf2612..f37dbc7 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -1,29 +1,26 @@ const { describe, it } = require('mocha') const assert = require('assert') const bitcoin = require('../../') -const dhttp = require('dhttp/200') +const dhttp = require('./_regtest').dhttp const TESTNET = bitcoin.networks.testnet describe('bitcoinjs-lib (addresses)', function () { - it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', function (done) { + it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', async function () { const keyPair = bitcoin.ECPair.makeRandom() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) // bitcoin P2PKH addresses start with a '1' assert.strictEqual(address.startsWith('1'), true) - dhttp({ + const result = await dhttp({ method: 'GET', url: 'https://blockchain.info/rawaddr/' + address - }, function (err, result) { - if (err) return done(err) - - // random private keys [probably!] have no transactions - assert.strictEqual(result.n_tx, 0) - assert.strictEqual(result.total_received, 0) - assert.strictEqual(result.total_sent, 0) - done() }) + + // random private keys [probably!] have no transactions + assert.strictEqual(result.n_tx, 0) + assert.strictEqual(result.total_received, 0) + assert.strictEqual(result.total_sent, 0) }) it('can import an address via WIF', function () { diff --git a/test/integration/bip32.js b/test/integration/bip32.js index 27a2da4..5f948fa 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -13,7 +13,7 @@ describe('bitcoinjs-lib (BIP32)', function () { const xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet) - assert.equal(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') + assert.strictEqual(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') }) it('can export a BIP32 xpriv, then import it', function () { @@ -23,8 +23,8 @@ describe('bitcoinjs-lib (BIP32)', function () { const string = node.toBase58() const restored = bip32.fromBase58(string) - assert.equal(getAddress(node), getAddress(restored)) // same public key - assert.equal(node.toWIF(), restored.toWIF()) // same private key + assert.strictEqual(getAddress(node), getAddress(restored)) // same public key + assert.strictEqual(node.toWIF(), restored.toWIF()) // same private key }) it('can export a BIP32 xpub', function () { @@ -33,7 +33,7 @@ describe('bitcoinjs-lib (BIP32)', function () { const node = bip32.fromSeed(seed) const string = node.neutered().toBase58() - assert.equal(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') + assert.strictEqual(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') }) it('can create a BIP32, bitcoin, account 0, external address', function () { @@ -47,8 +47,8 @@ describe('bitcoinjs-lib (BIP32)', function () { .derive(0) .derive(0) - assert.equal(getAddress(child1), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') - assert.equal(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') + assert.strictEqual(getAddress(child1), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') + assert.strictEqual(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') }) it('can create a BIP44, bitcoin, account 0, external address', function () { @@ -63,8 +63,8 @@ describe('bitcoinjs-lib (BIP32)', function () { .derive(0) .derive(0) - assert.equal(getAddress(child1), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') - assert.equal(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') + assert.strictEqual(getAddress(child1), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') + assert.strictEqual(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') }) it('can create a BIP49, bitcoin testnet, account 0, external address', function () { @@ -79,7 +79,7 @@ describe('bitcoinjs-lib (BIP32)', function () { redeem: bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network: bitcoin.networks.testnet }), network: bitcoin.networks.testnet }) - assert.equal(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2') + assert.strictEqual(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2') }) it('can use BIP39 to generate BIP32 addresses', function () { diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 8bf1c4a..66afb94 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -10,8 +10,8 @@ const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZs describe('bitcoinjs-lib (transactions w/ CLTV)', function () { // force update MTP - before(function (done) { - regtestUtils.mine(11, done) + before(async function () { + await regtestUtils.mine(11) }) const hashType = bitcoin.Transaction.SIGHASH_ALL @@ -38,188 +38,160 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { } // expiry past, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', function (done) { + it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', async function () { // 3 hours ago const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) // fund the P2SH(CLTV) address - regtestUtils.faucet(address, 1e5, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucet(address, 1e5) + const txb = new bitcoin.TransactionBuilder(regtest) + txb.setLockTime(lockTime) + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) - // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + // {Alice's signature} OP_TRUE + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]), + output: redeemScript + } + }).input + tx.setInputScript(0, redeemScriptSig) - // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.payments.p2sh({ - redeem: { - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) + await regtestUtils.broadcast(tx.toHex()) - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) - - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4 - }, done) - }) + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4 }) }) // expiry will pass, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', function (done) { - regtestUtils.height(function (err, height) { - if (err) return done(err) + it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async function () { + const height = await regtestUtils.height() + // 5 blocks from now + const lockTime = bip65.encode({ blocks: height + 5 }) + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) + const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) - // 5 blocks from now - const lockTime = bip65.encode({ blocks: height + 5 }) - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) + // fund the P2SH(CLTV) address + const unspent = await regtestUtils.faucet(address, 1e5) + const txb = new bitcoin.TransactionBuilder(regtest) + txb.setLockTime(lockTime) + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) - // fund the P2SH(CLTV) address - regtestUtils.faucet(address, 1e5, function (err, unspent) { - if (err) return done(err) + // {Alice's signature} OP_TRUE + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]), + output: redeemScript + } + }).input + tx.setInputScript(0, redeemScriptSig) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) - // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) - - // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.payments.p2sh({ - redeem: { - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) - - // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently - // ... - // into the future! - regtestUtils.mine(5, function (err) { - if (err) return done(err) - - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) - - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4 - }, done) - }) - }) - }) + // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently + // ... + // into the future! + await regtestUtils.mine(5) + await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4 }) }) // expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE - it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', function (done) { + it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', async function () { // two hours ago const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) // fund the P2SH(CLTV) address - regtestUtils.faucet(address, 2e5, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucet(address, 2e5) + const txb = new bitcoin.TransactionBuilder(regtest) + txb.setLockTime(lockTime) + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) - // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4) + // {Alice's signature} {Bob's signature} OP_FALSE + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_FALSE + ]), + output: redeemScript + } + }).input + tx.setInputScript(0, redeemScriptSig) - // {Alice's signature} {Bob's signature} OP_FALSE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.payments.p2sh({ - redeem: { - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_FALSE - ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) - - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) - - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 8e4 - }, done) - }) + await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 8e4 }) }) // expiry in the future, {Alice's signature} OP_TRUE - it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', function (done) { + it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async function () { // two hours from now const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) // fund the P2SH(CLTV) address - regtestUtils.faucet(address, 2e4, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucet(address, 2e4) + const txb = new bitcoin.TransactionBuilder(regtest) + txb.setLockTime(lockTime) + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) - // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + // {Alice's signature} OP_TRUE + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]), + output: redeemScript + } + }).input + tx.setInputScript(0, redeemScriptSig) - // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) - const redeemScriptSig = bitcoin.payments.p2sh({ - redeem: { - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) - - regtestUtils.broadcast(tx.toHex(), function (err) { - assert.throws(function () { - if (err) throw err - }, /Error: non-final \(code 64\)/) - - done() - }) + await regtestUtils.broadcast(tx.toHex()).catch(err => { + assert.throws(function () { + if (err) throw err + }, /Error: non-final \(code 64\)/) }) }) }) diff --git a/test/integration/csv.js b/test/integration/csv.js index 5996634..1ad8c90 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -10,8 +10,8 @@ const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZs describe('bitcoinjs-lib (transactions w/ CSV)', function () { // force update MTP - before(function (done) { - regtestUtils.mine(11, done) + before(async function () { + await regtestUtils.mine(11) }) const hashType = bitcoin.Transaction.SIGHASH_ALL @@ -35,66 +35,56 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { } // expiry will pass, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', function (done) { - regtestUtils.height(function (err, height) { - if (err) return done(err) + it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async function () { + // 5 blocks from now + const sequence = bip68.encode({ blocks: 5 }) + const p2sh = bitcoin.payments.p2sh({ + redeem: { + output: csvCheckSigOutput(alice, bob, sequence) + }, + network: regtest + }) - // 5 blocks from now - const sequence = bip68.encode({ blocks: 5 }) - const p2sh = bitcoin.payments.p2sh({ - redeem: { - output: csvCheckSigOutput(alice, bob, sequence) - }, - network: regtest - }) + // fund the P2SH(CSV) address + const unspent = await regtestUtils.faucet(p2sh.address, 1e5) - // fund the P2SH(CSV) address - regtestUtils.faucet(p2sh.address, 1e5, function (err, unspent) { - if (err) return done(err) + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, sequence) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + // {Alice's signature} OP_TRUE + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem.output, + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]) + } + }).input + tx.setInputScript(0, redeemScriptSig) - // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) - const redeemScriptSig = bitcoin.payments.p2sh({ - network: regtest, - redeem: { - network: regtest, - output: p2sh.redeem.output, - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently + // ... + // into the future! + await regtestUtils.mine(10) - // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently - // ... - // into the future! - regtestUtils.mine(10, function (err) { - if (err) return done(err) + await regtestUtils.broadcast(tx.toHex()) - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) - - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4 - }, done) - }) - }) - }) + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4 }) }) // expiry in the future, {Alice's signature} OP_TRUE - it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', function (done) { + it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async function () { // two hours after confirmation const sequence = bip68.encode({ seconds: 7168 }) const p2sh = bitcoin.payments.p2sh({ @@ -105,37 +95,33 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) // fund the P2SH(CSV) address - regtestUtils.faucet(p2sh.address, 2e4, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucet(p2sh.address, 2e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, sequence) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) - // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) - const redeemScriptSig = bitcoin.payments.p2sh({ + // {Alice's signature} OP_TRUE + const tx = txb.buildIncomplete() + const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { network: regtest, - redeem: { - network: regtest, - output: p2sh.redeem.output, - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + output: p2sh.redeem.output, + input: bitcoin.script.compile([ + bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE + ]) + } + }).input + tx.setInputScript(0, redeemScriptSig) - regtestUtils.broadcast(tx.toHex(), function (err) { - assert.throws(function () { - if (err) throw err - }, /Error: non-BIP68-final \(code 64\)/) - - done() - }) + await regtestUtils.broadcast(tx.toHex()).catch(err => { + assert.throws(function () { + if (err) throw err + }, /Error: non-BIP68-final \(code 64\)/) }) }) }) diff --git a/test/integration/payments.js b/test/integration/payments.js index a8fb84e..2fcdbda 100644 --- a/test/integration/payments.js +++ b/test/integration/payments.js @@ -8,24 +8,22 @@ const keyPairs = [ bitcoin.ECPair.makeRandom({ network: NETWORK }) ] -function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) { - regtestUtils.faucetComplex(prevOutput, 5e4, (err, unspent) => { - if (err) return done(err) +async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { + const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4) - const txb = new bitcoin.TransactionBuilder(NETWORK) - txb.addInput(unspent.txId, unspent.vout, null, prevOutput) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const txb = new bitcoin.TransactionBuilder(NETWORK) + txb.addInput(unspent.txId, unspent.vout, null, prevOutput) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - if (depends.signatures) { - keyPairs.forEach((keyPair) => { - txb.sign(0, keyPair, redeemScript, null, unspent.value, witnessScript) - }) - } else if (depends.signature) { - txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript) - } + if (depends.signatures) { + keyPairs.forEach((keyPair) => { + txb.sign(0, keyPair, redeemScript, null, unspent.value, witnessScript) + }) + } else if (depends.signature) { + txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript) + } - regtestUtils.broadcast(txb.build().toHex(), done) - }) + return regtestUtils.broadcast(txb.build().toHex()) } ;['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach((k) => { @@ -42,28 +40,28 @@ function buildAndSign (depends, prevOutput, redeemScript, witnessScript, done) { if (!output) throw new TypeError('Missing output') describe('bitcoinjs-lib (payments - ' + k + ')', function () { - it('can broadcast as an output, and be spent as an input', (done) => { - buildAndSign(depends, output, null, null, done) + it('can broadcast as an output, and be spent as an input', async () => { + await buildAndSign(depends, output, null, null) }) - it('can (as P2SH(' + k + ')) broadcast as an output, and be spent as an input', (done) => { + it('can (as P2SH(' + k + ')) broadcast as an output, and be spent as an input', async () => { const p2sh = bitcoin.payments.p2sh({ redeem: { output }, network: NETWORK }) - buildAndSign(depends, p2sh.output, p2sh.redeem.output, null, done) + await buildAndSign(depends, p2sh.output, p2sh.redeem.output, null) }) // NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail if (k === 'p2wpkh') return - it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', (done) => { + it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', async () => { const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) - buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output, done) + await buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output) }) - it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', (done) => { + it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', async () => { const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) const p2sh = bitcoin.payments.p2sh({ redeem: { output: p2wsh.output }, network: NETWORK }) - buildAndSign(depends, p2sh.output, p2sh.redeem.output, p2wsh.redeem.output, done) + await buildAndSign(depends, p2sh.output, p2sh.redeem.output, p2wsh.redeem.output) }) }) }) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index f725241..a3836a7 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -43,7 +43,7 @@ describe('bitcoinjs-lib (transactions)', function () { assert.strictEqual(txb.build().toHex(), '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000') }) - it('can create (and broadcast via 3PBP) a typical Transaction', function (done) { + it('can create (and broadcast via 3PBP) a typical Transaction', async () => { const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }) const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }) const aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng }) @@ -53,51 +53,45 @@ describe('bitcoinjs-lib (transactions)', function () { const aliceCpkh = bitcoin.payments.p2pkh({ pubkey: aliceChange.publicKey, network: regtest }) // give Alice 2 unspent outputs - regtestUtils.faucet(alice1pkh.address, 5e4, function (err, unspent0) { - if (err) return done(err) + const unspent0 = await regtestUtils.faucet(alice1pkh.address, 5e4) - regtestUtils.faucet(alice2pkh.address, 7e4, function (err, unspent1) { - if (err) return done(err) + const unspent1 = await regtestUtils.faucet(alice2pkh.address, 7e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent0.txId, unspent0.vout) // alice1 unspent - txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent - txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" - txb.addOutput(aliceCpkh.address, 1e4) // Alice's change - // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent0.txId, unspent0.vout) // alice1 unspent + txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent + txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" + txb.addOutput(aliceCpkh.address, 1e4) // Alice's change + // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee - // Alice signs each input with the respective private keys - txb.sign(0, alice1) - txb.sign(1, alice2) + // Alice signs each input with the respective private keys + txb.sign(0, alice1) + txb.sign(1, alice2) - // build and broadcast our RegTest network - regtestUtils.broadcast(txb.build().toHex(), done) - // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 - }) - }) + // build and broadcast our RegTest network + await regtestUtils.broadcast(txb.build().toHex()) + // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 }) - it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', function (done) { + it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => { const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: regtest }) - regtestUtils.faucet(p2pkh.address, 2e5, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucet(p2pkh.address, 2e5) - const txb = new bitcoin.TransactionBuilder(regtest) - const data = Buffer.from('bitcoinjs-lib', 'utf8') - const embed = bitcoin.payments.embed({ data: [data] }) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(embed.output, 1000) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) - txb.sign(0, keyPair) + const txb = new bitcoin.TransactionBuilder(regtest) + const data = Buffer.from('bitcoinjs-lib', 'utf8') + const embed = bitcoin.payments.embed({ data: [data] }) + txb.addInput(unspent.txId, unspent.vout) + txb.addOutput(embed.output, 1000) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) + txb.sign(0, keyPair) - // build and broadcast to the RegTest network - regtestUtils.broadcast(txb.build().toHex(), done) - }) + // build and broadcast to the RegTest network + await regtestUtils.broadcast(txb.build().toHex()) }) - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => { const keyPairs = [ bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), @@ -108,118 +102,102 @@ describe('bitcoinjs-lib (transactions)', function () { const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys: pubkeys, network: regtest }) const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest }) - regtestUtils.faucet(p2sh.address, 2e4, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucet(p2sh.address, 2e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) - txb.sign(0, keyPairs[0], p2sh.redeem.output) - txb.sign(0, keyPairs[2], p2sh.redeem.output) - const tx = txb.build() + txb.sign(0, keyPairs[0], p2sh.redeem.output) + txb.sign(0, keyPairs[2], p2sh.redeem.output) + const tx = txb.build() - // build and broadcast to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) + // build and broadcast to the Bitcoin RegTest network + await regtestUtils.broadcast(tx.toHex()) - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 1e4 - }, done) - }) + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 1e4 }) }) - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => { const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }) - regtestUtils.faucet(p2sh.address, 5e4, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucet(p2sh.address, 5e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - txb.sign(0, keyPair, p2sh.redeem.output, null, unspent.value) + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + txb.sign(0, keyPair, p2sh.redeem.output, null, unspent.value) - const tx = txb.build() + const tx = txb.build() - // build and broadcast to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) + // build and broadcast to the Bitcoin RegTest network + await regtestUtils.broadcast(tx.toHex()) - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 2e4 - }, done) - }) + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4 }) }) - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) - regtestUtils.faucetComplex(p2wpkh.address, 5e4, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucetComplex(p2wpkh.address, 5e4) - // XXX: build the Transaction w/ a P2WPKH input - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, null, p2wpkh.output) // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - txb.sign(0, keyPair, null, null, unspent.value) // NOTE: no redeem script - const tx = txb.build() + // XXX: build the Transaction w/ a P2WPKH input + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, null, p2wpkh.output) // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + txb.sign(0, keyPair, null, null, unspent.value) // NOTE: no redeem script + const tx = txb.build() - // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) + // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network + await regtestUtils.broadcast(tx.toHex()) - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 2e4 - }, done) - }) + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4 }) }) - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => { const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest }) const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }) - regtestUtils.faucetComplex(p2wsh.address, 5e4, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucetComplex(p2wsh.address, 5e4) - // XXX: build the Transaction w/ a P2WSH input - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, null, p2wsh.output) // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - txb.sign(0, keyPair, null, null, 5e4, p2wsh.redeem.output) // NOTE: provide a witnessScript! - const tx = txb.build() + // XXX: build the Transaction w/ a P2WSH input + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, null, p2wsh.output) // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + txb.sign(0, keyPair, null, null, 5e4, p2wsh.redeem.output) // NOTE: provide a witnessScript! + const tx = txb.build() - // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) + // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network + await regtestUtils.broadcast(tx.toHex()) - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 2e4 - }, done) - }) + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4 }) }) - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', function (done) { + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { const keyPairs = [ bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), @@ -232,29 +210,25 @@ describe('bitcoinjs-lib (transactions)', function () { const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest }) const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest }) - regtestUtils.faucet(p2sh.address, 6e4, function (err, unspent) { - if (err) return done(err) + const unspent = await regtestUtils.faucet(p2sh.address, 6e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, null, p2sh.output) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4) - txb.sign(0, keyPairs[0], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) - txb.sign(0, keyPairs[2], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) - txb.sign(0, keyPairs[3], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) + const txb = new bitcoin.TransactionBuilder(regtest) + txb.addInput(unspent.txId, unspent.vout, null, p2sh.output) + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4) + txb.sign(0, keyPairs[0], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) + txb.sign(0, keyPairs[2], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) + txb.sign(0, keyPairs[3], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) - const tx = txb.build() + const tx = txb.build() - // build and broadcast to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { - if (err) return done(err) + // build and broadcast to the Bitcoin RegTest network + await regtestUtils.broadcast(tx.toHex()) - regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 3e4 - }, done) - }) + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 3e4 }) }) From 77bd66c22ffd19944db561e6cb9cbcc756e0e668 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 8 Apr 2019 15:24:19 +0900 Subject: [PATCH 269/568] Fix Bad Request errors from the client side --- test/integration/_regtest.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 650a79c..81821c1 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -35,10 +35,17 @@ function height () { }) } +function _faucetRequest (address, value) { + return dhttp({ + method: 'POST', + url: APIURL + '/r/faucet?address=' + address + '&value=' + value + '&key=' + APIPASS + }) +} + async function faucet (address, value) { let count = 0 let _unspents = [] - const sleep = ms => new Promise(r => setTimeout(r, ms)) + const sleep = ms => new Promise((resolve, reject) => setTimeout(resolve, ms)) do { if (count > 0) { if (count >= 5) throw new Error('Missing Inputs') @@ -46,10 +53,20 @@ async function faucet (address, value) { await sleep(200) } - const txId = await dhttp({ - method: 'POST', - url: APIURL + '/r/faucet?address=' + address + '&value=' + value + '&key=' + APIPASS - }) + const txId = await _faucetRequest(address, value) + .then( + v => v, // Pass success value as is + async err => { + // Bad Request error is fixed by making sure height is >= 432 + const currentHeight = await height() + if (err.message === 'Bad Request' && currentHeight < 432) { + await mine(432 - currentHeight) + return _faucetRequest(address, value) + } else { + throw err + } + } + ) await sleep(100) From b27df612daaf2365d9d4fb3f5bd7fb6c5b0f9f80 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 8 Apr 2019 15:34:12 +0900 Subject: [PATCH 270/568] Randomize sleep times --- test/integration/_regtest.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 81821c1..d2a3c7d 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -46,11 +46,12 @@ async function faucet (address, value) { let count = 0 let _unspents = [] const sleep = ms => new Promise((resolve, reject) => setTimeout(resolve, ms)) + const randInt = (min, max) => min + Math.floor((max - min + 1) * Math.random()) do { if (count > 0) { if (count >= 5) throw new Error('Missing Inputs') console.log('Missing Inputs, retry #' + count) - await sleep(200) + await sleep(randInt(150, 250)) } const txId = await _faucetRequest(address, value) @@ -68,7 +69,7 @@ async function faucet (address, value) { } ) - await sleep(100) + await sleep(randInt(50, 150)) const results = await unspents(address) From d9fd6d619a58378e0bb6536928068602d33d5450 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 8 Apr 2019 16:40:38 +0900 Subject: [PATCH 271/568] Fix race condition for two integration test jobs --- test/integration/_regtest.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index d2a3c7d..0ea6736 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -63,6 +63,8 @@ async function faucet (address, value) { if (err.message === 'Bad Request' && currentHeight < 432) { await mine(432 - currentHeight) return _faucetRequest(address, value) + } else if (err.message === 'Bad Request' && currentHeight >= 432) { + return _faucetRequest(address, value) } else { throw err } From d951423a352178fa8f1c8bb6898da783b9b75a28 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 8 Apr 2019 18:15:25 +0900 Subject: [PATCH 272/568] Fix TransactionBuilder types --- ts_src/transaction_builder.ts | 18 +++++++++--------- types/transaction_builder.d.ts | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 2367250..6a49b3f 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -134,8 +134,8 @@ export class TransactionBuilder { addInput( txHash: Buffer | string | Transaction, vout: number, - sequence: number, - prevOutScript: Buffer, + sequence?: number, + prevOutScript?: Buffer, ): number { if (!this.__canModifyInputs()) { throw new Error('No, this would invalidate signatures'); @@ -188,10 +188,10 @@ export class TransactionBuilder { sign( vin: number, keyPair: ECPairInterface, - redeemScript: Buffer, - hashType: number, - witnessValue: number, - witnessScript: Buffer, + redeemScript?: Buffer, + hashType?: number, + witnessValue?: number, + witnessScript?: Buffer, ): void { // TODO: remove keyPair.network matching in 4.0.0 if (keyPair.network && keyPair.network !== this.network) @@ -267,7 +267,7 @@ export class TransactionBuilder { } const signature = keyPair.sign(signatureHash); - input.signatures![i] = bscript.signature.encode(signature, hashType); + input.signatures![i] = bscript.signature.encode(signature, hashType!); return true; }); @@ -679,8 +679,8 @@ function expandOutput(script: Buffer, ourPubKey?: Buffer): TxbOutput { function prepareInput( input: TxbInput, ourPubKey: Buffer, - redeemScript: Buffer, - witnessScript: Buffer, + redeemScript?: Buffer, + witnessScript?: Buffer, ): TxbInput { if (redeemScript && witnessScript) { const p2wsh = payments.p2wsh({ diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts index 16adee6..82c4ef0 100644 --- a/types/transaction_builder.d.ts +++ b/types/transaction_builder.d.ts @@ -12,11 +12,11 @@ export declare class TransactionBuilder { constructor(network?: Network, maximumFeeRate?: number); setLockTime(locktime: number): void; setVersion(version: number): void; - addInput(txHash: Buffer | string | Transaction, vout: number, sequence: number, prevOutScript: Buffer): number; + addInput(txHash: Buffer | string | Transaction, vout: number, sequence?: number, prevOutScript?: Buffer): number; addOutput(scriptPubKey: string | Buffer, value: number): number; build(): Transaction; buildIncomplete(): Transaction; - sign(vin: number, keyPair: ECPairInterface, redeemScript: Buffer, hashType: number, witnessValue: number, witnessScript: Buffer): void; + sign(vin: number, keyPair: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void; private __addInputUnsafe; private __build; private __canModifyInputs; From 2381b6643f1466518e36a2e16757f656c87968d3 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 8 Apr 2019 18:17:04 +0900 Subject: [PATCH 273/568] 5.0.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e56b8d2..b57448e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.0", + "version": "5.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 69d704f..10088cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.0", + "version": "5.0.1", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 16823e90131b7668575be9ada467a83a908b05e6 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Apr 2019 12:09:43 +0900 Subject: [PATCH 274/568] Add APIURL env for endpoint --- test/integration/_regtest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 0ea6736..e32e11b 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -10,7 +10,7 @@ const dhttp = options => new Promise((resolve, reject) => { }) const APIPASS = process.env.APIPASS || 'satoshi' -const APIURL = 'https://regtest.bitbank.cc/1' +const APIURL = process.env.APIURL || 'https://regtest.bitbank.cc/1' const NETWORK = bitcoin.networks.testnet function broadcast (txHex) { From dc1ef5958b7c7115e5c6960d6bccb2704f9c13cd Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Apr 2019 15:09:50 +0900 Subject: [PATCH 275/568] Tests to arrow functions, use strict asserts, travis uses docker instead of regtest server --- .travis.yml | 8 +- test/address.js | 64 +++++----- test/bitcoin.core.js | 78 ++++++------ test/block.js | 84 ++++++------- test/bufferutils.js | 26 ++-- test/classify.js | 42 +++---- test/crypto.js | 10 +- test/ecpair.js | 102 ++++++++-------- test/integration/_regtest.js | 4 +- test/integration/addresses.js | 20 ++-- test/integration/bip32.js | 16 +-- test/integration/blocks.js | 4 +- test/integration/cltv.js | 14 +-- test/integration/csv.js | 10 +- test/integration/payments.js | 6 +- test/integration/transactions.js | 16 +-- test/payments.js | 26 ++-- test/payments.utils.js | 13 +- test/script.js | 70 +++++------ test/script_number.js | 14 +-- test/script_signature.js | 28 ++--- test/transaction.js | 124 +++++++++---------- test/transaction_builder.js | 200 +++++++++++++++---------------- test/types.js | 24 ++-- 24 files changed, 505 insertions(+), 498 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5c1cfd3..7921fac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,11 @@ sudo: false language: node_js +services: + - docker +before_install: + - docker pull junderw/bitcoinjs-regtest-server + - docker run -d -p 127.0.0.1:8080:8080 junderw/bitcoinjs-regtest-server + - docker ps -a node_js: - "8" - "lts/*" @@ -15,5 +21,5 @@ matrix: env: TEST_SUITE=coverage env: - TEST_SUITE=unit - - TEST_SUITE=integration + - TEST_SUITE=integration APIURL=http://127.0.0.1:8080/1 script: npm run-script $TEST_SUITE diff --git a/test/address.js b/test/address.js index a0f4df0..be16879 100644 --- a/test/address.js +++ b/test/address.js @@ -16,12 +16,12 @@ const NETWORKS = Object.assign({ } }, require('../src/networks')) -describe('address', function () { - describe('fromBase58Check', function () { - fixtures.standard.forEach(function (f) { +describe('address', () => { + describe('fromBase58Check', () => { + fixtures.standard.forEach(f => { if (!f.base58check) return - it('decodes ' + f.base58check, function () { + it('decodes ' + f.base58check, () => { const decode = baddress.fromBase58Check(f.base58check) assert.strictEqual(decode.version, f.version) @@ -29,20 +29,20 @@ describe('address', function () { }) }) - fixtures.invalid.fromBase58Check.forEach(function (f) { - it('throws on ' + f.exception, function () { - assert.throws(function () { + fixtures.invalid.fromBase58Check.forEach(f => { + it('throws on ' + f.exception, () => { + assert.throws(() => { baddress.fromBase58Check(f.address) }, new RegExp(f.address + ' ' + f.exception)) }) }) }) - describe('fromBech32', function () { - fixtures.standard.forEach((f) => { + describe('fromBech32', () => { + fixtures.standard.forEach(f => { if (!f.bech32) return - it('decodes ' + f.bech32, function () { + it('decodes ' + f.bech32, () => { const actual = baddress.fromBech32(f.bech32) assert.strictEqual(actual.version, f.version) @@ -52,17 +52,17 @@ describe('address', function () { }) fixtures.invalid.bech32.forEach((f, i) => { - it('decode fails for ' + f.bech32 + '(' + f.exception + ')', function () { - assert.throws(function () { + it('decode fails for ' + f.bech32 + '(' + f.exception + ')', () => { + assert.throws(() => { baddress.fromBech32(f.address) }, new RegExp(f.exception)) }) }) }) - describe('fromOutputScript', function () { - fixtures.standard.forEach(function (f) { - it('encodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', function () { + describe('fromOutputScript', () => { + fixtures.standard.forEach(f => { + it('encodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', () => { const script = bscript.fromASM(f.script) const address = baddress.fromOutputScript(script, NETWORKS[f.network]) @@ -70,22 +70,22 @@ describe('address', function () { }) }) - fixtures.invalid.fromOutputScript.forEach(function (f) { - it('throws when ' + f.script.slice(0, 30) + '... ' + f.exception, function () { + fixtures.invalid.fromOutputScript.forEach(f => { + it('throws when ' + f.script.slice(0, 30) + '... ' + f.exception, () => { const script = bscript.fromASM(f.script) - assert.throws(function () { + assert.throws(() => { baddress.fromOutputScript(script) }, new RegExp(f.exception)) }) }) }) - describe('toBase58Check', function () { - fixtures.standard.forEach(function (f) { + describe('toBase58Check', () => { + fixtures.standard.forEach(f => { if (!f.base58check) return - it('encodes ' + f.hash + ' (' + f.network + ')', function () { + it('encodes ' + f.hash + ' (' + f.network + ')', () => { const address = baddress.toBase58Check(Buffer.from(f.hash, 'hex'), f.version) assert.strictEqual(address, f.base58check) @@ -93,39 +93,39 @@ describe('address', function () { }) }) - describe('toBech32', function () { + describe('toBech32', () => { fixtures.bech32.forEach((f, i) => { if (!f.bech32) return const data = Buffer.from(f.data, 'hex') - it('encode ' + f.address, function () { - assert.deepEqual(baddress.toBech32(data, f.version, f.prefix), f.address) + it('encode ' + f.address, () => { + assert.deepStrictEqual(baddress.toBech32(data, f.version, f.prefix), f.address) }) }) fixtures.invalid.bech32.forEach((f, i) => { if (!f.prefix || f.version === undefined || f.data === undefined) return - it('encode fails (' + f.exception, function () { - assert.throws(function () { + it('encode fails (' + f.exception, () => { + assert.throws(() => { baddress.toBech32(Buffer.from(f.data, 'hex'), f.version, f.prefix) }, new RegExp(f.exception)) }) }) }) - describe('toOutputScript', function () { - fixtures.standard.forEach(function (f) { - it('decodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', function () { + describe('toOutputScript', () => { + fixtures.standard.forEach(f => { + it('decodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', () => { const script = baddress.toOutputScript(f.base58check || f.bech32, NETWORKS[f.network]) assert.strictEqual(bscript.toASM(script), f.script) }) }) - fixtures.invalid.toOutputScript.forEach(function (f) { - it('throws when ' + f.exception, function () { - assert.throws(function () { + fixtures.invalid.toOutputScript.forEach(f => { + it('throws when ' + f.exception, () => { + assert.throws(() => { baddress.toOutputScript(f.address, f.network) }, new RegExp(f.address + ' ' + f.exception)) }) diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index 560bf20..734c9a9 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -12,21 +12,21 @@ const sigHash = require('./fixtures/core/sighash.json') const sigNoncanonical = require('./fixtures/core/sig_noncanonical.json') const txValid = require('./fixtures/core/tx_valid.json') -describe('Bitcoin-core', function () { +describe('Bitcoin-core', () => { // base58EncodeDecode - describe('base58', function () { - base58EncodeDecode.forEach(function (f) { + describe('base58', () => { + base58EncodeDecode.forEach(f => { const fhex = f[0] const fb58 = f[1] - it('can decode ' + fb58, function () { + it('can decode ' + fb58, () => { const buffer = base58.decode(fb58) const actual = buffer.toString('hex') assert.strictEqual(actual, fhex) }) - it('can encode ' + fhex, function () { + it('can encode ' + fhex, () => { const buffer = Buffer.from(fhex, 'hex') const actual = base58.encode(buffer) @@ -36,13 +36,13 @@ describe('Bitcoin-core', function () { }) // base58KeysValid - describe('address.toBase58Check', function () { + describe('address.toBase58Check', () => { const typeMap = { 'pubkey': 'pubKeyHash', 'script': 'scriptHash' } - base58KeysValid.forEach(function (f) { + base58KeysValid.forEach(f => { const expected = f[0] const hash = Buffer.from(f[1], 'hex') const params = f[2] @@ -52,14 +52,14 @@ describe('Bitcoin-core', function () { const network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin const version = network[typeMap[params.addrType]] - it('can export ' + expected, function () { + it('can export ' + expected, () => { assert.strictEqual(bitcoin.address.toBase58Check(hash, version), expected) }) }) }) // base58KeysInvalid - describe('address.fromBase58Check', function () { + describe('address.fromBase58Check', () => { const allowedNetworks = [ bitcoin.networks.bitcoin.pubkeyhash, bitcoin.networks.bitcoin.scripthash, @@ -67,22 +67,22 @@ describe('Bitcoin-core', function () { bitcoin.networks.testnet.scripthash ] - base58KeysInvalid.forEach(function (f) { + base58KeysInvalid.forEach(f => { const string = f[0] - it('throws on ' + string, function () { - assert.throws(function () { + it('throws on ' + string, () => { + assert.throws(() => { const address = bitcoin.address.fromBase58Check(string) - assert.notEqual(allowedNetworks.indexOf(address.version), -1, 'Invalid network') + assert.notStrictEqual(allowedNetworks.indexOf(address.version), -1, 'Invalid network') }, /(Invalid (checksum|network))|(too (short|long))/) }) }) }) // base58KeysValid - describe('ECPair', function () { - base58KeysValid.forEach(function (f) { + describe('ECPair', () => { + base58KeysValid.forEach(f => { const string = f[0] const hex = f[1] const params = f[2] @@ -92,38 +92,38 @@ describe('Bitcoin-core', function () { const network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin const keyPair = bitcoin.ECPair.fromWIF(string, network) - it('fromWIF imports ' + string, function () { + it('fromWIF imports ' + string, () => { assert.strictEqual(keyPair.privateKey.toString('hex'), hex) assert.strictEqual(keyPair.compressed, params.isCompressed) }) - it('toWIF exports ' + hex + ' to ' + string, function () { + it('toWIF exports ' + hex + ' to ' + string, () => { assert.strictEqual(keyPair.toWIF(), string) }) }) }) // base58KeysInvalid - describe('ECPair.fromWIF', function () { + describe('ECPair.fromWIF', () => { const allowedNetworks = [ bitcoin.networks.bitcoin, bitcoin.networks.testnet ] - base58KeysInvalid.forEach(function (f) { + base58KeysInvalid.forEach(f => { const string = f[0] - it('throws on ' + string, function () { - assert.throws(function () { + it('throws on ' + string, () => { + assert.throws(() => { bitcoin.ECPair.fromWIF(string, allowedNetworks) }, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/) }) }) }) - describe('Block.fromHex', function () { - blocksValid.forEach(function (f) { - it('can parse ' + f.id, function () { + describe('Block.fromHex', () => { + blocksValid.forEach(f => { + it('can parse ' + f.id, () => { const block = bitcoin.Block.fromHex(f.hex) assert.strictEqual(block.getId(), f.id) @@ -133,8 +133,8 @@ describe('Bitcoin-core', function () { }) // txValid - describe('Transaction.fromHex', function () { - txValid.forEach(function (f) { + describe('Transaction.fromHex', () => { + txValid.forEach(f => { // Objects that are only a single string are ignored if (f.length === 1) return @@ -142,17 +142,17 @@ describe('Bitcoin-core', function () { const fhex = f[1] // const verifyFlags = f[2] // TODO: do we need to test this? - it('can decode ' + fhex, function () { + it('can decode ' + fhex, () => { const transaction = bitcoin.Transaction.fromHex(fhex) - transaction.ins.forEach(function (txIn, i) { + transaction.ins.forEach((txIn, i) => { const input = inputs[i] // reverse because test data is reversed const prevOutHash = Buffer.from(input[0], 'hex').reverse() const prevOutIndex = input[1] - assert.deepEqual(txIn.hash, prevOutHash) + assert.deepStrictEqual(txIn.hash, prevOutHash) // we read UInt32, not Int32 assert.strictEqual(txIn.index & 0xffffffff, prevOutIndex) @@ -162,8 +162,8 @@ describe('Bitcoin-core', function () { }) // sighash - describe('Transaction', function () { - sigHash.forEach(function (f) { + describe('Transaction', () => { + sigHash.forEach(f => { // Objects that are only a single string are ignored if (f.length === 1) return @@ -181,7 +181,7 @@ describe('Bitcoin-core', function () { const hashTypeName = hashTypes.join(' | ') - it('should hash ' + txHex.slice(0, 40) + '... (' + hashTypeName + ')', function () { + it('should hash ' + txHex.slice(0, 40) + '... (' + hashTypeName + ')', () => { const transaction = bitcoin.Transaction.fromHex(txHex) assert.strictEqual(transaction.toHex(), txHex) @@ -192,16 +192,16 @@ describe('Bitcoin-core', function () { const hash = transaction.hashForSignature(inIndex, script, hashType) // reverse because test data is reversed - assert.equal(hash.reverse().toString('hex'), expectedHash) + assert.strictEqual(hash.reverse().toString('hex'), expectedHash) }) }) }) - describe('script.signature.decode', function () { - sigCanonical.forEach(function (hex) { + describe('script.signature.decode', () => { + sigCanonical.forEach(hex => { const buffer = Buffer.from(hex, 'hex') - it('can parse ' + hex, function () { + it('can parse ' + hex, () => { const parsed = bitcoin.script.signature.decode(buffer) const actual = bitcoin.script.signature.encode(parsed.signature, parsed.hashType) @@ -209,15 +209,15 @@ describe('Bitcoin-core', function () { }) }) - sigNoncanonical.forEach(function (hex, i) { + sigNoncanonical.forEach((hex, i) => { if (i === 0) return if (i % 2 !== 0) return const description = sigNoncanonical[i - 1].slice(0, -1) const buffer = Buffer.from(hex, 'hex') - it('throws on ' + description, function () { - assert.throws(function () { + it('throws on ' + description, () => { + assert.throws(() => { bitcoin.script.signature.decode(buffer) }, /Expected DER (integer|sequence)|(R|S) value (excessively padded|is negative)|(R|S|DER sequence) length is (zero|too short|too long|invalid)|Invalid hashType/) }) diff --git a/test/block.js b/test/block.js index e646737..d5e41af 100644 --- a/test/block.js +++ b/test/block.js @@ -4,29 +4,29 @@ const Block = require('..').Block const fixtures = require('./fixtures/block') -describe('Block', function () { - describe('version', function () { - it('should be interpreted as an int32le', function () { +describe('Block', () => { + describe('version', () => { + it('should be interpreted as an int32le', () => { const blockHex = 'ffffffff0000000000000000000000000000000000000000000000000000000000000000414141414141414141414141414141414141414141414141414141414141414101000000020000000300000000' const block = Block.fromHex(blockHex) - assert.equal(-1, block.version) - assert.equal(1, block.timestamp) + assert.strictEqual(-1, block.version) + assert.strictEqual(1, block.timestamp) }) }) - describe('calculateTarget', function () { - fixtures.targets.forEach(function (f) { - it('returns ' + f.expected + ' for 0x' + f.bits, function () { + describe('calculateTarget', () => { + fixtures.targets.forEach(f => { + it('returns ' + f.expected + ' for 0x' + f.bits, () => { const bits = parseInt(f.bits, 16) - assert.equal(Block.calculateTarget(bits).toString('hex'), f.expected) + assert.strictEqual(Block.calculateTarget(bits).toString('hex'), f.expected) }) }) }) - describe('fromBuffer/fromHex', function () { - fixtures.valid.forEach(function (f) { - it('imports ' + f.description, function () { + describe('fromBuffer/fromHex', () => { + fixtures.valid.forEach(f => { + it('imports ' + f.description, () => { const block = Block.fromHex(f.hex) assert.strictEqual(block.version, f.version) @@ -42,54 +42,54 @@ describe('Block', function () { }) }) - fixtures.invalid.forEach(function (f) { - it('throws on ' + f.exception, function () { - assert.throws(function () { + fixtures.invalid.forEach(f => { + it('throws on ' + f.exception, () => { + assert.throws(() => { Block.fromHex(f.hex) }, new RegExp(f.exception)) }) }) }) - describe('toBuffer/toHex', function () { - fixtures.valid.forEach(function (f) { + describe('toBuffer/toHex', () => { + fixtures.valid.forEach(f => { let block - beforeEach(function () { + beforeEach(() => { block = Block.fromHex(f.hex) }) - it('exports ' + f.description, function () { + it('exports ' + f.description, () => { assert.strictEqual(block.toHex(true), f.hex.slice(0, 160)) assert.strictEqual(block.toHex(), f.hex) }) }) }) - describe('getHash/getId', function () { - fixtures.valid.forEach(function (f) { + describe('getHash/getId', () => { + fixtures.valid.forEach(f => { let block - beforeEach(function () { + beforeEach(() => { block = Block.fromHex(f.hex) }) - it('returns ' + f.id + ' for ' + f.description, function () { + it('returns ' + f.id + ' for ' + f.description, () => { assert.strictEqual(block.getHash().toString('hex'), f.hash) assert.strictEqual(block.getId(), f.id) }) }) }) - describe('getUTCDate', function () { - fixtures.valid.forEach(function (f) { + describe('getUTCDate', () => { + fixtures.valid.forEach(f => { let block - beforeEach(function () { + beforeEach(() => { block = Block.fromHex(f.hex) }) - it('returns UTC date of ' + f.id, function () { + it('returns UTC date of ' + f.id, () => { const utcDate = block.getUTCDate().getTime() assert.strictEqual(utcDate, f.timestamp * 1e3) @@ -97,59 +97,59 @@ describe('Block', function () { }) }) - describe('calculateMerkleRoot', function () { - it('should throw on zero-length transaction array', function () { - assert.throws(function () { + describe('calculateMerkleRoot', () => { + it('should throw on zero-length transaction array', () => { + assert.throws(() => { Block.calculateMerkleRoot([]) }, /Cannot compute merkle root for zero transactions/) }) - fixtures.valid.forEach(function (f) { + fixtures.valid.forEach(f => { if (f.hex.length === 160) return let block - beforeEach(function () { + beforeEach(() => { block = Block.fromHex(f.hex) }) - it('returns ' + f.merkleRoot + ' for ' + f.id, function () { + it('returns ' + f.merkleRoot + ' for ' + f.id, () => { assert.strictEqual(Block.calculateMerkleRoot(block.transactions).toString('hex'), f.merkleRoot) }) if (f.witnessCommit) { - it('returns witness commit ' + f.witnessCommit + ' for ' + f.id, function () { + it('returns witness commit ' + f.witnessCommit + ' for ' + f.id, () => { assert.strictEqual(Block.calculateMerkleRoot(block.transactions, true).toString('hex'), f.witnessCommit) }) } }) }) - describe('checkTxRoots', function () { - fixtures.valid.forEach(function (f) { + describe('checkTxRoots', () => { + fixtures.valid.forEach(f => { if (f.hex.length === 160) return let block - beforeEach(function () { + beforeEach(() => { block = Block.fromHex(f.hex) }) - it('returns ' + f.valid + ' for ' + f.id, function () { + it('returns ' + f.valid + ' for ' + f.id, () => { assert.strictEqual(block.checkTxRoots(), true) }) }) }) - describe('checkProofOfWork', function () { - fixtures.valid.forEach(function (f) { + describe('checkProofOfWork', () => { + fixtures.valid.forEach(f => { let block - beforeEach(function () { + beforeEach(() => { block = Block.fromHex(f.hex) }) - it('returns ' + f.valid + ' for ' + f.id, function () { + it('returns ' + f.valid + ' for ' + f.id, () => { assert.strictEqual(block.checkProofOfWork(), f.valid) }) }) diff --git a/test/bufferutils.js b/test/bufferutils.js index 5f2c39e..1a38406 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -4,10 +4,10 @@ const bufferutils = require('../src/bufferutils') const fixtures = require('./fixtures/bufferutils.json') -describe('bufferutils', function () { - describe('readUInt64LE', function () { - fixtures.valid.forEach(function (f) { - it('decodes ' + f.hex, function () { +describe('bufferutils', () => { + describe('readUInt64LE', () => { + fixtures.valid.forEach(f => { + it('decodes ' + f.hex, () => { const buffer = Buffer.from(f.hex, 'hex') const number = bufferutils.readUInt64LE(buffer, 0) @@ -15,20 +15,20 @@ describe('bufferutils', function () { }) }) - fixtures.invalid.readUInt64LE.forEach(function (f) { - it('throws on ' + f.description, function () { + fixtures.invalid.readUInt64LE.forEach(f => { + it('throws on ' + f.description, () => { const buffer = Buffer.from(f.hex, 'hex') - assert.throws(function () { + assert.throws(() => { bufferutils.readUInt64LE(buffer, 0) }, new RegExp(f.exception)) }) }) }) - describe('writeUInt64LE', function () { - fixtures.valid.forEach(function (f) { - it('encodes ' + f.dec, function () { + describe('writeUInt64LE', () => { + fixtures.valid.forEach(f => { + it('encodes ' + f.dec, () => { const buffer = Buffer.alloc(8, 0) bufferutils.writeUInt64LE(buffer, f.dec, 0) @@ -36,11 +36,11 @@ describe('bufferutils', function () { }) }) - fixtures.invalid.readUInt64LE.forEach(function (f) { - it('throws on ' + f.description, function () { + fixtures.invalid.readUInt64LE.forEach(f => { + it('throws on ' + f.description, () => { const buffer = Buffer.alloc(8, 0) - assert.throws(function () { + assert.throws(() => { bufferutils.writeUInt64LE(buffer, f.dec, 0) }, new RegExp(f.exception)) }) diff --git a/test/classify.js b/test/classify.js index 3efcc74..86da74d 100644 --- a/test/classify.js +++ b/test/classify.js @@ -25,12 +25,12 @@ const tmap = { witnessCommitment } -describe('classify', function () { - describe('input', function () { - fixtures.valid.forEach(function (f) { +describe('classify', () => { + describe('input', () => { + fixtures.valid.forEach(f => { if (!f.input) return - it('classifies ' + f.input + ' as ' + f.type, function () { + it('classifies ' + f.input + ' as ' + f.type, () => { const input = bscript.fromASM(f.input) const type = classify.input(input) @@ -38,11 +38,11 @@ describe('classify', function () { }) }) - fixtures.valid.forEach(function (f) { + fixtures.valid.forEach(f => { if (!f.input) return if (!f.typeIncomplete) return - it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, function () { + it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, () => { const input = bscript.fromASM(f.input) const type = classify.input(input, true) @@ -51,11 +51,11 @@ describe('classify', function () { }) }) - describe('classifyOutput', function () { - fixtures.valid.forEach(function (f) { + describe('classifyOutput', () => { + fixtures.valid.forEach(f => { if (!f.output) return - it('classifies ' + f.output + ' as ' + f.type, function () { + it('classifies ' + f.output + ' as ' + f.type, () => { const output = bscript.fromASM(f.output) const type = classify.output(output) @@ -73,12 +73,12 @@ describe('classify', function () { 'multisig', 'nullData', 'witnessCommitment' - ].forEach(function (name) { + ].forEach(name => { const inputType = tmap[name].input const outputType = tmap[name].output - describe(name + '.input.check', function () { - fixtures.valid.forEach(function (f) { + describe(name + '.input.check', () => { + fixtures.valid.forEach(f => { if (name.toLowerCase() === classify.types.P2WPKH) return if (name.toLowerCase() === classify.types.P2WSH) return const expected = name.toLowerCase() === f.type.toLowerCase() @@ -86,14 +86,14 @@ describe('classify', function () { if (inputType && f.input) { const input = bscript.fromASM(f.input) - it('returns ' + expected + ' for ' + f.input, function () { + it('returns ' + expected + ' for ' + f.input, () => { assert.strictEqual(inputType.check(input), expected) }) if (f.typeIncomplete) { const expectedIncomplete = name.toLowerCase() === f.typeIncomplete - it('returns ' + expected + ' for ' + f.input, function () { + it('returns ' + expected + ' for ' + f.input, () => { assert.strictEqual(inputType.check(input, true), expectedIncomplete) }) } @@ -102,10 +102,10 @@ describe('classify', function () { if (!(fixtures.invalid[name])) return - fixtures.invalid[name].inputs.forEach(function (f) { + fixtures.invalid[name].inputs.forEach(f => { if (!f.input && !f.inputHex) return - it('returns false for ' + f.description + ' (' + (f.input || f.inputHex) + ')', function () { + it('returns false for ' + f.description + ' (' + (f.input || f.inputHex) + ')', () => { let input if (f.input) { @@ -119,12 +119,12 @@ describe('classify', function () { }) }) - describe(name + '.output.check', function () { - fixtures.valid.forEach(function (f) { + describe(name + '.output.check', () => { + fixtures.valid.forEach(f => { const expected = name.toLowerCase() === f.type if (outputType && f.output) { - it('returns ' + expected + ' for ' + f.output, function () { + it('returns ' + expected + ' for ' + f.output, () => { const output = bscript.fromASM(f.output) if (name.toLowerCase() === 'nulldata' && f.type === classify.types.WITNESS_COMMITMENT) return @@ -136,10 +136,10 @@ describe('classify', function () { if (!(fixtures.invalid[name])) return - fixtures.invalid[name].outputs.forEach(function (f) { + fixtures.invalid[name].outputs.forEach(f => { if (!f.output && !f.outputHex) return - it('returns false for ' + f.description + ' (' + (f.output || f.outputHex) + ')', function () { + it('returns false for ' + f.description + ' (' + (f.output || f.outputHex) + ')', () => { let output if (f.output) { diff --git a/test/crypto.js b/test/crypto.js index 3f7802a..14b4f33 100644 --- a/test/crypto.js +++ b/test/crypto.js @@ -4,14 +4,14 @@ const bcrypto = require('../src/crypto') const fixtures = require('./fixtures/crypto') -describe('crypto', function () { - ['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(function (algorithm) { - describe(algorithm, function () { - fixtures.forEach(function (f) { +describe('crypto', () => { + ['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(algorithm => { + describe(algorithm, () => { + fixtures.forEach(f => { const fn = bcrypto[algorithm] const expected = f[algorithm] - it('returns ' + expected + ' for ' + f.hex, function () { + it('returns ' + expected + ' for ' + f.hex, () => { const data = Buffer.from(f.hex, 'hex') const actual = fn(data).toString('hex') diff --git a/test/ecpair.js b/test/ecpair.js index de5c485..ce32683 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -19,15 +19,15 @@ const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000 const GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 'hex') const GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 'hex') -describe('ECPair', function () { - describe('getPublicKey', function () { +describe('ECPair', () => { + describe('getPublicKey', () => { let keyPair - beforeEach(function () { + beforeEach(() => { keyPair = ECPair.fromPrivateKey(ONE) }) - it('calls pointFromScalar lazily', hoodwink(function () { + it('calls pointFromScalar lazily', hoodwink(() => { assert.strictEqual(keyPair.__Q, undefined) // .publicKey forces the memoization @@ -36,14 +36,14 @@ describe('ECPair', function () { })) }) - describe('fromPrivateKey', function () { - it('defaults to compressed', function () { + describe('fromPrivateKey', () => { + it('defaults to compressed', () => { const keyPair = ECPair.fromPrivateKey(ONE) assert.strictEqual(keyPair.compressed, true) }) - it('supports the uncompressed option', function () { + it('supports the uncompressed option', () => { const keyPair = ECPair.fromPrivateKey(ONE, { compressed: false }) @@ -51,7 +51,7 @@ describe('ECPair', function () { assert.strictEqual(keyPair.compressed, false) }) - it('supports the network option', function () { + it('supports the network option', () => { const keyPair = ECPair.fromPrivateKey(ONE, { compressed: false, network: NETWORKS.testnet @@ -60,8 +60,8 @@ describe('ECPair', function () { assert.strictEqual(keyPair.network, NETWORKS.testnet) }) - fixtures.valid.forEach(function (f) { - it('derives public key for ' + f.WIF, function () { + fixtures.valid.forEach(f => { + it('derives public key for ' + f.WIF, () => { const d = Buffer.from(f.d, 'hex') const keyPair = ECPair.fromPrivateKey(d, { compressed: f.compressed @@ -71,30 +71,30 @@ describe('ECPair', function () { }) }) - fixtures.invalid.fromPrivateKey.forEach(function (f) { - it('throws ' + f.exception, function () { + fixtures.invalid.fromPrivateKey.forEach(f => { + it('throws ' + f.exception, () => { const d = Buffer.from(f.d, 'hex') - assert.throws(function () { + assert.throws(() => { ECPair.fromPrivateKey(d, f.options) }, new RegExp(f.exception)) }) }) }) - describe('fromPublicKey', function () { - fixtures.invalid.fromPublicKey.forEach(function (f) { - it('throws ' + f.exception, function () { + describe('fromPublicKey', () => { + fixtures.invalid.fromPublicKey.forEach(f => { + it('throws ' + f.exception, () => { const Q = Buffer.from(f.Q, 'hex') - assert.throws(function () { + assert.throws(() => { ECPair.fromPublicKey(Q, f.options) }, new RegExp(f.exception)) }) }) }) - describe('fromWIF', function () { - fixtures.valid.forEach(function (f) { - it('imports ' + f.WIF + ' (' + f.network + ')', function () { + describe('fromWIF', () => { + fixtures.valid.forEach(f => { + it('imports ' + f.WIF + ' (' + f.network + ')', () => { const network = NETWORKS[f.network] const keyPair = ECPair.fromWIF(f.WIF, network) @@ -104,8 +104,8 @@ describe('ECPair', function () { }) }) - fixtures.valid.forEach(function (f) { - it('imports ' + f.WIF + ' (via list of networks)', function () { + fixtures.valid.forEach(f => { + it('imports ' + f.WIF + ' (via list of networks)', () => { const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) assert.strictEqual(keyPair.privateKey.toString('hex'), f.d) @@ -114,9 +114,9 @@ describe('ECPair', function () { }) }) - fixtures.invalid.fromWIF.forEach(function (f) { - it('throws on ' + f.WIF, function () { - assert.throws(function () { + fixtures.invalid.fromWIF.forEach(f => { + it('throws on ' + f.WIF, () => { + assert.throws(() => { const networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST ECPair.fromWIF(f.WIF, networks) @@ -125,9 +125,9 @@ describe('ECPair', function () { }) }) - describe('toWIF', function () { - fixtures.valid.forEach(function (f) { - it('exports ' + f.WIF, function () { + describe('toWIF', () => { + fixtures.valid.forEach(f => { + it('exports ' + f.WIF, () => { const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) const result = keyPair.toWIF() assert.strictEqual(result, f.WIF) @@ -135,13 +135,13 @@ describe('ECPair', function () { }) }) - describe('makeRandom', function () { + describe('makeRandom', () => { const d = Buffer.alloc(32, 4) const exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' - describe('uses randombytes RNG', function () { - it('generates a ECPair', function () { - const stub = { randombytes: function () { return d } } + describe('uses randombytes RNG', () => { + it('generates a ECPair', () => { + const stub = { randombytes: () => { return d } } const ProxiedECPair = proxyquire('../src/ecpair', stub) const keyPair = ProxiedECPair.makeRandom() @@ -149,22 +149,22 @@ describe('ECPair', function () { }) }) - it('allows a custom RNG to be used', function () { + it('allows a custom RNG to be used', () => { const keyPair = ECPair.makeRandom({ - rng: function (size) { return d.slice(0, size) } + rng: size => { return d.slice(0, size) } }) assert.strictEqual(keyPair.toWIF(), exWIF) }) - it('retains the same defaults as ECPair constructor', function () { + it('retains the same defaults as ECPair constructor', () => { const keyPair = ECPair.makeRandom() assert.strictEqual(keyPair.compressed, true) assert.strictEqual(keyPair.network, NETWORKS.bitcoin) }) - it('supports the options parameter', function () { + it('supports the options parameter', () => { const keyPair = ECPair.makeRandom({ compressed: false, network: NETWORKS.testnet @@ -174,18 +174,18 @@ describe('ECPair', function () { assert.strictEqual(keyPair.network, NETWORKS.testnet) }) - it('throws if d is bad length', function () { + it('throws if d is bad length', () => { function rng () { return Buffer.alloc(28) } - assert.throws(function () { + assert.throws(() => { ECPair.makeRandom({ rng: rng }) }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/) }) it('loops until d is within interval [1, n) : 1', hoodwink(function () { - const rng = this.stub(function () { + const rng = this.stub(() => { if (rng.calls === 0) return ZERO // 0 return ONE // >0 }, 2) @@ -194,7 +194,7 @@ describe('ECPair', function () { })) it('loops until d is within interval [1, n) : n - 1', hoodwink(function () { - const rng = this.stub(function () { + const rng = this.stub(() => { if (rng.calls === 0) return ZERO // <1 if (rng.calls === 1) return GROUP_ORDER // >n-1 return GROUP_ORDER_LESS_1 // n-1 @@ -204,9 +204,9 @@ describe('ECPair', function () { })) }) - describe('.network', function () { - fixtures.valid.forEach(function (f) { - it('returns ' + f.network + ' for ' + f.WIF, function () { + describe('.network', () => { + fixtures.valid.forEach(f => { + it('returns ' + f.network + ' for ' + f.WIF, () => { const network = NETWORKS[f.network] const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) @@ -215,20 +215,20 @@ describe('ECPair', function () { }) }) - describe('tinysecp wrappers', function () { + describe('tinysecp wrappers', () => { let keyPair let hash let signature - beforeEach(function () { + beforeEach(() => { keyPair = ECPair.makeRandom() hash = ZERO signature = Buffer.alloc(64, 1) }) - describe('signing', function () { + describe('signing', () => { it('wraps tinysecp.sign', hoodwink(function () { - this.mock(tinysecp, 'sign', function (h, d) { + this.mock(tinysecp, 'sign', (h, d) => { assert.strictEqual(h, hash) assert.strictEqual(d, keyPair.privateKey) return signature @@ -237,18 +237,18 @@ describe('ECPair', function () { assert.strictEqual(keyPair.sign(hash), signature) })) - it('throws if no private key is found', function () { + it('throws if no private key is found', () => { delete keyPair.__D - assert.throws(function () { + assert.throws(() => { keyPair.sign(hash) }, /Missing private key/) }) }) - describe('verify', function () { + describe('verify', () => { it('wraps tinysecp.verify', hoodwink(function () { - this.mock(tinysecp, 'verify', function (h, q, s) { + this.mock(tinysecp, 'verify', (h, q, s) => { assert.strictEqual(h, hash) assert.strictEqual(q, keyPair.publicKey) assert.strictEqual(s, signature) diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index e32e11b..abfee1a 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -47,7 +47,7 @@ async function faucet (address, value) { let _unspents = [] const sleep = ms => new Promise((resolve, reject) => setTimeout(resolve, ms)) const randInt = (min, max) => min + Math.floor((max - min + 1) * Math.random()) - do { + while (_unspents.length === 0) { if (count > 0) { if (count >= 5) throw new Error('Missing Inputs') console.log('Missing Inputs, retry #' + count) @@ -78,7 +78,7 @@ async function faucet (address, value) { _unspents = results.filter(x => x.txId === txId) count++ - } while (_unspents.length === 0) + } return _unspents.pop() } diff --git a/test/integration/addresses.js b/test/integration/addresses.js index f37dbc7..1d28020 100644 --- a/test/integration/addresses.js +++ b/test/integration/addresses.js @@ -4,8 +4,8 @@ const bitcoin = require('../../') const dhttp = require('./_regtest').dhttp const TESTNET = bitcoin.networks.testnet -describe('bitcoinjs-lib (addresses)', function () { - it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', async function () { +describe('bitcoinjs-lib (addresses)', () => { + it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', async () => { const keyPair = bitcoin.ECPair.makeRandom() const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) @@ -23,14 +23,14 @@ describe('bitcoinjs-lib (addresses)', function () { assert.strictEqual(result.total_sent, 0) }) - it('can import an address via WIF', function () { + it('can import an address via WIF', () => { const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) assert.strictEqual(address, '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH') }) - it('can generate a P2SH, pay-to-multisig (2-of-3) address', function () { + it('can generate a P2SH, pay-to-multisig (2-of-3) address', () => { const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', @@ -43,14 +43,14 @@ describe('bitcoinjs-lib (addresses)', function () { assert.strictEqual(address, '36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7') }) - it('can generate a SegWit address', function () { + it('can generate a SegWit address', () => { const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) assert.strictEqual(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4') }) - it('can generate a SegWit address (via P2SH)', function () { + it('can generate a SegWit address (via P2SH)', () => { const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') const { address } = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) @@ -59,7 +59,7 @@ describe('bitcoinjs-lib (addresses)', function () { assert.strictEqual(address, '3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN') }) - it('can generate a P2WSH (SegWit), pay-to-multisig (3-of-4) address', function () { + it('can generate a P2WSH (SegWit), pay-to-multisig (3-of-4) address', () => { const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', @@ -73,7 +73,7 @@ describe('bitcoinjs-lib (addresses)', function () { assert.strictEqual(address, 'bc1q75f6dv4q8ug7zhujrsp5t0hzf33lllnr3fe7e2pra3v24mzl8rrqtp3qul') }) - it('can generate a P2SH(P2WSH(...)), pay-to-multisig (2-of-2) address', function () { + it('can generate a P2SH(P2WSH(...)), pay-to-multisig (2-of-2) address', () => { const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9' @@ -88,7 +88,7 @@ describe('bitcoinjs-lib (addresses)', function () { }) // examples using other network information - it('can generate a Testnet address', function () { + it('can generate a Testnet address', () => { const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET }) const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: TESTNET }) @@ -96,7 +96,7 @@ describe('bitcoinjs-lib (addresses)', function () { assert.strictEqual(address.startsWith('m') || address.startsWith('n'), true) }) - it('can generate a Litecoin address', function () { + it('can generate a Litecoin address', () => { // WARNING: although possible, bitcoinjs is NOT necessarily compatible with Litecoin const LITECOIN = { messagePrefix: '\x19Litecoin Signed Message:\n', diff --git a/test/integration/bip32.js b/test/integration/bip32.js index 5f948fa..255669c 100644 --- a/test/integration/bip32.js +++ b/test/integration/bip32.js @@ -8,15 +8,15 @@ function getAddress (node, network) { return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address } -describe('bitcoinjs-lib (BIP32)', function () { - it('can import a BIP32 testnet xpriv and export to WIF', function () { +describe('bitcoinjs-lib (BIP32)', () => { + it('can import a BIP32 testnet xpriv and export to WIF', () => { const xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet) assert.strictEqual(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') }) - it('can export a BIP32 xpriv, then import it', function () { + it('can export a BIP32 xpriv, then import it', () => { const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' const seed = bip39.mnemonicToSeed(mnemonic) const node = bip32.fromSeed(seed) @@ -27,7 +27,7 @@ describe('bitcoinjs-lib (BIP32)', function () { assert.strictEqual(node.toWIF(), restored.toWIF()) // same private key }) - it('can export a BIP32 xpub', function () { + it('can export a BIP32 xpub', () => { const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' const seed = bip39.mnemonicToSeed(mnemonic) const node = bip32.fromSeed(seed) @@ -36,7 +36,7 @@ describe('bitcoinjs-lib (BIP32)', function () { assert.strictEqual(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') }) - it('can create a BIP32, bitcoin, account 0, external address', function () { + it('can create a BIP32, bitcoin, account 0, external address', () => { const path = "m/0'/0/0" const root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) @@ -51,7 +51,7 @@ describe('bitcoinjs-lib (BIP32)', function () { assert.strictEqual(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') }) - it('can create a BIP44, bitcoin, account 0, external address', function () { + it('can create a BIP44, bitcoin, account 0, external address', () => { const root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) const child1 = root.derivePath("m/44'/0'/0'/0/0") @@ -67,7 +67,7 @@ describe('bitcoinjs-lib (BIP32)', function () { assert.strictEqual(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') }) - it('can create a BIP49, bitcoin testnet, account 0, external address', function () { + it('can create a BIP49, bitcoin testnet, account 0, external address', () => { const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' const seed = bip39.mnemonicToSeed(mnemonic) const root = bip32.fromSeed(seed) @@ -82,7 +82,7 @@ describe('bitcoinjs-lib (BIP32)', function () { assert.strictEqual(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2') }) - it('can use BIP39 to generate BIP32 addresses', function () { + it('can use BIP39 to generate BIP32 addresses', () => { // var mnemonic = bip39.generateMnemonic() const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' assert(bip39.validateMnemonic(mnemonic)) diff --git a/test/integration/blocks.js b/test/integration/blocks.js index 915fd65..044862d 100644 --- a/test/integration/blocks.js +++ b/test/integration/blocks.js @@ -4,8 +4,8 @@ const { describe, it } = require('mocha') const assert = require('assert') const bitcoin = require('../../') -describe('bitcoinjs-lib (blocks)', function () { - it('can extract a height from a CoinBase transaction', function () { +describe('bitcoinjs-lib (blocks)', () => { + it('can extract a height from a CoinBase transaction', () => { // from 00000000000000000097669cdca131f24d40c4cc7d80eaa65967a2d09acf6ce6 const txHex = '010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98fd16761d220400000000000000aa340000d49f0000ffffffff02b07fc366000000001976a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12bf1e400120000000000000000000000000000000000000000000000000000000000000000000000000' const tx = bitcoin.Transaction.fromHex(txHex) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 66afb94..b2fd478 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -8,9 +8,9 @@ const bip65 = require('bip65') const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) -describe('bitcoinjs-lib (transactions w/ CLTV)', function () { +describe('bitcoinjs-lib (transactions w/ CLTV)', () => { // force update MTP - before(async function () { + before(async () => { await regtestUtils.mine(11) }) @@ -38,7 +38,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { } // expiry past, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', async function () { + it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', async () => { // 3 hours ago const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) @@ -77,7 +77,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { }) // expiry will pass, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async function () { + it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async () => { const height = await regtestUtils.height() // 5 blocks from now const lockTime = bip65.encode({ blocks: height + 5 }) @@ -120,7 +120,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { }) // expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE - it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', async function () { + it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', async () => { // two hours ago const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) @@ -159,7 +159,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { }) // expiry in the future, {Alice's signature} OP_TRUE - it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async function () { + it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async () => { // two hours from now const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) @@ -189,7 +189,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { tx.setInputScript(0, redeemScriptSig) await regtestUtils.broadcast(tx.toHex()).catch(err => { - assert.throws(function () { + assert.throws(() => { if (err) throw err }, /Error: non-final \(code 64\)/) }) diff --git a/test/integration/csv.js b/test/integration/csv.js index 1ad8c90..8833412 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -8,9 +8,9 @@ const bip68 = require('bip68') const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) -describe('bitcoinjs-lib (transactions w/ CSV)', function () { +describe('bitcoinjs-lib (transactions w/ CSV)', () => { // force update MTP - before(async function () { + before(async () => { await regtestUtils.mine(11) }) @@ -35,7 +35,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { } // expiry will pass, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async function () { + it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async () => { // 5 blocks from now const sequence = bip68.encode({ blocks: 5 }) const p2sh = bitcoin.payments.p2sh({ @@ -84,7 +84,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { }) // expiry in the future, {Alice's signature} OP_TRUE - it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async function () { + it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async () => { // two hours after confirmation const sequence = bip68.encode({ seconds: 7168 }) const p2sh = bitcoin.payments.p2sh({ @@ -119,7 +119,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () { tx.setInputScript(0, redeemScriptSig) await regtestUtils.broadcast(tx.toHex()).catch(err => { - assert.throws(function () { + assert.throws(() => { if (err) throw err }, /Error: non-BIP68-final \(code 64\)/) }) diff --git a/test/integration/payments.js b/test/integration/payments.js index 2fcdbda..66b0a13 100644 --- a/test/integration/payments.js +++ b/test/integration/payments.js @@ -16,7 +16,7 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) if (depends.signatures) { - keyPairs.forEach((keyPair) => { + keyPairs.forEach(keyPair => { txb.sign(0, keyPair, redeemScript, null, unspent.value, witnessScript) }) } else if (depends.signature) { @@ -26,7 +26,7 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { return regtestUtils.broadcast(txb.build().toHex()) } -;['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach((k) => { +;['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => { const fixtures = require('../fixtures/' + k) const { depends } = fixtures.dynamic const fn = bitcoin.payments[k] @@ -39,7 +39,7 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { const { output } = fn(base) if (!output) throw new TypeError('Missing output') - describe('bitcoinjs-lib (payments - ' + k + ')', function () { + describe('bitcoinjs-lib (payments - ' + k + ')', () => { it('can broadcast as an output, and be spent as an input', async () => { await buildAndSign(depends, output, null, null) }) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index a3836a7..c096b87 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -8,8 +8,8 @@ function rng () { return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64') } -describe('bitcoinjs-lib (transactions)', function () { - it('can create a 1-to-1 Transaction', function () { +describe('bitcoinjs-lib (transactions)', () => { + it('can create a 1-to-1 Transaction', () => { const alice = bitcoin.ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') const txb = new bitcoin.TransactionBuilder() @@ -24,7 +24,7 @@ describe('bitcoinjs-lib (transactions)', function () { assert.strictEqual(txb.build().toHex(), '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000') }) - it('can create a 2-to-2 Transaction', function () { + it('can create a 2-to-2 Transaction', () => { const alice = bitcoin.ECPair.fromWIF('L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1') const bob = bitcoin.ECPair.fromWIF('KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z') @@ -232,17 +232,17 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) - it('can verify Transaction (P2PKH) signatures', function () { + it('can verify Transaction (P2PKH) signatures', () => { const txHex = '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700' const keyPairs = [ '032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d', '0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a', '039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f' - ].map(function (q) { return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')) }) + ].map(q => { return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')) }) const tx = bitcoin.Transaction.fromHex(txHex) - tx.ins.forEach(function (input, i) { + tx.ins.forEach((input, i) => { const keyPair = keyPairs[i] const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, @@ -256,7 +256,7 @@ describe('bitcoinjs-lib (transactions)', function () { }) }) - it('can verify Transaction (P2SH(P2WPKH)) signatures', function () { + it('can verify Transaction (P2SH(P2WPKH)) signatures', () => { const utxos = { 'f72d1d83ac40fcedd01415751556a905844ab5f44bbb7728565ebb91b1590109:0': { value: 50000 @@ -266,7 +266,7 @@ describe('bitcoinjs-lib (transactions)', function () { const txHex = '02000000000101090159b191bb5e562877bb4bf4b54a8405a95615751514d0edfc40ac831d2df7000000001716001435a179e5516947a39ae9c8a25e9fe62c0fc598edffffffff01204e0000000000001976a91431d43308d3c886d53e9ae8a45728370571ff456988ac0247304402206ec41f685b997a51f325b07ee852e82a535f6b52ef54485cc133e05168aa052a022070bafa86108acb51c77b2b259ae8fb7fd1efa10fef804fcfe9b13c2db719acf5012103fb03e9d0a9af86cbed94225dbb8bb70f6b82109bce0a61ddcf41dab6cbb4871100000000' const tx = bitcoin.Transaction.fromHex(txHex) - tx.ins.forEach(function (input, i) { + tx.ins.forEach((input, i) => { const txId = Buffer.from(input.hash).reverse().toString('hex') const utxo = utxos[`${txId}:${i}`] if (!utxo) throw new Error('Missing utxo') diff --git a/test/payments.js b/test/payments.js index a4f07bb..5619cdb 100644 --- a/test/payments.js +++ b/test/payments.js @@ -2,8 +2,8 @@ const { describe, it } = require('mocha') const assert = require('assert') const u = require('./payments.utils') -;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(function (p) { - describe(p, function () { +;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(p => { + describe(p, () => { let fn let payment = require('../src/payments/' + p) if (p === 'embed') { @@ -13,15 +13,15 @@ const u = require('./payments.utils') } const fixtures = require('./fixtures/' + p) - fixtures.valid.forEach(function (f, i) { - it(f.description + ' as expected', function () { + fixtures.valid.forEach((f, i) => { + it(f.description + ' as expected', () => { const args = u.preform(f.arguments) const actual = fn(args, f.options) u.equate(actual, f.expected, f.arguments) }) - it(f.description + ' as expected (no validation)', function () { + it(f.description + ' as expected (no validation)', () => { const args = u.preform(f.arguments) const actual = fn(args, Object.assign({}, f.options, { validate: false @@ -31,11 +31,11 @@ const u = require('./payments.utils') }) }) - fixtures.invalid.forEach(function (f) { - it('throws ' + f.exception + (f.description ? ('for ' + f.description) : ''), function () { + fixtures.invalid.forEach(f => { + it('throws ' + f.exception + (f.description ? ('for ' + f.description) : ''), () => { const args = u.preform(f.arguments) - assert.throws(function () { + assert.throws(() => { fn(args, f.options) }, new RegExp(f.exception)) }) @@ -45,23 +45,23 @@ const u = require('./payments.utils') if (!fixtures.dynamic) return const { depends, details } = fixtures.dynamic - details.forEach(function (f) { + details.forEach(f => { const detail = u.preform(f) const disabled = {} - if (f.disabled) f.disabled.forEach(function (k) { disabled[k] = true }) + if (f.disabled) f.disabled.forEach(k => { disabled[k] = true }) for (let key in depends) { if (key in disabled) continue const dependencies = depends[key] - dependencies.forEach(function (dependency) { + dependencies.forEach(dependency => { if (!Array.isArray(dependency)) dependency = [dependency] const args = {} - dependency.forEach(function (d) { u.from(d, detail, args) }) + dependency.forEach(d => { u.from(d, detail, args) }) const expected = u.from(key, detail) - it(f.description + ', ' + key + ' derives from ' + JSON.stringify(dependency), function () { + it(f.description + ', ' + key + ' derives from ' + JSON.stringify(dependency), () => { u.equate(fn(args), expected) }) }) diff --git a/test/payments.utils.js b/test/payments.utils.js index 485bf03..15414c4 100644 --- a/test/payments.utils.js +++ b/test/payments.utils.js @@ -39,7 +39,7 @@ function carryOver (a, b) { function equateBase (a, b, context) { if ('output' in b) t.strictEqual(tryASM(a.output), tryASM(b.output), `Inequal ${context}output`) if ('input' in b) t.strictEqual(tryASM(a.input), tryASM(b.input), `Inequal ${context}input`) - if ('witness' in b) t.deepEqual(tryHex(a.witness), tryHex(b.witness), `Inequal ${context}witness`) + if ('witness' in b) t.deepStrictEqual(tryHex(a.witness), tryHex(b.witness), `Inequal ${context}witness`) } function equate (a, b, args) { @@ -58,19 +58,20 @@ function equate (a, b, args) { equateBase(a, b, '') if (b.redeem) equateBase(a.redeem, b.redeem, 'redeem.') - if (b.network) t.deepEqual(a.network, BNETWORKS[b.network], 'Inequal *.network') + if (b.network) t.deepStrictEqual(a.network, BNETWORKS[b.network], 'Inequal *.network') // contextual if (b.signature === null) b.signature = undefined + if (b.signatures === null) b.signatures = undefined if ('address' in b) t.strictEqual(a.address, b.address, 'Inequal *.address') if ('hash' in b) t.strictEqual(tryHex(a.hash), tryHex(b.hash), 'Inequal *.hash') if ('pubkey' in b) t.strictEqual(tryHex(a.pubkey), tryHex(b.pubkey), 'Inequal *.pubkey') if ('signature' in b) t.strictEqual(tryHex(a.signature), tryHex(b.signature), 'Inequal signature') if ('m' in b) t.strictEqual(a.m, b.m, 'Inequal *.m') if ('n' in b) t.strictEqual(a.n, b.n, 'Inequal *.n') - if ('pubkeys' in b) t.deepEqual(tryHex(a.pubkeys), tryHex(b.pubkeys), 'Inequal *.pubkeys') - if ('signatures' in b) t.deepEqual(tryHex(a.signatures), tryHex(b.signatures), 'Inequal *.signatures') - if ('data' in b) t.deepEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data') + if ('pubkeys' in b) t.deepStrictEqual(tryHex(a.pubkeys), tryHex(b.pubkeys), 'Inequal *.pubkeys') + if ('signatures' in b) t.deepStrictEqual(tryHex(a.signatures), tryHex(b.signatures), 'Inequal *.signatures') + if ('data' in b) t.deepStrictEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data') } function preform (x) { @@ -94,7 +95,7 @@ function preform (x) { if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex') if (x.signature) x.signature = Buffer.from(x.signature, 'hex') if (x.pubkeys) x.pubkeys = x.pubkeys.map(fromHex) - if (x.signatures) x.signatures = x.signatures.map(function (y) { return Number.isFinite(y) ? y : Buffer.from(y, 'hex') }) + if (x.signatures) x.signatures = x.signatures.map(y => { return Number.isFinite(y) ? y : Buffer.from(y, 'hex') }) if (x.redeem) { x.redeem = Object.assign({}, x.redeem) if (typeof x.redeem.input === 'string') x.redeem.input = asmToBuffer(x.redeem.input) diff --git a/test/script.js b/test/script.js index c2a60ad..d003558 100644 --- a/test/script.js +++ b/test/script.js @@ -6,44 +6,44 @@ const minimalData = require('minimaldata') const fixtures = require('./fixtures/script.json') const fixtures2 = require('./fixtures/templates.json') -describe('script', function () { +describe('script', () => { // TODO - describe('isCanonicalPubKey', function () { - it('rejects if not provided a Buffer', function () { + describe('isCanonicalPubKey', () => { + it('rejects if not provided a Buffer', () => { assert.strictEqual(false, bscript.isCanonicalPubKey(0)) }) - it('rejects smaller than 33', function () { + it('rejects smaller than 33', () => { for (var i = 0; i < 33; i++) { assert.strictEqual(false, bscript.isCanonicalPubKey(Buffer.from('', i))) } }) }) - describe.skip('isCanonicalScriptSignature', function () { + describe.skip('isCanonicalScriptSignature', () => { }) - describe('fromASM/toASM', function () { - fixtures.valid.forEach(function (f) { - it('encodes/decodes ' + f.asm, function () { + describe('fromASM/toASM', () => { + fixtures.valid.forEach(f => { + it('encodes/decodes ' + f.asm, () => { const script = bscript.fromASM(f.asm) assert.strictEqual(bscript.toASM(script), f.asm) }) }) - fixtures.invalid.fromASM.forEach(function (f) { - it('throws ' + f.description, function () { - assert.throws(function () { + fixtures.invalid.fromASM.forEach(f => { + it('throws ' + f.description, () => { + assert.throws(() => { bscript.fromASM(f.script) }, new RegExp(f.description)) }) }) }) - describe('fromASM/toASM (templates)', function () { - fixtures2.valid.forEach(function (f) { + describe('fromASM/toASM (templates)', () => { + fixtures2.valid.forEach(f => { if (f.inputHex) { const ih = bscript.toASM(Buffer.from(f.inputHex, 'hex')) - it('encodes/decodes ' + ih, function () { + it('encodes/decodes ' + ih, () => { const script = bscript.fromASM(f.input) assert.strictEqual(script.toString('hex'), f.inputHex) assert.strictEqual(bscript.toASM(script), f.input) @@ -51,7 +51,7 @@ describe('script', function () { } if (f.outputHex) { - it('encodes/decodes ' + f.output, function () { + it('encodes/decodes ' + f.output, () => { const script = bscript.fromASM(f.output) assert.strictEqual(script.toString('hex'), f.outputHex) assert.strictEqual(bscript.toASM(script), f.output) @@ -60,9 +60,9 @@ describe('script', function () { }) }) - describe('isPushOnly', function () { - fixtures.valid.forEach(function (f) { - it('returns ' + !!f.stack + ' for ' + f.asm, function () { + describe('isPushOnly', () => { + fixtures.valid.forEach(f => { + it('returns ' + !!f.stack + ' for ' + f.asm, () => { const script = bscript.fromASM(f.asm) const chunks = bscript.decompile(script) @@ -71,26 +71,26 @@ describe('script', function () { }) }) - describe('toStack', function () { - fixtures.valid.forEach(function (f) { - it('returns ' + !!f.stack + ' for ' + f.asm, function () { + describe('toStack', () => { + fixtures.valid.forEach(f => { + it('returns ' + !!f.stack + ' for ' + f.asm, () => { if (!f.stack || !f.asm) return const script = bscript.fromASM(f.asm) const stack = bscript.toStack(script) - assert.deepEqual(stack.map(function (x) { + assert.deepStrictEqual(stack.map(x => { return x.toString('hex') }), f.stack) - assert.equal(bscript.toASM(bscript.compile(stack)), f.asm, 'should rebuild same script from stack') + assert.strictEqual(bscript.toASM(bscript.compile(stack)), f.asm, 'should rebuild same script from stack') }) }) }) - describe('compile (via fromASM)', function () { - fixtures.valid.forEach(function (f) { - it('(' + f.type + ') compiles ' + f.asm, function () { + describe('compile (via fromASM)', () => { + fixtures.valid.forEach(f => { + it('(' + f.type + ') compiles ' + f.asm, () => { const scriptSig = bscript.fromASM(f.asm) assert.strictEqual(scriptSig.toString('hex'), f.script) @@ -104,9 +104,9 @@ describe('script', function () { }) }) - describe('decompile', function () { - fixtures.valid.forEach(function (f) { - it('decompiles ' + f.asm, function () { + describe('decompile', () => { + fixtures.valid.forEach(f => { + it('decompiles ' + f.asm, () => { const chunks = bscript.decompile(Buffer.from(f.script, 'hex')) assert.strictEqual(bscript.compile(chunks).toString('hex'), f.script) @@ -123,8 +123,8 @@ describe('script', function () { }) }) - fixtures.invalid.decompile.forEach(function (f) { - it('fails to decompile ' + f.script + ', because "' + f.description + '"', function () { + fixtures.invalid.decompile.forEach(f => { + it('fails to decompile ' + f.script + ', because "' + f.description + '"', () => { const chunks = bscript.decompile(Buffer.from(f.script, 'hex')) assert.strictEqual(chunks, null) @@ -132,9 +132,9 @@ describe('script', function () { }) }) - describe('SCRIPT_VERIFY_MINIMALDATA policy', function () { - fixtures.valid.forEach(function (f) { - it('compliant for ' + f.type + ' scriptSig ' + f.asm, function () { + describe('SCRIPT_VERIFY_MINIMALDATA policy', () => { + fixtures.valid.forEach(f => { + it('compliant for ' + f.type + ' scriptSig ' + f.asm, () => { const script = Buffer.from(f.script, 'hex') assert(minimalData(script)) @@ -142,7 +142,7 @@ describe('script', function () { }) function testEncodingForSize (i) { - it('compliant for data PUSH of length ' + i, function () { + it('compliant for data PUSH of length ' + i, () => { const buffer = Buffer.alloc(i) const script = bscript.compile([buffer]) diff --git a/test/script_number.js b/test/script_number.js index bc8f395..d5b97e2 100644 --- a/test/script_number.js +++ b/test/script_number.js @@ -3,10 +3,10 @@ const assert = require('assert') const scriptNumber = require('../src/script_number') const fixtures = require('./fixtures/script_number.json') -describe('script-number', function () { - describe('decode', function () { - fixtures.forEach(function (f) { - it(f.hex + ' returns ' + f.number, function () { +describe('script-number', () => { + describe('decode', () => { + fixtures.forEach(f => { + it(f.hex + ' returns ' + f.number, () => { const actual = scriptNumber.decode(Buffer.from(f.hex, 'hex'), f.bytes) assert.strictEqual(actual, f.number) @@ -14,9 +14,9 @@ describe('script-number', function () { }) }) - describe('encode', function () { - fixtures.forEach(function (f) { - it(f.number + ' returns ' + f.hex, function () { + describe('encode', () => { + fixtures.forEach(f => { + it(f.number + ' returns ' + f.hex, () => { const actual = scriptNumber.encode(f.number) assert.strictEqual(actual.toString('hex'), f.hex) diff --git a/test/script_signature.js b/test/script_signature.js index cee69bd..6888ca5 100644 --- a/test/script_signature.js +++ b/test/script_signature.js @@ -4,7 +4,7 @@ const bscriptSig = require('../src/script').signature const Buffer = require('safe-buffer').Buffer const fixtures = require('./fixtures/signature.json') -describe('Script Signatures', function () { +describe('Script Signatures', () => { function fromRaw (signature) { return Buffer.concat([ Buffer.from(signature.r, 'hex'), @@ -19,43 +19,43 @@ describe('Script Signatures', function () { } } - describe('encode', function () { - fixtures.valid.forEach(function (f) { - it('encodes ' + f.hex, function () { + describe('encode', () => { + fixtures.valid.forEach(f => { + it('encodes ' + f.hex, () => { const buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType) assert.strictEqual(buffer.toString('hex'), f.hex) }) }) - fixtures.invalid.forEach(function (f) { + fixtures.invalid.forEach(f => { if (!f.raw) return - it('throws ' + f.exception, function () { + it('throws ' + f.exception, () => { const signature = fromRaw(f.raw) - assert.throws(function () { + assert.throws(() => { bscriptSig.encode(signature, f.hashType) }, new RegExp(f.exception)) }) }) }) - describe('decode', function () { - fixtures.valid.forEach(function (f) { - it('decodes ' + f.hex, function () { + describe('decode', () => { + fixtures.valid.forEach(f => { + it('decodes ' + f.hex, () => { const decode = bscriptSig.decode(Buffer.from(f.hex, 'hex')) - assert.deepEqual(toRaw(decode.signature), f.raw) + assert.deepStrictEqual(toRaw(decode.signature), f.raw) assert.strictEqual(decode.hashType, f.hashType) }) }) - fixtures.invalid.forEach(function (f) { - it('throws on ' + f.hex, function () { + fixtures.invalid.forEach(f => { + it('throws on ' + f.hex, () => { const buffer = Buffer.from(f.hex, 'hex') - assert.throws(function () { + assert.throws(() => { bscriptSig.decode(buffer) }, new RegExp(f.exception)) }) diff --git a/test/transaction.js b/test/transaction.js index d923ce2..3fa9243 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -4,13 +4,13 @@ const bscript = require('../src/script') const fixtures = require('./fixtures/transaction') const Transaction = require('..').Transaction -describe('Transaction', function () { +describe('Transaction', () => { function fromRaw (raw, noWitness) { const tx = new Transaction() tx.version = raw.version tx.locktime = raw.locktime - raw.ins.forEach(function (txIn, i) { + raw.ins.forEach((txIn, i) => { const txHash = Buffer.from(txIn.hash, 'hex') let scriptSig @@ -23,7 +23,7 @@ describe('Transaction', function () { tx.addInput(txHash, txIn.index, txIn.sequence, scriptSig) if (!noWitness && txIn.witness) { - const witness = txIn.witness.map(function (x) { + const witness = txIn.witness.map(x => { return Buffer.from(x, 'hex') }) @@ -31,7 +31,7 @@ describe('Transaction', function () { } }) - raw.outs.forEach(function (txOut) { + raw.outs.forEach(txOut => { let script if (txOut.data) { @@ -46,19 +46,19 @@ describe('Transaction', function () { return tx } - describe('fromBuffer/fromHex', function () { + describe('fromBuffer/fromHex', () => { function importExport (f) { const id = f.id || f.hash const txHex = f.hex || f.txHex - it('imports ' + f.description + ' (' + id + ')', function () { + it('imports ' + f.description + ' (' + id + ')', () => { const actual = Transaction.fromHex(txHex) assert.strictEqual(actual.toHex(), txHex) }) if (f.whex) { - it('imports ' + f.description + ' (' + id + ') as witness', function () { + it('imports ' + f.description + ' (' + id + ') as witness', () => { const actual = Transaction.fromHex(f.whex) assert.strictEqual(actual.toHex(), f.whex) @@ -70,38 +70,38 @@ describe('Transaction', function () { fixtures.hashForSignature.forEach(importExport) fixtures.hashForWitnessV0.forEach(importExport) - fixtures.invalid.fromBuffer.forEach(function (f) { - it('throws on ' + f.exception, function () { - assert.throws(function () { + fixtures.invalid.fromBuffer.forEach(f => { + it('throws on ' + f.exception, () => { + assert.throws(() => { Transaction.fromHex(f.hex) }, new RegExp(f.exception)) }) }) - it('.version should be interpreted as an int32le', function () { + it('.version should be interpreted as an int32le', () => { const txHex = 'ffffffff0000ffffffff' const tx = Transaction.fromHex(txHex) - assert.equal(-1, tx.version) - assert.equal(0xffffffff, tx.locktime) + assert.strictEqual(-1, tx.version) + assert.strictEqual(0xffffffff, tx.locktime) }) }) - describe('toBuffer/toHex', function () { - fixtures.valid.forEach(function (f) { - it('exports ' + f.description + ' (' + f.id + ')', function () { + describe('toBuffer/toHex', () => { + fixtures.valid.forEach(f => { + it('exports ' + f.description + ' (' + f.id + ')', () => { const actual = fromRaw(f.raw, true) assert.strictEqual(actual.toHex(), f.hex) }) if (f.whex) { - it('exports ' + f.description + ' (' + f.id + ') as witness', function () { + it('exports ' + f.description + ' (' + f.id + ') as witness', () => { const wactual = fromRaw(f.raw) assert.strictEqual(wactual.toHex(), f.whex) }) } }) - it('accepts target Buffer and offset parameters', function () { + it('accepts target Buffer and offset parameters', () => { const f = fixtures.valid[0] const actual = fromRaw(f.raw) const byteLength = actual.byteLength() @@ -114,31 +114,31 @@ describe('Transaction', function () { assert.strictEqual(b.length, byteLength) assert.strictEqual(a.toString('hex'), f.hex) assert.strictEqual(b.toString('hex'), f.hex) - assert.deepEqual(a, b) - assert.deepEqual(a, target.slice(0, byteLength)) - assert.deepEqual(b, target.slice(byteLength)) + assert.deepStrictEqual(a, b) + assert.deepStrictEqual(a, target.slice(0, byteLength)) + assert.deepStrictEqual(b, target.slice(byteLength)) }) }) - describe('hasWitnesses', function () { - fixtures.valid.forEach(function (f) { - it('detects if the transaction has witnesses: ' + (f.whex ? 'true' : 'false'), function () { + describe('hasWitnesses', () => { + fixtures.valid.forEach(f => { + it('detects if the transaction has witnesses: ' + (f.whex ? 'true' : 'false'), () => { assert.strictEqual(Transaction.fromHex(f.whex ? f.whex : f.hex).hasWitnesses(), !!f.whex) }) }) }) - describe('weight/virtualSize', function () { - it('computes virtual size', function () { - fixtures.valid.forEach(function (f) { + describe('weight/virtualSize', () => { + it('computes virtual size', () => { + fixtures.valid.forEach(f => { const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) assert.strictEqual(transaction.virtualSize(), f.virtualSize) }) }) - it('computes weight', function () { - fixtures.valid.forEach(function (f) { + it('computes weight', () => { + fixtures.valid.forEach(f => { const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) assert.strictEqual(transaction.weight(), f.weight) @@ -146,19 +146,19 @@ describe('Transaction', function () { }) }) - describe('addInput', function () { + describe('addInput', () => { let prevTxHash - beforeEach(function () { + beforeEach(() => { prevTxHash = Buffer.from('ffffffff00ffff000000000000000000000000000000000000000000101010ff', 'hex') }) - it('returns an index', function () { + it('returns an index', () => { const tx = new Transaction() assert.strictEqual(tx.addInput(prevTxHash, 0), 0) assert.strictEqual(tx.addInput(prevTxHash, 0), 1) }) - it('defaults to empty script, witness and 0xffffffff SEQUENCE number', function () { + it('defaults to empty script, witness and 0xffffffff SEQUENCE number', () => { const tx = new Transaction() tx.addInput(prevTxHash, 0) @@ -167,49 +167,49 @@ describe('Transaction', function () { assert.strictEqual(tx.ins[0].sequence, 0xffffffff) }) - fixtures.invalid.addInput.forEach(function (f) { - it('throws on ' + f.exception, function () { + fixtures.invalid.addInput.forEach(f => { + it('throws on ' + f.exception, () => { const tx = new Transaction() const hash = Buffer.from(f.hash, 'hex') - assert.throws(function () { + assert.throws(() => { tx.addInput(hash, f.index) }, new RegExp(f.exception)) }) }) }) - describe('addOutput', function () { - it('returns an index', function () { + describe('addOutput', () => { + it('returns an index', () => { const tx = new Transaction() assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 0) assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 1) }) }) - describe('clone', function () { - fixtures.valid.forEach(function (f) { + describe('clone', () => { + fixtures.valid.forEach(f => { let actual let expected - beforeEach(function () { + beforeEach(() => { expected = Transaction.fromHex(f.hex) actual = expected.clone() }) - it('should have value equality', function () { - assert.deepEqual(actual, expected) + it('should have value equality', () => { + assert.deepStrictEqual(actual, expected) }) - it('should not have reference equality', function () { - assert.notEqual(actual, expected) + it('should not have reference equality', () => { + assert.notStrictEqual(actual, expected) }) }) }) - describe('getHash/getId', function () { + describe('getHash/getId', () => { function verify (f) { - it('should return the id for ' + f.id + '(' + f.description + ')', function () { + it('should return the id for ' + f.id + '(' + f.description + ')', () => { const tx = Transaction.fromHex(f.whex || f.hex) assert.strictEqual(tx.getHash().toString('hex'), f.hash) @@ -220,9 +220,9 @@ describe('Transaction', function () { fixtures.valid.forEach(verify) }) - describe('isCoinbase', function () { + describe('isCoinbase', () => { function verify (f) { - it('should return ' + f.coinbase + ' for ' + f.id + '(' + f.description + ')', function () { + it('should return ' + f.coinbase + ' for ' + f.id + '(' + f.description + ')', () => { const tx = Transaction.fromHex(f.hex) assert.strictEqual(tx.isCoinbase(), f.coinbase) @@ -232,8 +232,8 @@ describe('Transaction', function () { fixtures.valid.forEach(verify) }) - describe('hashForSignature', function () { - it('does not use Witness serialization', function () { + describe('hashForSignature', () => { + it('does not use Witness serialization', () => { const randScript = Buffer.from('6a', 'hex') const tx = new Transaction() @@ -241,24 +241,24 @@ describe('Transaction', function () { tx.addOutput(randScript, 5000000000) const original = tx.__toBuffer - tx.__toBuffer = function (a, b, c) { + tx.__toBuffer = (a, b, c) => { if (c !== false) throw new Error('hashForSignature MUST pass false') return original.call(this, a, b, c) } - assert.throws(function () { + assert.throws(() => { tx.__toBuffer(undefined, undefined, true) }, /hashForSignature MUST pass false/) // assert hashForSignature does not pass false - assert.doesNotThrow(function () { + assert.doesNotThrow(() => { tx.hashForSignature(0, randScript, 1) }) }) - fixtures.hashForSignature.forEach(function (f) { - it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : f.script), function () { + fixtures.hashForSignature.forEach(f => { + it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : f.script), () => { const tx = Transaction.fromHex(f.txHex) const script = bscript.fromASM(f.script) @@ -267,9 +267,9 @@ describe('Transaction', function () { }) }) - describe('hashForWitnessV0', function () { - fixtures.hashForWitnessV0.forEach(function (f) { - it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : ''), function () { + describe('hashForWitnessV0', () => { + fixtures.hashForWitnessV0.forEach(f => { + it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : ''), () => { const tx = Transaction.fromHex(f.txHex) const script = bscript.fromASM(f.script) @@ -278,9 +278,9 @@ describe('Transaction', function () { }) }) - describe('setWitness', function () { - it('only accepts a a witness stack (Array of Buffers)', function () { - assert.throws(function () { + describe('setWitness', () => { + it('only accepts a a witness stack (Array of Buffers)', () => { + assert.throws(() => { (new Transaction()).setWitness(0, 'foobar') }, /Expected property "1" of type \[Buffer], got String "foobar"/) }) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 3f84b19..28aa545 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -15,9 +15,9 @@ function constructSign (f, txb) { const network = NETWORKS[f.network] const stages = f.stages && f.stages.concat() - f.inputs.forEach(function (input, index) { + f.inputs.forEach((input, index) => { if (!input.signs) return - input.signs.forEach(function (sign) { + input.signs.forEach(sign => { const keyPair = ECPair.fromWIF(sign.keyPair, network) let redeemScript let witnessScript @@ -55,7 +55,7 @@ function construct (f, dontSign) { if (Number.isFinite(f.version)) txb.setVersion(f.version) if (f.locktime !== undefined) txb.setLockTime(f.locktime) - f.inputs.forEach(function (input) { + f.inputs.forEach(input => { let prevTx if (input.txRaw) { const constructed = construct(input.txRaw) @@ -75,7 +75,7 @@ function construct (f, dontSign) { txb.addInput(prevTx, input.vout, input.sequence, prevTxScript) }) - f.outputs.forEach(function (output) { + f.outputs.forEach(output => { if (output.address) { txb.addOutput(output.address, output.value) } else { @@ -87,20 +87,20 @@ function construct (f, dontSign) { return constructSign(f, txb) } -describe('TransactionBuilder', function () { +describe('TransactionBuilder', () => { // constants const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) const scripts = [ '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP' - ].map(function (x) { + ].map(x => { return baddress.toOutputScript(x) }) const txHash = Buffer.from('0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', 'hex') - describe('fromTransaction', function () { - fixtures.valid.build.forEach(function (f) { - it('returns TransactionBuilder, with ' + f.description, function () { + describe('fromTransaction', () => { + fixtures.valid.build.forEach(f => { + it('returns TransactionBuilder, with ' + f.description, () => { const network = NETWORKS[f.network || 'bitcoin'] const tx = Transaction.fromHex(f.txHex) @@ -112,82 +112,82 @@ describe('TransactionBuilder', function () { }) }) - fixtures.valid.fromTransaction.forEach(function (f) { - it('returns TransactionBuilder, with ' + f.description, function () { + fixtures.valid.fromTransaction.forEach(f => { + it('returns TransactionBuilder, with ' + f.description, () => { const tx = new Transaction() - f.inputs.forEach(function (input) { + f.inputs.forEach(input => { const txHash2 = Buffer.from(input.txId, 'hex').reverse() tx.addInput(txHash2, input.vout, undefined, bscript.fromASM(input.scriptSig)) }) - f.outputs.forEach(function (output) { + f.outputs.forEach(output => { tx.addOutput(bscript.fromASM(output.script), output.value) }) const txb = TransactionBuilder.fromTransaction(tx) const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() - txAfter.ins.forEach(function (input, i) { - assert.equal(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) + txAfter.ins.forEach((input, i) => { + assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) }) - txAfter.outs.forEach(function (output, i) { - assert.equal(bscript.toASM(output.script), f.outputs[i].script) + txAfter.outs.forEach((output, i) => { + assert.strictEqual(bscript.toASM(output.script), f.outputs[i].script) }) }) }) - fixtures.valid.fromTransactionSequential.forEach(function (f) { - it('with ' + f.description, function () { + fixtures.valid.fromTransactionSequential.forEach(f => { + it('with ' + f.description, () => { const network = NETWORKS[f.network] const tx = Transaction.fromHex(f.txHex) const txb = TransactionBuilder.fromTransaction(tx, network) - tx.ins.forEach(function (input, i) { - assert.equal(bscript.toASM(input.script), f.inputs[i].scriptSig) + tx.ins.forEach((input, i) => { + assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSig) }) constructSign(f, txb) const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() - txAfter.ins.forEach(function (input, i) { - assert.equal(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) + txAfter.ins.forEach((input, i) => { + assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) }) - assert.equal(txAfter.toHex(), f.txHexAfter) + assert.strictEqual(txAfter.toHex(), f.txHexAfter) }) }) - it('classifies transaction inputs', function () { + it('classifies transaction inputs', () => { const tx = Transaction.fromHex(fixtures.valid.classification.hex) const txb = TransactionBuilder.fromTransaction(tx) - txb.__INPUTS.forEach(function (i) { + txb.__INPUTS.forEach(i => { assert.strictEqual(i.prevOutType, 'scripthash') assert.strictEqual(i.redeemScriptType, 'multisig') }) }) - fixtures.invalid.fromTransaction.forEach(function (f) { - it('throws ' + f.exception, function () { + fixtures.invalid.fromTransaction.forEach(f => { + it('throws ' + f.exception, () => { const tx = Transaction.fromHex(f.txHex) - assert.throws(function () { + assert.throws(() => { TransactionBuilder.fromTransaction(tx) }, new RegExp(f.exception)) }) }) }) - describe('addInput', function () { + describe('addInput', () => { let txb - beforeEach(function () { + beforeEach(() => { txb = new TransactionBuilder() }) - it('accepts a txHash, index [and sequence number]', function () { + it('accepts a txHash, index [and sequence number]', () => { const vin = txb.addInput(txHash, 1, 54) assert.strictEqual(vin, 0) @@ -198,7 +198,7 @@ describe('TransactionBuilder', function () { assert.strictEqual(txb.__INPUTS[0].prevOutScript, undefined) }) - it('accepts a txHash, index [, sequence number and scriptPubKey]', function () { + it('accepts a txHash, index [, sequence number and scriptPubKey]', () => { const vin = txb.addInput(txHash, 1, 54, scripts[1]) assert.strictEqual(vin, 0) @@ -209,7 +209,7 @@ describe('TransactionBuilder', function () { assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) }) - it('accepts a prevTx, index [and sequence number]', function () { + it('accepts a prevTx, index [and sequence number]', () => { const prevTx = new Transaction() prevTx.addOutput(scripts[0], 0) prevTx.addOutput(scripts[1], 1) @@ -218,116 +218,116 @@ describe('TransactionBuilder', function () { assert.strictEqual(vin, 0) const txIn = txb.__TX.ins[0] - assert.deepEqual(txIn.hash, prevTx.getHash()) + assert.deepStrictEqual(txIn.hash, prevTx.getHash()) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) }) - it('returns the input index', function () { + it('returns the input index', () => { assert.strictEqual(txb.addInput(txHash, 0), 0) assert.strictEqual(txb.addInput(txHash, 1), 1) }) - it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', function () { + it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { txb.addInput(txHash, 0) txb.addOutput(scripts[0], 1000) txb.sign(0, keyPair) - assert.throws(function () { + assert.throws(() => { txb.addInput(txHash, 0) }, /No, this would invalidate signatures/) }) }) - describe('addOutput', function () { + describe('addOutput', () => { let txb - beforeEach(function () { + beforeEach(() => { txb = new TransactionBuilder() }) - it('accepts an address string and value', function () { + it('accepts an address string and value', () => { const { address } = payments.p2pkh({ pubkey: keyPair.publicKey }) const vout = txb.addOutput(address, 1000) assert.strictEqual(vout, 0) const txout = txb.__TX.outs[0] - assert.deepEqual(txout.script, scripts[0]) + assert.deepStrictEqual(txout.script, scripts[0]) assert.strictEqual(txout.value, 1000) }) - it('accepts a ScriptPubKey and value', function () { + it('accepts a ScriptPubKey and value', () => { const vout = txb.addOutput(scripts[0], 1000) assert.strictEqual(vout, 0) const txout = txb.__TX.outs[0] - assert.deepEqual(txout.script, scripts[0]) + assert.deepStrictEqual(txout.script, scripts[0]) assert.strictEqual(txout.value, 1000) }) - it('throws if address is of the wrong network', function () { - assert.throws(function () { + it('throws if address is of the wrong network', () => { + assert.throws(() => { txb.addOutput('2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9', 1000) }, /2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9 has no matching Script/) }) - it('add second output after signed first input with SIGHASH_NONE', function () { + it('add second output after signed first input with SIGHASH_NONE', () => { txb.addInput(txHash, 0) txb.addOutput(scripts[0], 2000) txb.sign(0, keyPair, undefined, Transaction.SIGHASH_NONE) - assert.equal(txb.addOutput(scripts[1], 9000), 1) + assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) }) - it('add first output after signed first input with SIGHASH_NONE', function () { + it('add first output after signed first input with SIGHASH_NONE', () => { txb.addInput(txHash, 0) txb.sign(0, keyPair, undefined, Transaction.SIGHASH_NONE) - assert.equal(txb.addOutput(scripts[0], 2000), 0) + assert.strictEqual(txb.addOutput(scripts[0], 2000), 0) }) - it('add second output after signed first input with SIGHASH_SINGLE', function () { + it('add second output after signed first input with SIGHASH_SINGLE', () => { txb.addInput(txHash, 0) txb.addOutput(scripts[0], 2000) txb.sign(0, keyPair, undefined, Transaction.SIGHASH_SINGLE) - assert.equal(txb.addOutput(scripts[1], 9000), 1) + assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) }) - it('add first output after signed first input with SIGHASH_SINGLE', function () { + it('add first output after signed first input with SIGHASH_SINGLE', () => { txb.addInput(txHash, 0) txb.sign(0, keyPair, undefined, Transaction.SIGHASH_SINGLE) - assert.throws(function () { + assert.throws(() => { txb.addOutput(scripts[0], 2000) }, /No, this would invalidate signatures/) }) - it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', function () { + it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { txb.addInput(txHash, 0) txb.addOutput(scripts[0], 2000) txb.sign(0, keyPair) - assert.throws(function () { + assert.throws(() => { txb.addOutput(scripts[1], 9000) }, /No, this would invalidate signatures/) }) }) - describe('setLockTime', function () { - it('throws if if there exist any scriptSigs', function () { + describe('setLockTime', () => { + it('throws if if there exist any scriptSigs', () => { const txb = new TransactionBuilder() txb.addInput(txHash, 0) txb.addOutput(scripts[0], 100) txb.sign(0, keyPair) - assert.throws(function () { + assert.throws(() => { txb.setLockTime(65535) }, /No, this would invalidate signatures/) }) }) - describe('sign', function () { - it('supports the alternative abstract interface { publicKey, sign }', function () { + describe('sign', () => { + it('supports the alternative abstract interface { publicKey, sign }', () => { const keyPair = { - publicKey: ECPair.makeRandom({ rng: function () { return Buffer.alloc(32, 1) } }).publicKey, - sign: function (hash) { return Buffer.alloc(64, 0x5f) } + publicKey: ECPair.makeRandom({ rng: () => { return Buffer.alloc(32, 1) } }).publicKey, + sign: hash => { return Buffer.alloc(64, 0x5f) } } const txb = new TransactionBuilder() @@ -335,16 +335,16 @@ describe('TransactionBuilder', function () { txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) txb.addOutput('1111111111111111111114oLvT2', 100000) txb.sign(0, keyPair) - assert.equal(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) - fixtures.invalid.sign.forEach(function (f) { - it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), function () { + fixtures.invalid.sign.forEach(f => { + it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), () => { const txb = construct(f, true) let threw = false - f.inputs.forEach(function (input, index) { - input.signs.forEach(function (sign) { + f.inputs.forEach((input, index) => { + input.signs.forEach(sign => { const keyPairNetwork = NETWORKS[sign.network || f.network] const keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork) let redeemScript @@ -359,7 +359,7 @@ describe('TransactionBuilder', function () { } if (sign.throws) { - assert.throws(function () { + assert.throws(() => { txb.sign(index, keyPair2, redeemScript, sign.hashType, sign.value, witnessScript) }, new RegExp(f.exception)) threw = true @@ -369,14 +369,14 @@ describe('TransactionBuilder', function () { }) }) - assert.equal(threw, true) + assert.strictEqual(threw, true) }) }) }) - describe('build', function () { - fixtures.valid.build.forEach(function (f) { - it('builds "' + f.description + '"', function () { + describe('build', () => { + fixtures.valid.build.forEach(f => { + it('builds "' + f.description + '"', () => { const txb = construct(f) const tx = f.incomplete ? txb.buildIncomplete() : txb.build() @@ -385,10 +385,10 @@ describe('TransactionBuilder', function () { }) // TODO: remove duplicate test code - fixtures.invalid.build.forEach(function (f) { - describe('for ' + (f.description || f.exception), function () { - it('throws ' + f.exception, function () { - assert.throws(function () { + fixtures.invalid.build.forEach(f => { + describe('for ' + (f.description || f.exception), () => { + it('throws ' + f.exception, () => { + assert.throws(() => { let txb if (f.txHex) { txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) @@ -402,8 +402,8 @@ describe('TransactionBuilder', function () { // if throws on incomplete too, enforce that if (f.incomplete) { - it('throws ' + f.exception, function () { - assert.throws(function () { + it('throws ' + f.exception, () => { + assert.throws(() => { let txb if (f.txHex) { txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) @@ -415,7 +415,7 @@ describe('TransactionBuilder', function () { }, new RegExp(f.exception)) }) } else { - it('does not throw if buildIncomplete', function () { + it('does not throw if buildIncomplete', () => { let txb if (f.txHex) { txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) @@ -429,7 +429,7 @@ describe('TransactionBuilder', function () { }) }) - it('for incomplete with 0 signatures', function () { + it('for incomplete with 0 signatures', () => { const randomTxData = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000' const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH' @@ -441,7 +441,7 @@ describe('TransactionBuilder', function () { assert(tx) }) - it('for incomplete P2SH with 0 signatures', function () { + it('for incomplete P2SH with 0 signatures', () => { const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', 'hex') // arbitrary P2SH input const inpTx = Transaction.fromBuffer(inp) @@ -452,7 +452,7 @@ describe('TransactionBuilder', function () { txb.buildIncomplete() }) - it('for incomplete P2WPKH with 0 signatures', function () { + it('for incomplete P2WPKH with 0 signatures', () => { const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', 'hex') const inpTx = Transaction.fromBuffer(inp) @@ -463,7 +463,7 @@ describe('TransactionBuilder', function () { txb.buildIncomplete() }) - it('for incomplete P2WSH with 0 signatures', function () { + it('for incomplete P2WSH with 0 signatures', () => { const inpTx = Transaction.fromBuffer(Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', 'hex')) const txb = new TransactionBuilder(NETWORKS.testnet) @@ -474,17 +474,17 @@ describe('TransactionBuilder', function () { }) }) - describe('multisig', function () { - fixtures.valid.multisig.forEach(function (f) { - it(f.description, function () { + describe('multisig', () => { + fixtures.valid.multisig.forEach(f => { + it(f.description, () => { const network = NETWORKS[f.network] let txb = construct(f, true) let tx - f.inputs.forEach(function (input, i) { + f.inputs.forEach((input, i) => { const redeemScript = bscript.fromASM(input.redeemScript) - input.signs.forEach(function (sign) { + input.signs.forEach(sign => { // rebuild the transaction each-time after the first if (tx) { // manually override the scriptSig? @@ -513,10 +513,10 @@ describe('TransactionBuilder', function () { }) }) - describe('various edge case', function () { + describe('various edge case', () => { const network = NETWORKS.testnet - it('should warn of high fee for segwit transaction based on VSize, not Size', function () { + it('should warn of high fee for segwit transaction based on VSize, not Size', () => { const rawtx = '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + '1df90000000000fffffffffdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a1df9' + '0100000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca40000' + @@ -538,12 +538,12 @@ describe('TransactionBuilder', function () { txb.__INPUTS[2].value = 248920 txb.__INPUTS[3].value = 248920 - assert.throws(function () { + assert.throws(() => { txb.build() }, new RegExp('Transaction has absurd fees')) }) - it('should classify witness inputs with witness = true during multisigning', function () { + it('should classify witness inputs with witness = true during multisigning', () => { const keyPair = ECPair.fromWIF('cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', network) const witnessScript = Buffer.from('522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', 'hex') const redeemScript = Buffer.from('002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', 'hex') @@ -558,13 +558,13 @@ describe('TransactionBuilder', function () { const tx = txb.buildIncomplete() // Only input is segwit, so txid should be accurate with the final tx - assert.equal(tx.getId(), 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') + assert.strictEqual(tx.getId(), 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') const txHex = tx.toHex() TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) }) - it('should handle badly pre-filled OP_0s', function () { + it('should handle badly pre-filled OP_0s', () => { // OP_0 is used where a signature is missing const redeemScripSig = bscript.fromASM('OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') const redeemScript = bscript.fromASM('OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG') @@ -580,11 +580,11 @@ describe('TransactionBuilder', function () { txb.sign(0, keyPair2, redeemScript) const tx2 = txb.build() - assert.equal(tx2.getId(), 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') - assert.equal(bscript.toASM(tx2.ins[0].script), 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') + assert.strictEqual(tx2.getId(), 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') + assert.strictEqual(bscript.toASM(tx2.ins[0].script), 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') }) - it('should not classify blank scripts as nonstandard', function () { + it('should not classify blank scripts as nonstandard', () => { let txb = new TransactionBuilder() txb.setVersion(1) txb.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) @@ -596,14 +596,14 @@ describe('TransactionBuilder', function () { txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) txb.sign(0, keyPair) const txId = txb.build().getId() - assert.equal(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') + assert.strictEqual(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') // and, repeat txb = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) txb.sign(0, keyPair) const txId2 = txb.build().getId() - assert.equal(txId, txId2) + assert.strictEqual(txId, txId2) }) }) }) diff --git a/test/types.js b/test/types.js index d245d53..8911ca1 100644 --- a/test/types.js +++ b/test/types.js @@ -3,38 +3,38 @@ const assert = require('assert') const types = require('../src/types') const typeforce = require('typeforce') -describe('types', function () { - describe('Buffer Hash160/Hash256', function () { +describe('types', () => { + describe('Buffer Hash160/Hash256', () => { const buffer20byte = Buffer.alloc(20) const buffer32byte = Buffer.alloc(32) - it('return true for valid size', function () { + it('return true for valid size', () => { assert(types.Hash160bit(buffer20byte)) assert(types.Hash256bit(buffer32byte)) }) - it('return true for oneOf', function () { - assert.doesNotThrow(function () { + it('return true for oneOf', () => { + assert.doesNotThrow(() => { typeforce(types.oneOf(types.Hash160bit, types.Hash256bit), buffer32byte) }) - assert.doesNotThrow(function () { + assert.doesNotThrow(() => { typeforce(types.oneOf(types.Hash256bit, types.Hash160bit), buffer32byte) }) }) - it('throws for invalid size', function () { - assert.throws(function () { + it('throws for invalid size', () => { + assert.throws(() => { types.Hash160bit(buffer32byte) }, /Expected Buffer\(Length: 20\), got Buffer\(Length: 32\)/) - assert.throws(function () { + assert.throws(() => { types.Hash256bit(buffer20byte) }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 20\)/) }) }) - describe('Satoshi', function () { + describe('Satoshi', () => { [ { value: -1, result: false }, { value: 0, result: true }, @@ -42,8 +42,8 @@ describe('types', function () { { value: 20999999 * 1e8, result: true }, { value: 21000000 * 1e8, result: true }, { value: 21000001 * 1e8, result: false } - ].forEach(function (f) { - it('returns ' + f.result + ' for valid for ' + f.value, function () { + ].forEach(f => { + it('returns ' + f.result + ' for valid for ' + f.value, () => { assert.strictEqual(types.Satoshi(f.value), f.result) }) }) From c77db1a14f3b08de38a068f28cef13bfe70b865c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Apr 2019 15:19:05 +0900 Subject: [PATCH 276/568] Only run docker in integration with cache --- .travis.yml | 8 +++++--- test/integration/_regtest.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7921fac..bb87738 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,11 @@ language: node_js services: - docker before_install: - - docker pull junderw/bitcoinjs-regtest-server - - docker run -d -p 127.0.0.1:8080:8080 junderw/bitcoinjs-regtest-server - - docker ps -a + - if [ $TEST_SUITE = "integration" ]; then + docker pull junderw/bitcoinjs-regtest-server && + docker run -d -p 127.0.0.1:8080:8080 junderw/bitcoinjs-regtest-server && + docker ps -a; + fi node_js: - "8" - "lts/*" diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index abfee1a..8be864a 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -71,7 +71,7 @@ async function faucet (address, value) { } ) - await sleep(randInt(50, 150)) + await sleep(randInt(10, 40)) const results = await unspents(address) From 329809fa4a10f1c9725c09d2f4e43e6b1860bf6d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 11 Apr 2019 15:55:33 +0900 Subject: [PATCH 277/568] Fix address.*OutputScript and ECPairOptions rng --- ts_src/address.ts | 4 ++-- ts_src/ecpair.ts | 2 +- types/address.d.ts | 4 ++-- types/ecpair.d.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ts_src/address.ts b/ts_src/address.ts index 0c0cda5..ad791ad 100644 --- a/ts_src/address.ts +++ b/ts_src/address.ts @@ -64,7 +64,7 @@ export function toBech32( return bech32.encode(prefix, words); } -export function fromOutputScript(output: Buffer, network: Network): string { +export function fromOutputScript(output: Buffer, network?: Network): string { // TODO: Network network = network || networks.bitcoin; @@ -84,7 +84,7 @@ export function fromOutputScript(output: Buffer, network: Network): string { throw new Error(bscript.toASM(output) + ' has no matching Address'); } -export function toOutputScript(address: string, network: Network): Buffer { +export function toOutputScript(address: string, network?: Network): Buffer { network = network || networks.bitcoin; let decodeBase58: Base58CheckResult | undefined; diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index 9e56919..3941afa 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -16,7 +16,7 @@ const isOptions = typeforce.maybe( interface ECPairOptions { compressed?: boolean; network?: Network; - rng?(arg0: Buffer): Buffer; + rng?(arg0: number): Buffer; } export interface ECPairInterface { diff --git a/types/address.d.ts b/types/address.d.ts index 1da68ac..be0e00a 100644 --- a/types/address.d.ts +++ b/types/address.d.ts @@ -13,5 +13,5 @@ export declare function fromBase58Check(address: string): Base58CheckResult; export declare function fromBech32(address: string): Bech32Result; export declare function toBase58Check(hash: Buffer, version: number): string; export declare function toBech32(data: Buffer, version: number, prefix: string): string; -export declare function fromOutputScript(output: Buffer, network: Network): string; -export declare function toOutputScript(address: string, network: Network): Buffer; +export declare function fromOutputScript(output: Buffer, network?: Network): string; +export declare function toOutputScript(address: string, network?: Network): Buffer; diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index 33535bd..a5ae716 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -3,7 +3,7 @@ import { Network } from './networks'; interface ECPairOptions { compressed?: boolean; network?: Network; - rng?(arg0: Buffer): Buffer; + rng?(arg0: number): Buffer; } export interface ECPairInterface { compressed: boolean; From f6f33595f6fdb319ef38ac6a5adc10259b4dad2b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 11 Apr 2019 16:05:02 +0900 Subject: [PATCH 278/568] 5.0.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b57448e..f56a619 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.1", + "version": "5.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 10088cd..591e98a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.1", + "version": "5.0.2", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 3b402d00c61fc50a290a8d3f6bfb3749b8c13e63 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 12 Apr 2019 17:44:55 +0900 Subject: [PATCH 279/568] Add low R grinding option --- src/ecpair.js | 19 +++++++++++++++++-- ts_src/ecpair.ts | 18 ++++++++++++++++-- types/ecpair.d.ts | 2 +- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index 2026c63..6bb7eb9 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -35,10 +35,25 @@ class ECPair { throw new Error('Missing private key'); return wif.encode(this.network.wif, this.__D, this.compressed); } - sign(hash) { + sign(hash, lowR = false) { if (!this.__D) throw new Error('Missing private key'); - return ecc.sign(hash, this.__D); + if (lowR === false) { + return ecc.sign(hash, this.__D); + } + else { + let sig = ecc.sign(hash, this.__D); + const extraData = Buffer.alloc(32, 0); + let counter = 0; + // if first try is lowR, skip the loop + // for second try and on, add extra entropy counting up + while (sig[0] > 0x7f) { + counter++; + extraData.writeUIntLE(counter, 0, 6); + sig = ecc.signWithEntropy(hash, this.__D, extraData); + } + return sig; + } } verify(hash, signature) { return ecc.verify(hash, this.publicKey, signature); diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index 3941afa..7245d0d 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -61,9 +61,23 @@ class ECPair implements ECPairInterface { return wif.encode(this.network.wif, this.__D, this.compressed); } - sign(hash: Buffer): Buffer { + sign(hash: Buffer, lowR: boolean = false): Buffer { if (!this.__D) throw new Error('Missing private key'); - return ecc.sign(hash, this.__D); + if (lowR === false) { + return ecc.sign(hash, this.__D); + } else { + let sig = ecc.sign(hash, this.__D); + const extraData = Buffer.alloc(32, 0); + let counter = 0; + // if first try is lowR, skip the loop + // for second try and on, add extra entropy counting up + while (sig[0] > 0x7f) { + counter++; + extraData.writeUIntLE(counter, 0, 6); + sig = ecc.signWithEntropy(hash, this.__D, extraData); + } + return sig; + } } verify(hash: Buffer, signature: Buffer): Buffer { diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index a5ae716..2f9bbc4 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -24,7 +24,7 @@ declare class ECPair implements ECPairInterface { readonly privateKey: Buffer | undefined; readonly publicKey: Buffer | undefined; toWIF(): string; - sign(hash: Buffer): Buffer; + sign(hash: Buffer, lowR?: boolean): Buffer; verify(hash: Buffer, signature: Buffer): Buffer; } declare function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair; From ccd439b80519a198326f077b24870b1027c01ba6 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 12 Apr 2019 17:55:45 +0900 Subject: [PATCH 280/568] Modify interface --- ts_src/ecpair.ts | 2 +- types/ecpair.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index 7245d0d..649aa37 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -25,7 +25,7 @@ export interface ECPairInterface { privateKey?: Buffer; publicKey?: Buffer; toWIF(): string; - sign(hash: Buffer): Buffer; + sign(hash: Buffer, lowR?: boolean): Buffer; verify(hash: Buffer, signature: Buffer): Buffer; getPublicKey?(): Buffer; } diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index 2f9bbc4..87ebff4 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -11,7 +11,7 @@ export interface ECPairInterface { privateKey?: Buffer; publicKey?: Buffer; toWIF(): string; - sign(hash: Buffer): Buffer; + sign(hash: Buffer, lowR?: boolean): Buffer; verify(hash: Buffer, signature: Buffer): Buffer; getPublicKey?(): Buffer; } From b5577607d4496a6774d9e1f126f312d634ca3b55 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 15 Apr 2019 15:28:01 +0900 Subject: [PATCH 281/568] Add tests for low R signing --- package-lock.json | 14 +++++++------- package.json | 2 +- test/ecpair.js | 22 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index f56a619..8b225f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -731,9 +731,9 @@ "dev": true }, "nan": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==" }, "nyc": { "version": "13.3.0", @@ -1911,15 +1911,15 @@ } }, "tiny-secp256k1": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.0.1.tgz", - "integrity": "sha512-Wz2kMPWtCI5XBftFeF3bUL8uz2+VlasniKwOkRPjvL7h1QVd9rbhrve/HWUu747kJKzVf1XHonzcdM4Ut8fvww==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.0.tgz", + "integrity": "sha512-DIl0SCUIVcPrk/oOiq8/YgQ69Beayw4XSW2icyXJN8xfKMmxo5XM8gXVG1Ex+rYsHg2xuEpNFeeU6J4CtqQFrA==", "requires": { "bindings": "^1.3.0", "bn.js": "^4.11.8", "create-hmac": "^1.1.7", "elliptic": "^6.4.0", - "nan": "^2.10.0" + "nan": "^2.12.1" } }, "to-fast-properties": { diff --git a/package.json b/package.json index 591e98a..eff2d63 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "merkle-lib": "^2.0.10", "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", - "tiny-secp256k1": "^1.0.0", + "tiny-secp256k1": "^1.1.0", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" diff --git a/test/ecpair.js b/test/ecpair.js index ce32683..e067ddd 100644 --- a/test/ecpair.js +++ b/test/ecpair.js @@ -259,4 +259,26 @@ describe('ECPair', () => { })) }) }) + describe('optional low R signing', () => { + const sig = Buffer.from('95a6619140fca3366f1d3b013b0367c4f86e39508a50fdce' + + 'e5245fbb8bd60aa6086449e28cf15387cf9f85100bfd0838624ca96759e59f65c10a00' + + '16b86f5229', 'hex') + const sigLowR = Buffer.from('6a2660c226e8055afad317eeba918a304be79208d505' + + '3bc5ea4a5e4c5892b4a061c717c5284ae5202d721c0e49b4717b79966280906b1d3b52' + + '95d1fdde963c35', 'hex') + const lowRKeyPair = ECPair.fromWIF('L3nThUzbAwpUiBAjR5zCu66ybXSPMr2zZ3ikp' + + 'ScpTPiYTxBynfZu') + const dataToSign = Buffer.from('b6c5c548a7f6164c8aa7af5350901626ebd69f9ae' + + '2c1ecf8871f5088ec204cfe', 'hex') + + it('signs with normal R by default', () => { + const signed = lowRKeyPair.sign(dataToSign) + assert.deepStrictEqual(sig, signed) + }) + + it('signs with low R when true is passed', () => { + const signed = lowRKeyPair.sign(dataToSign, true) + assert.deepStrictEqual(sigLowR, signed) + }) + }) }) From 352e9ef0a3a2be01f9c85a077d85563812890ed4 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 15 Apr 2019 17:27:28 +0900 Subject: [PATCH 282/568] Add low R signing to TransactionBuilder --- src/transaction_builder.js | 11 ++++++++++- test/transaction_builder.js | 19 +++++++++++++++++++ ts_src/transaction_builder.ts | 13 ++++++++++++- types/transaction_builder.d.ts | 2 ++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index d3b2673..cf27404 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -29,6 +29,7 @@ class TransactionBuilder { this.__INPUTS = []; this.__TX = new transaction_1.Transaction(); this.__TX.version = 2; + this.__USE_LOW_R = false; } static fromTransaction(transaction, network) { const txb = new TransactionBuilder(network); @@ -53,6 +54,14 @@ class TransactionBuilder { }); return txb; } + setLowR(setting) { + typeforce(typeforce.maybe(typeforce.Boolean), setting); + if (setting === undefined) { + setting = true; + } + this.__USE_LOW_R = setting; + return setting; + } setLockTime(locktime) { typeforce(types.UInt32, locktime); // if any signatures exist, throw @@ -159,7 +168,7 @@ class TransactionBuilder { if (ourPubKey.length !== 33 && input.hasWitness) { throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH'); } - const signature = keyPair.sign(signatureHash); + const signature = keyPair.sign(signatureHash, this.__USE_LOW_R); input.signatures[i] = bscript.signature.encode(signature, hashType); return true; }); diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 28aa545..1af8272 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -338,6 +338,25 @@ describe('TransactionBuilder', () => { assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) + it('supports low R signature signing', () => { + let txb = new TransactionBuilder() + txb.setVersion(1) + txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) + txb.addOutput('1111111111111111111114oLvT2', 100000) + txb.sign(0, keyPair) + // high R + assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + + txb = new TransactionBuilder() + txb.setVersion(1) + txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) + txb.addOutput('1111111111111111111114oLvT2', 100000) + txb.setLowR() + txb.sign(0, keyPair) + // low R + assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + }) + fixtures.invalid.sign.forEach(f => { it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), () => { const txb = construct(f, true) diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 6a49b3f..0665719 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -94,6 +94,7 @@ export class TransactionBuilder { private __PREV_TX_SET: { [index: string]: boolean }; private __INPUTS: TxbInput[]; private __TX: Transaction; + private __USE_LOW_R: boolean; // WARNING: maximumFeeRate is __NOT__ to be relied on, // it's just another potential safety mechanism (safety in-depth) @@ -105,6 +106,16 @@ export class TransactionBuilder { this.__INPUTS = []; this.__TX = new Transaction(); this.__TX.version = 2; + this.__USE_LOW_R = false; + } + + setLowR(setting?: boolean): boolean { + typeforce(typeforce.maybe(typeforce.Boolean), setting); + if (setting === undefined) { + setting = true; + } + this.__USE_LOW_R = setting; + return setting; } setLockTime(locktime: number): void { @@ -266,7 +277,7 @@ export class TransactionBuilder { ); } - const signature = keyPair.sign(signatureHash); + const signature = keyPair.sign(signatureHash, this.__USE_LOW_R); input.signatures![i] = bscript.signature.encode(signature, hashType!); return true; }); diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts index 82c4ef0..f993807 100644 --- a/types/transaction_builder.d.ts +++ b/types/transaction_builder.d.ts @@ -9,7 +9,9 @@ export declare class TransactionBuilder { private __PREV_TX_SET; private __INPUTS; private __TX; + private __USE_LOW_R; constructor(network?: Network, maximumFeeRate?: number); + setLowR(setting?: boolean): boolean; setLockTime(locktime: number): void; setVersion(version: number): void; addInput(txHash: Buffer | string | Transaction, vout: number, sequence?: number, prevOutScript?: Buffer): number; From e28e04427e0ba072cf4af5b08300eaa45aadcfcd Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sun, 21 Apr 2019 21:30:21 +0900 Subject: [PATCH 283/568] Use Prettier to make JS easier to read/audit --- package.json | 5 +- src/address.js | 145 +-- src/block.js | 442 +++---- src/bufferutils.js | 58 +- src/classify.js | 110 +- src/crypto.js | 39 +- src/ecpair.js | 149 ++- src/index.js | 26 +- src/networks.js | 58 +- src/payments/embed.js | 82 +- src/payments/index.js | 18 +- src/payments/lazy.js | 47 +- src/payments/p2ms.js | 254 ++-- src/payments/p2pk.js | 129 +- src/payments/p2pkh.js | 254 ++-- src/payments/p2sh.js | 331 +++-- src/payments/p2wpkh.js | 242 ++-- src/payments/p2wsh.js | 321 +++-- src/script.js | 264 ++-- src/script_number.js | 108 +- src/script_signature.js | 77 +- src/templates/multisig/index.js | 8 +- src/templates/multisig/input.js | 30 +- src/templates/multisig/output.js | 49 +- src/templates/nulldata.js | 12 +- src/templates/pubkey/index.js | 8 +- src/templates/pubkey/input.js | 13 +- src/templates/pubkey/output.js | 20 +- src/templates/pubkeyhash/index.js | 8 +- src/templates/pubkeyhash/input.js | 18 +- src/templates/pubkeyhash/output.js | 26 +- src/templates/scripthash/index.js | 8 +- src/templates/scripthash/input.js | 82 +- src/templates/scripthash/output.js | 22 +- src/templates/witnesscommitment/index.js | 6 +- src/templates/witnesscommitment/output.js | 38 +- src/templates/witnesspubkeyhash/index.js | 8 +- src/templates/witnesspubkeyhash/input.js | 20 +- src/templates/witnesspubkeyhash/output.js | 18 +- src/templates/witnessscripthash/index.js | 8 +- src/templates/witnessscripthash/input.js | 61 +- src/templates/witnessscripthash/output.js | 18 +- src/transaction.js | 840 +++++++------ src/transaction_builder.js | 1387 +++++++++++---------- src/types.js | 28 +- 45 files changed, 2947 insertions(+), 2948 deletions(-) diff --git a/package.json b/package.json index 591e98a..d9fe283 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,13 @@ "bitcoinjs" ], "scripts": { - "build": "tsc -p ./tsconfig.json", + "build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs", + "clean": "rm -rf src/", "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", "format": "npm run prettier -- --write", + "formatjs": "npm run prettierjs -- --write > /dev/null 2>&1", "format:ci": "npm run prettier -- --check", "gitdiff:ci": "npm run build && git diff --exit-code", "integration": "npm run build && npm run nobuild:integration", @@ -30,6 +32,7 @@ "nobuild:integration": "mocha --timeout 50000 test/integration/", "nobuild:unit": "mocha", "prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore", + "prettierjs": "prettier 'src/**/*.js' --ignore-path ./.prettierignore", "test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" }, diff --git a/src/address.js b/src/address.js index b0be0e1..e15c55e 100644 --- a/src/address.js +++ b/src/address.js @@ -1,100 +1,91 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const networks = require("./networks"); -const payments = require("./payments"); -const bscript = require("./script"); -const types = require("./types"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const networks = require('./networks'); +const payments = require('./payments'); +const bscript = require('./script'); +const types = require('./types'); const bech32 = require('bech32'); const bs58check = require('bs58check'); const typeforce = require('typeforce'); function fromBase58Check(address) { - const payload = bs58check.decode(address); - // TODO: 4.0.0, move to "toOutputScript" - if (payload.length < 21) - throw new TypeError(address + ' is too short'); - if (payload.length > 21) - throw new TypeError(address + ' is too long'); - const version = payload.readUInt8(0); - const hash = payload.slice(1); - return { version, hash }; + const payload = bs58check.decode(address); + // TODO: 4.0.0, move to "toOutputScript" + if (payload.length < 21) throw new TypeError(address + ' is too short'); + if (payload.length > 21) throw new TypeError(address + ' is too long'); + const version = payload.readUInt8(0); + const hash = payload.slice(1); + return { version, hash }; } exports.fromBase58Check = fromBase58Check; function fromBech32(address) { - const result = bech32.decode(address); - const data = bech32.fromWords(result.words.slice(1)); - return { - version: result.words[0], - prefix: result.prefix, - data: Buffer.from(data), - }; + const result = bech32.decode(address); + const data = bech32.fromWords(result.words.slice(1)); + return { + version: result.words[0], + prefix: result.prefix, + data: Buffer.from(data), + }; } exports.fromBech32 = fromBech32; function toBase58Check(hash, version) { - typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); - const payload = Buffer.allocUnsafe(21); - payload.writeUInt8(version, 0); - hash.copy(payload, 1); - return bs58check.encode(payload); + typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments); + const payload = Buffer.allocUnsafe(21); + payload.writeUInt8(version, 0); + hash.copy(payload, 1); + return bs58check.encode(payload); } exports.toBase58Check = toBase58Check; function toBech32(data, version, prefix) { - const words = bech32.toWords(data); - words.unshift(version); - return bech32.encode(prefix, words); + const words = bech32.toWords(data); + words.unshift(version); + return bech32.encode(prefix, words); } exports.toBech32 = toBech32; function fromOutputScript(output, network) { - // TODO: Network - network = network || networks.bitcoin; - try { - return payments.p2pkh({ output, network }).address; - } - catch (e) { } - try { - return payments.p2sh({ output, network }).address; - } - catch (e) { } - try { - return payments.p2wpkh({ output, network }).address; - } - catch (e) { } - try { - return payments.p2wsh({ output, network }).address; - } - catch (e) { } - throw new Error(bscript.toASM(output) + ' has no matching Address'); + // TODO: Network + network = network || networks.bitcoin; + try { + return payments.p2pkh({ output, network }).address; + } catch (e) {} + try { + return payments.p2sh({ output, network }).address; + } catch (e) {} + try { + return payments.p2wpkh({ output, network }).address; + } catch (e) {} + try { + return payments.p2wsh({ output, network }).address; + } catch (e) {} + throw new Error(bscript.toASM(output) + ' has no matching Address'); } exports.fromOutputScript = fromOutputScript; function toOutputScript(address, network) { - network = network || networks.bitcoin; - let decodeBase58; - let decodeBech32; + network = network || networks.bitcoin; + let decodeBase58; + let decodeBech32; + try { + decodeBase58 = fromBase58Check(address); + } catch (e) {} + if (decodeBase58) { + if (decodeBase58.version === network.pubKeyHash) + return payments.p2pkh({ hash: decodeBase58.hash }).output; + if (decodeBase58.version === network.scriptHash) + return payments.p2sh({ hash: decodeBase58.hash }).output; + } else { try { - decodeBase58 = fromBase58Check(address); + decodeBech32 = fromBech32(address); + } catch (e) {} + if (decodeBech32) { + if (decodeBech32.prefix !== network.bech32) + throw new Error(address + ' has an invalid prefix'); + if (decodeBech32.version === 0) { + if (decodeBech32.data.length === 20) + return payments.p2wpkh({ hash: decodeBech32.data }).output; + if (decodeBech32.data.length === 32) + return payments.p2wsh({ hash: decodeBech32.data }).output; + } } - catch (e) { } - if (decodeBase58) { - if (decodeBase58.version === network.pubKeyHash) - return payments.p2pkh({ hash: decodeBase58.hash }).output; - if (decodeBase58.version === network.scriptHash) - return payments.p2sh({ hash: decodeBase58.hash }).output; - } - else { - try { - decodeBech32 = fromBech32(address); - } - catch (e) { } - if (decodeBech32) { - if (decodeBech32.prefix !== network.bech32) - throw new Error(address + ' has an invalid prefix'); - if (decodeBech32.version === 0) { - if (decodeBech32.data.length === 20) - return payments.p2wpkh({ hash: decodeBech32.data }).output; - if (decodeBech32.data.length === 32) - return payments.p2wsh({ hash: decodeBech32.data }).output; - } - } - } - throw new Error(address + ' has no matching Script'); + } + throw new Error(address + ' has no matching Script'); } exports.toOutputScript = toOutputScript; diff --git a/src/block.js b/src/block.js index da17193..22449fd 100644 --- a/src/block.js +++ b/src/block.js @@ -1,222 +1,242 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const bufferutils_1 = require("./bufferutils"); -const bcrypto = require("./crypto"); -const transaction_1 = require("./transaction"); -const types = require("./types"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const bufferutils_1 = require('./bufferutils'); +const bcrypto = require('./crypto'); +const transaction_1 = require('./transaction'); +const types = require('./types'); const fastMerkleRoot = require('merkle-lib/fastRoot'); const typeforce = require('typeforce'); const varuint = require('varuint-bitcoin'); -const errorMerkleNoTxes = new TypeError('Cannot compute merkle root for zero transactions'); -const errorWitnessNotSegwit = new TypeError('Cannot compute witness commit for non-segwit block'); +const errorMerkleNoTxes = new TypeError( + 'Cannot compute merkle root for zero transactions', +); +const errorWitnessNotSegwit = new TypeError( + 'Cannot compute witness commit for non-segwit block', +); class Block { - constructor() { - this.version = 1; - this.prevHash = undefined; - this.merkleRoot = undefined; - this.timestamp = 0; - this.witnessCommit = undefined; - this.bits = 0; - this.nonce = 0; - this.transactions = undefined; - } - static fromBuffer(buffer) { - if (buffer.length < 80) - throw new Error('Buffer too small (< 80 bytes)'); - let offset = 0; - const readSlice = (n) => { - offset += n; - return buffer.slice(offset - n, offset); - }; - const readUInt32 = () => { - const i = buffer.readUInt32LE(offset); - offset += 4; - return i; - }; - const readInt32 = () => { - const i = buffer.readInt32LE(offset); - offset += 4; - return i; - }; - const block = new Block(); - block.version = readInt32(); - block.prevHash = readSlice(32); - block.merkleRoot = readSlice(32); - block.timestamp = readUInt32(); - block.bits = readUInt32(); - block.nonce = readUInt32(); - if (buffer.length === 80) - return block; - const readVarInt = () => { - const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; - return vi; - }; - const readTransaction = () => { - const tx = transaction_1.Transaction.fromBuffer(buffer.slice(offset), true); - offset += tx.byteLength(); - return tx; - }; - const nTransactions = readVarInt(); - block.transactions = []; - for (let i = 0; i < nTransactions; ++i) { - const tx = readTransaction(); - block.transactions.push(tx); - } - const witnessCommit = block.getWitnessCommit(); - // This Block contains a witness commit - if (witnessCommit) - block.witnessCommit = witnessCommit; - return block; - } - static fromHex(hex) { - return Block.fromBuffer(Buffer.from(hex, 'hex')); - } - static calculateTarget(bits) { - const exponent = ((bits & 0xff000000) >> 24) - 3; - const mantissa = bits & 0x007fffff; - const target = Buffer.alloc(32, 0); - target.writeUIntBE(mantissa, 29 - exponent, 3); - return target; - } - static calculateMerkleRoot(transactions, forWitness) { - typeforce([{ getHash: types.Function }], transactions); - if (transactions.length === 0) - throw errorMerkleNoTxes; - if (forWitness && !txesHaveWitnessCommit(transactions)) - throw errorWitnessNotSegwit; - const hashes = transactions.map(transaction => transaction.getHash(forWitness)); - const rootHash = fastMerkleRoot(hashes, bcrypto.hash256); - return forWitness - ? bcrypto.hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) - : rootHash; - } - getWitnessCommit() { - if (!txesHaveWitnessCommit(this.transactions)) - return null; - // The merkle root for the witness data is in an OP_RETURN output. - // There is no rule for the index of the output, so use filter to find it. - // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed - // If multiple commits are found, the output with highest index is assumed. - const witnessCommits = this.transactions[0].outs.filter(out => out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex'))).map(out => out.script.slice(6, 38)); - if (witnessCommits.length === 0) - return null; - // Use the commit with the highest output (should only be one though) - const result = witnessCommits[witnessCommits.length - 1]; - if (!(result instanceof Buffer && result.length === 32)) - return null; - return result; - } - hasWitnessCommit() { - if (this.witnessCommit instanceof Buffer && - this.witnessCommit.length === 32) - return true; - if (this.getWitnessCommit() !== null) - return true; - return false; - } - hasWitness() { - return anyTxHasWitness(this.transactions); - } - byteLength(headersOnly) { - if (headersOnly || !this.transactions) - return 80; - return (80 + - varuint.encodingLength(this.transactions.length) + - this.transactions.reduce((a, x) => a + x.byteLength(), 0)); - } - getHash() { - return bcrypto.hash256(this.toBuffer(true)); - } - getId() { - return bufferutils_1.reverseBuffer(this.getHash()).toString('hex'); - } - getUTCDate() { - const date = new Date(0); // epoch - date.setUTCSeconds(this.timestamp); - return date; - } - // TODO: buffer, offset compatibility - toBuffer(headersOnly) { - const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); - let offset = 0; - const writeSlice = (slice) => { - slice.copy(buffer, offset); - offset += slice.length; - }; - const writeInt32 = (i) => { - buffer.writeInt32LE(i, offset); - offset += 4; - }; - const writeUInt32 = (i) => { - buffer.writeUInt32LE(i, offset); - offset += 4; - }; - writeInt32(this.version); - writeSlice(this.prevHash); - writeSlice(this.merkleRoot); - writeUInt32(this.timestamp); - writeUInt32(this.bits); - writeUInt32(this.nonce); - if (headersOnly || !this.transactions) - return buffer; - varuint.encode(this.transactions.length, buffer, offset); - offset += varuint.encode.bytes; - this.transactions.forEach(tx => { - const txSize = tx.byteLength(); // TODO: extract from toBuffer? - tx.toBuffer(buffer, offset); - offset += txSize; - }); - return buffer; - } - toHex(headersOnly) { - return this.toBuffer(headersOnly).toString('hex'); - } - checkTxRoots() { - // If the Block has segwit transactions but no witness commit, - // there's no way it can be valid, so fail the check. - const hasWitnessCommit = this.hasWitnessCommit(); - if (!hasWitnessCommit && this.hasWitness()) - return false; - return (this.__checkMerkleRoot() && - (hasWitnessCommit ? this.__checkWitnessCommit() : true)); - } - checkProofOfWork() { - const hash = bufferutils_1.reverseBuffer(this.getHash()); - const target = Block.calculateTarget(this.bits); - return hash.compare(target) <= 0; - } - __checkMerkleRoot() { - if (!this.transactions) - throw errorMerkleNoTxes; - const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions); - return this.merkleRoot.compare(actualMerkleRoot) === 0; - } - __checkWitnessCommit() { - if (!this.transactions) - throw errorMerkleNoTxes; - if (!this.hasWitnessCommit()) - throw errorWitnessNotSegwit; - const actualWitnessCommit = Block.calculateMerkleRoot(this.transactions, true); - return this.witnessCommit.compare(actualWitnessCommit) === 0; + constructor() { + this.version = 1; + this.prevHash = undefined; + this.merkleRoot = undefined; + this.timestamp = 0; + this.witnessCommit = undefined; + this.bits = 0; + this.nonce = 0; + this.transactions = undefined; + } + static fromBuffer(buffer) { + if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); + let offset = 0; + const readSlice = n => { + offset += n; + return buffer.slice(offset - n, offset); + }; + const readUInt32 = () => { + const i = buffer.readUInt32LE(offset); + offset += 4; + return i; + }; + const readInt32 = () => { + const i = buffer.readInt32LE(offset); + offset += 4; + return i; + }; + const block = new Block(); + block.version = readInt32(); + block.prevHash = readSlice(32); + block.merkleRoot = readSlice(32); + block.timestamp = readUInt32(); + block.bits = readUInt32(); + block.nonce = readUInt32(); + if (buffer.length === 80) return block; + const readVarInt = () => { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + }; + const readTransaction = () => { + const tx = transaction_1.Transaction.fromBuffer( + buffer.slice(offset), + true, + ); + offset += tx.byteLength(); + return tx; + }; + const nTransactions = readVarInt(); + block.transactions = []; + for (let i = 0; i < nTransactions; ++i) { + const tx = readTransaction(); + block.transactions.push(tx); } + const witnessCommit = block.getWitnessCommit(); + // This Block contains a witness commit + if (witnessCommit) block.witnessCommit = witnessCommit; + return block; + } + static fromHex(hex) { + return Block.fromBuffer(Buffer.from(hex, 'hex')); + } + static calculateTarget(bits) { + const exponent = ((bits & 0xff000000) >> 24) - 3; + const mantissa = bits & 0x007fffff; + const target = Buffer.alloc(32, 0); + target.writeUIntBE(mantissa, 29 - exponent, 3); + return target; + } + static calculateMerkleRoot(transactions, forWitness) { + typeforce([{ getHash: types.Function }], transactions); + if (transactions.length === 0) throw errorMerkleNoTxes; + if (forWitness && !txesHaveWitnessCommit(transactions)) + throw errorWitnessNotSegwit; + const hashes = transactions.map(transaction => + transaction.getHash(forWitness), + ); + const rootHash = fastMerkleRoot(hashes, bcrypto.hash256); + return forWitness + ? bcrypto.hash256( + Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]), + ) + : rootHash; + } + getWitnessCommit() { + if (!txesHaveWitnessCommit(this.transactions)) return null; + // The merkle root for the witness data is in an OP_RETURN output. + // There is no rule for the index of the output, so use filter to find it. + // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed + // If multiple commits are found, the output with highest index is assumed. + const witnessCommits = this.transactions[0].outs + .filter(out => + out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')), + ) + .map(out => out.script.slice(6, 38)); + if (witnessCommits.length === 0) return null; + // Use the commit with the highest output (should only be one though) + const result = witnessCommits[witnessCommits.length - 1]; + if (!(result instanceof Buffer && result.length === 32)) return null; + return result; + } + hasWitnessCommit() { + if ( + this.witnessCommit instanceof Buffer && + this.witnessCommit.length === 32 + ) + return true; + if (this.getWitnessCommit() !== null) return true; + return false; + } + hasWitness() { + return anyTxHasWitness(this.transactions); + } + byteLength(headersOnly) { + if (headersOnly || !this.transactions) return 80; + return ( + 80 + + varuint.encodingLength(this.transactions.length) + + this.transactions.reduce((a, x) => a + x.byteLength(), 0) + ); + } + getHash() { + return bcrypto.hash256(this.toBuffer(true)); + } + getId() { + return bufferutils_1.reverseBuffer(this.getHash()).toString('hex'); + } + getUTCDate() { + const date = new Date(0); // epoch + date.setUTCSeconds(this.timestamp); + return date; + } + // TODO: buffer, offset compatibility + toBuffer(headersOnly) { + const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); + let offset = 0; + const writeSlice = slice => { + slice.copy(buffer, offset); + offset += slice.length; + }; + const writeInt32 = i => { + buffer.writeInt32LE(i, offset); + offset += 4; + }; + const writeUInt32 = i => { + buffer.writeUInt32LE(i, offset); + offset += 4; + }; + writeInt32(this.version); + writeSlice(this.prevHash); + writeSlice(this.merkleRoot); + writeUInt32(this.timestamp); + writeUInt32(this.bits); + writeUInt32(this.nonce); + if (headersOnly || !this.transactions) return buffer; + varuint.encode(this.transactions.length, buffer, offset); + offset += varuint.encode.bytes; + this.transactions.forEach(tx => { + const txSize = tx.byteLength(); // TODO: extract from toBuffer? + tx.toBuffer(buffer, offset); + offset += txSize; + }); + return buffer; + } + toHex(headersOnly) { + return this.toBuffer(headersOnly).toString('hex'); + } + checkTxRoots() { + // If the Block has segwit transactions but no witness commit, + // there's no way it can be valid, so fail the check. + const hasWitnessCommit = this.hasWitnessCommit(); + if (!hasWitnessCommit && this.hasWitness()) return false; + return ( + this.__checkMerkleRoot() && + (hasWitnessCommit ? this.__checkWitnessCommit() : true) + ); + } + checkProofOfWork() { + const hash = bufferutils_1.reverseBuffer(this.getHash()); + const target = Block.calculateTarget(this.bits); + return hash.compare(target) <= 0; + } + __checkMerkleRoot() { + if (!this.transactions) throw errorMerkleNoTxes; + const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions); + return this.merkleRoot.compare(actualMerkleRoot) === 0; + } + __checkWitnessCommit() { + if (!this.transactions) throw errorMerkleNoTxes; + if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit; + const actualWitnessCommit = Block.calculateMerkleRoot( + this.transactions, + true, + ); + return this.witnessCommit.compare(actualWitnessCommit) === 0; + } } exports.Block = Block; function txesHaveWitnessCommit(transactions) { - return (transactions instanceof Array && - transactions[0] && - transactions[0].ins && - transactions[0].ins instanceof Array && - transactions[0].ins[0] && - transactions[0].ins[0].witness && - transactions[0].ins[0].witness instanceof Array && - transactions[0].ins[0].witness.length > 0); + return ( + transactions instanceof Array && + transactions[0] && + transactions[0].ins && + transactions[0].ins instanceof Array && + transactions[0].ins[0] && + transactions[0].ins[0].witness && + transactions[0].ins[0].witness instanceof Array && + transactions[0].ins[0].witness.length > 0 + ); } function anyTxHasWitness(transactions) { - return (transactions instanceof Array && - transactions.some(tx => typeof tx === 'object' && - tx.ins instanceof Array && - tx.ins.some(input => typeof input === 'object' && - input.witness instanceof Array && - input.witness.length > 0))); + return ( + transactions instanceof Array && + transactions.some( + tx => + typeof tx === 'object' && + tx.ins instanceof Array && + tx.ins.some( + input => + typeof input === 'object' && + input.witness instanceof Array && + input.witness.length > 0, + ), + ) + ); } diff --git a/src/bufferutils.js b/src/bufferutils.js index f768250..54ce1c9 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -1,42 +1,40 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); // https://github.com/feross/buffer/blob/master/index.js#L1127 function verifuint(value, max) { - if (typeof value !== 'number') - throw new Error('cannot write a non-number as a number'); - if (value < 0) - throw new Error('specified a negative value for writing an unsigned value'); - if (value > max) - throw new Error('RangeError: value out of range'); - if (Math.floor(value) !== value) - throw new Error('value has a fractional component'); + if (typeof value !== 'number') + throw new Error('cannot write a non-number as a number'); + if (value < 0) + throw new Error('specified a negative value for writing an unsigned value'); + if (value > max) throw new Error('RangeError: value out of range'); + if (Math.floor(value) !== value) + throw new Error('value has a fractional component'); } function readUInt64LE(buffer, offset) { - const a = buffer.readUInt32LE(offset); - let b = buffer.readUInt32LE(offset + 4); - b *= 0x100000000; - verifuint(b + a, 0x001fffffffffffff); - return b + a; + const a = buffer.readUInt32LE(offset); + let b = buffer.readUInt32LE(offset + 4); + b *= 0x100000000; + verifuint(b + a, 0x001fffffffffffff); + return b + a; } exports.readUInt64LE = readUInt64LE; function writeUInt64LE(buffer, value, offset) { - verifuint(value, 0x001fffffffffffff); - buffer.writeInt32LE(value & -1, offset); - buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); - return offset + 8; + verifuint(value, 0x001fffffffffffff); + buffer.writeInt32LE(value & -1, offset); + buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4); + return offset + 8; } exports.writeUInt64LE = writeUInt64LE; function reverseBuffer(buffer) { - if (buffer.length < 1) - return buffer; - let j = buffer.length - 1; - let tmp = 0; - for (let i = 0; i < buffer.length / 2; i++) { - tmp = buffer[i]; - buffer[i] = buffer[j]; - buffer[j] = tmp; - j--; - } - return buffer; + if (buffer.length < 1) return buffer; + let j = buffer.length - 1; + let tmp = 0; + for (let i = 0; i < buffer.length / 2; i++) { + tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + j--; + } + return buffer; } exports.reverseBuffer = reverseBuffer; diff --git a/src/classify.js b/src/classify.js index 7d8e57d..70c600c 100644 --- a/src/classify.js +++ b/src/classify.js @@ -1,75 +1,59 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const script_1 = require("./script"); -const multisig = require("./templates/multisig"); -const nullData = require("./templates/nulldata"); -const pubKey = require("./templates/pubkey"); -const pubKeyHash = require("./templates/pubkeyhash"); -const scriptHash = require("./templates/scripthash"); -const witnessCommitment = require("./templates/witnesscommitment"); -const witnessPubKeyHash = require("./templates/witnesspubkeyhash"); -const witnessScriptHash = require("./templates/witnessscripthash"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const script_1 = require('./script'); +const multisig = require('./templates/multisig'); +const nullData = require('./templates/nulldata'); +const pubKey = require('./templates/pubkey'); +const pubKeyHash = require('./templates/pubkeyhash'); +const scriptHash = require('./templates/scripthash'); +const witnessCommitment = require('./templates/witnesscommitment'); +const witnessPubKeyHash = require('./templates/witnesspubkeyhash'); +const witnessScriptHash = require('./templates/witnessscripthash'); const types = { - P2MS: 'multisig', - NONSTANDARD: 'nonstandard', - NULLDATA: 'nulldata', - P2PK: 'pubkey', - P2PKH: 'pubkeyhash', - P2SH: 'scripthash', - P2WPKH: 'witnesspubkeyhash', - P2WSH: 'witnessscripthash', - WITNESS_COMMITMENT: 'witnesscommitment', + P2MS: 'multisig', + NONSTANDARD: 'nonstandard', + NULLDATA: 'nulldata', + P2PK: 'pubkey', + P2PKH: 'pubkeyhash', + P2SH: 'scripthash', + P2WPKH: 'witnesspubkeyhash', + P2WSH: 'witnessscripthash', + WITNESS_COMMITMENT: 'witnesscommitment', }; exports.types = types; function classifyOutput(script) { - if (witnessPubKeyHash.output.check(script)) - return types.P2WPKH; - if (witnessScriptHash.output.check(script)) - return types.P2WSH; - if (pubKeyHash.output.check(script)) - return types.P2PKH; - if (scriptHash.output.check(script)) - return types.P2SH; - // XXX: optimization, below functions .decompile before use - const chunks = script_1.decompile(script); - if (!chunks) - throw new TypeError('Invalid script'); - if (multisig.output.check(chunks)) - return types.P2MS; - if (pubKey.output.check(chunks)) - return types.P2PK; - if (witnessCommitment.output.check(chunks)) - return types.WITNESS_COMMITMENT; - if (nullData.output.check(chunks)) - return types.NULLDATA; - return types.NONSTANDARD; + if (witnessPubKeyHash.output.check(script)) return types.P2WPKH; + if (witnessScriptHash.output.check(script)) return types.P2WSH; + if (pubKeyHash.output.check(script)) return types.P2PKH; + if (scriptHash.output.check(script)) return types.P2SH; + // XXX: optimization, below functions .decompile before use + const chunks = script_1.decompile(script); + if (!chunks) throw new TypeError('Invalid script'); + if (multisig.output.check(chunks)) return types.P2MS; + if (pubKey.output.check(chunks)) return types.P2PK; + if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT; + if (nullData.output.check(chunks)) return types.NULLDATA; + return types.NONSTANDARD; } exports.output = classifyOutput; function classifyInput(script, allowIncomplete) { - // XXX: optimization, below functions .decompile before use - const chunks = script_1.decompile(script); - if (!chunks) - throw new TypeError('Invalid script'); - if (pubKeyHash.input.check(chunks)) - return types.P2PKH; - if (scriptHash.input.check(chunks, allowIncomplete)) - return types.P2SH; - if (multisig.input.check(chunks, allowIncomplete)) - return types.P2MS; - if (pubKey.input.check(chunks)) - return types.P2PK; - return types.NONSTANDARD; + // XXX: optimization, below functions .decompile before use + const chunks = script_1.decompile(script); + if (!chunks) throw new TypeError('Invalid script'); + if (pubKeyHash.input.check(chunks)) return types.P2PKH; + if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH; + if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS; + if (pubKey.input.check(chunks)) return types.P2PK; + return types.NONSTANDARD; } exports.input = classifyInput; function classifyWitness(script, allowIncomplete) { - // XXX: optimization, below functions .decompile before use - const chunks = script_1.decompile(script); - if (!chunks) - throw new TypeError('Invalid script'); - if (witnessPubKeyHash.input.check(chunks)) - return types.P2WPKH; - if (witnessScriptHash.input.check(chunks, allowIncomplete)) - return types.P2WSH; - return types.NONSTANDARD; + // XXX: optimization, below functions .decompile before use + const chunks = script_1.decompile(script); + if (!chunks) throw new TypeError('Invalid script'); + if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH; + if (witnessScriptHash.input.check(chunks, allowIncomplete)) + return types.P2WSH; + return types.NONSTANDARD; } exports.witness = classifyWitness; diff --git a/src/crypto.js b/src/crypto.js index 38ec4f9..e7dd596 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -1,36 +1,35 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); const createHash = require('create-hash'); function ripemd160(buffer) { - try { - return createHash('rmd160') - .update(buffer) - .digest(); - } - catch (err) { - return createHash('ripemd160') - .update(buffer) - .digest(); - } + try { + return createHash('rmd160') + .update(buffer) + .digest(); + } catch (err) { + return createHash('ripemd160') + .update(buffer) + .digest(); + } } exports.ripemd160 = ripemd160; function sha1(buffer) { - return createHash('sha1') - .update(buffer) - .digest(); + return createHash('sha1') + .update(buffer) + .digest(); } exports.sha1 = sha1; function sha256(buffer) { - return createHash('sha256') - .update(buffer) - .digest(); + return createHash('sha256') + .update(buffer) + .digest(); } exports.sha256 = sha256; function hash160(buffer) { - return ripemd160(sha256(buffer)); + return ripemd160(sha256(buffer)); } exports.hash160 = hash160; function hash256(buffer) { - return sha256(sha256(buffer)); + return sha256(sha256(buffer)); } exports.hash256 = hash256; diff --git a/src/ecpair.js b/src/ecpair.js index 2026c63..9acdd3c 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -1,98 +1,91 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const NETWORKS = require("./networks"); -const types = require("./types"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const NETWORKS = require('./networks'); +const types = require('./types'); const ecc = require('tiny-secp256k1'); const randomBytes = require('randombytes'); const typeforce = require('typeforce'); const wif = require('wif'); -const isOptions = typeforce.maybe(typeforce.compile({ +const isOptions = typeforce.maybe( + typeforce.compile({ compressed: types.maybe(types.Boolean), network: types.maybe(types.Network), -})); + }), +); class ECPair { - constructor(__D, __Q, options) { - this.__D = __D; - this.__Q = __Q; - if (options === undefined) - options = {}; - this.compressed = - options.compressed === undefined ? true : options.compressed; - this.network = options.network || NETWORKS.bitcoin; - if (__Q !== undefined) - this.__Q = ecc.pointCompress(__Q, this.compressed); - } - get privateKey() { - return this.__D; - } - get publicKey() { - if (!this.__Q) - this.__Q = ecc.pointFromScalar(this.__D, this.compressed); - return this.__Q; - } - toWIF() { - if (!this.__D) - throw new Error('Missing private key'); - return wif.encode(this.network.wif, this.__D, this.compressed); - } - sign(hash) { - if (!this.__D) - throw new Error('Missing private key'); - return ecc.sign(hash, this.__D); - } - verify(hash, signature) { - return ecc.verify(hash, this.publicKey, signature); - } + constructor(__D, __Q, options) { + this.__D = __D; + this.__Q = __Q; + if (options === undefined) options = {}; + this.compressed = + options.compressed === undefined ? true : options.compressed; + this.network = options.network || NETWORKS.bitcoin; + if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed); + } + get privateKey() { + return this.__D; + } + get publicKey() { + if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__D, this.compressed); + return this.__Q; + } + toWIF() { + if (!this.__D) throw new Error('Missing private key'); + return wif.encode(this.network.wif, this.__D, this.compressed); + } + sign(hash) { + if (!this.__D) throw new Error('Missing private key'); + return ecc.sign(hash, this.__D); + } + verify(hash, signature) { + return ecc.verify(hash, this.publicKey, signature); + } } function fromPrivateKey(buffer, options) { - typeforce(types.Buffer256bit, buffer); - if (!ecc.isPrivate(buffer)) - throw new TypeError('Private key not in range [1, n)'); - typeforce(isOptions, options); - return new ECPair(buffer, undefined, options); + typeforce(types.Buffer256bit, buffer); + if (!ecc.isPrivate(buffer)) + throw new TypeError('Private key not in range [1, n)'); + typeforce(isOptions, options); + return new ECPair(buffer, undefined, options); } exports.fromPrivateKey = fromPrivateKey; function fromPublicKey(buffer, options) { - typeforce(ecc.isPoint, buffer); - typeforce(isOptions, options); - return new ECPair(undefined, buffer, options); + typeforce(ecc.isPoint, buffer); + typeforce(isOptions, options); + return new ECPair(undefined, buffer, options); } exports.fromPublicKey = fromPublicKey; function fromWIF(wifString, network) { - const decoded = wif.decode(wifString); - const version = decoded.version; - // list of networks? - if (types.Array(network)) { - network = network - .filter((x) => { - return version === x.wif; - }) - .pop(); - if (!network) - throw new Error('Unknown network version'); - // otherwise, assume a network object (or default to bitcoin) - } - else { - network = network || NETWORKS.bitcoin; - if (version !== network.wif) - throw new Error('Invalid network version'); - } - return fromPrivateKey(decoded.privateKey, { - compressed: decoded.compressed, - network: network, - }); + const decoded = wif.decode(wifString); + const version = decoded.version; + // list of networks? + if (types.Array(network)) { + network = network + .filter(x => { + return version === x.wif; + }) + .pop(); + if (!network) throw new Error('Unknown network version'); + // otherwise, assume a network object (or default to bitcoin) + } else { + network = network || NETWORKS.bitcoin; + if (version !== network.wif) throw new Error('Invalid network version'); + } + return fromPrivateKey(decoded.privateKey, { + compressed: decoded.compressed, + network: network, + }); } exports.fromWIF = fromWIF; function makeRandom(options) { - typeforce(isOptions, options); - if (options === undefined) - options = {}; - const rng = options.rng || randomBytes; - let d; - do { - d = rng(32); - typeforce(types.Buffer256bit, d); - } while (!ecc.isPrivate(d)); - return fromPrivateKey(d, options); + typeforce(isOptions, options); + if (options === undefined) options = {}; + const rng = options.rng || randomBytes; + let d; + do { + d = rng(32); + typeforce(types.Buffer256bit, d); + } while (!ecc.isPrivate(d)); + return fromPrivateKey(d, options); } exports.makeRandom = makeRandom; diff --git a/src/index.js b/src/index.js index 73ac6f4..499380e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,24 +1,24 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const bip32 = require("bip32"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const bip32 = require('bip32'); exports.bip32 = bip32; -const address = require("./address"); +const address = require('./address'); exports.address = address; -const crypto = require("./crypto"); +const crypto = require('./crypto'); exports.crypto = crypto; -const ECPair = require("./ecpair"); +const ECPair = require('./ecpair'); exports.ECPair = ECPair; -const networks = require("./networks"); +const networks = require('./networks'); exports.networks = networks; -const payments = require("./payments"); +const payments = require('./payments'); exports.payments = payments; -const script = require("./script"); +const script = require('./script'); exports.script = script; -var block_1 = require("./block"); +var block_1 = require('./block'); exports.Block = block_1.Block; -var script_1 = require("./script"); +var script_1 = require('./script'); exports.opcodes = script_1.OPS; -var transaction_1 = require("./transaction"); +var transaction_1 = require('./transaction'); exports.Transaction = transaction_1.Transaction; -var transaction_builder_1 = require("./transaction_builder"); +var transaction_builder_1 = require('./transaction_builder'); exports.TransactionBuilder = transaction_builder_1.TransactionBuilder; diff --git a/src/networks.js b/src/networks.js index 298808d..0c31fe1 100644 --- a/src/networks.js +++ b/src/networks.js @@ -1,35 +1,35 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); exports.bitcoin = { - messagePrefix: '\x18Bitcoin Signed Message:\n', - bech32: 'bc', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4, - }, - pubKeyHash: 0x00, - scriptHash: 0x05, - wif: 0x80, + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'bc', + bip32: { + public: 0x0488b21e, + private: 0x0488ade4, + }, + pubKeyHash: 0x00, + scriptHash: 0x05, + wif: 0x80, }; exports.regtest = { - messagePrefix: '\x18Bitcoin Signed Message:\n', - bech32: 'bcrt', - bip32: { - public: 0x043587cf, - private: 0x04358394, - }, - pubKeyHash: 0x6f, - scriptHash: 0xc4, - wif: 0xef, + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'bcrt', + bip32: { + public: 0x043587cf, + private: 0x04358394, + }, + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef, }; exports.testnet = { - messagePrefix: '\x18Bitcoin Signed Message:\n', - bech32: 'tb', - bip32: { - public: 0x043587cf, - private: 0x04358394, - }, - pubKeyHash: 0x6f, - scriptHash: 0xc4, - wif: 0xef, + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'tb', + bip32: { + public: 0x043587cf, + private: 0x04358394, + }, + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef, }; diff --git a/src/payments/embed.js b/src/payments/embed.js index 800c8de..3ddceb9 100644 --- a/src/payments/embed.js +++ b/src/payments/embed.js @@ -1,51 +1,49 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const networks_1 = require("../networks"); -const bscript = require("../script"); -const lazy = require("./lazy"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const networks_1 = require('../networks'); +const bscript = require('../script'); +const lazy = require('./lazy'); const typef = require('typeforce'); const OPS = bscript.OPS; function stacksEqual(a, b) { - if (a.length !== b.length) - return false; - return a.every((x, i) => { - return x.equals(b[i]); - }); + if (a.length !== b.length) return false; + return a.every((x, i) => { + return x.equals(b[i]); + }); } // output: OP_RETURN ... function p2data(a, opts) { - if (!a.data && !a.output) - throw new TypeError('Not enough data'); - opts = Object.assign({ validate: true }, opts || {}); - typef({ - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - data: typef.maybe(typef.arrayOf(typef.Buffer)), - }, a); - const network = a.network || networks_1.bitcoin; - const o = { network }; - lazy.prop(o, 'output', () => { - if (!a.data) - return; - return bscript.compile([OPS.OP_RETURN].concat(a.data)); - }); - lazy.prop(o, 'data', () => { - if (!a.output) - return; - return bscript.decompile(a.output).slice(1); - }); - // extended validation - if (opts.validate) { - if (a.output) { - const chunks = bscript.decompile(a.output); - if (chunks[0] !== OPS.OP_RETURN) - throw new TypeError('Output is invalid'); - if (!chunks.slice(1).every(typef.Buffer)) - throw new TypeError('Output is invalid'); - if (a.data && !stacksEqual(a.data, o.data)) - throw new TypeError('Data mismatch'); - } + if (!a.data && !a.output) throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef( + { + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + data: typef.maybe(typef.arrayOf(typef.Buffer)), + }, + a, + ); + const network = a.network || networks_1.bitcoin; + const o = { network }; + lazy.prop(o, 'output', () => { + if (!a.data) return; + return bscript.compile([OPS.OP_RETURN].concat(a.data)); + }); + lazy.prop(o, 'data', () => { + if (!a.output) return; + return bscript.decompile(a.output).slice(1); + }); + // extended validation + if (opts.validate) { + if (a.output) { + const chunks = bscript.decompile(a.output); + if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid'); + if (!chunks.slice(1).every(typef.Buffer)) + throw new TypeError('Output is invalid'); + if (a.data && !stacksEqual(a.data, o.data)) + throw new TypeError('Data mismatch'); } - return Object.assign(o, a); + } + return Object.assign(o, a); } exports.p2data = p2data; diff --git a/src/payments/index.js b/src/payments/index.js index f21762d..ddab977 100644 --- a/src/payments/index.js +++ b/src/payments/index.js @@ -1,18 +1,18 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const embed_1 = require("./embed"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const embed_1 = require('./embed'); exports.embed = embed_1.p2data; -const p2ms_1 = require("./p2ms"); +const p2ms_1 = require('./p2ms'); exports.p2ms = p2ms_1.p2ms; -const p2pk_1 = require("./p2pk"); +const p2pk_1 = require('./p2pk'); exports.p2pk = p2pk_1.p2pk; -const p2pkh_1 = require("./p2pkh"); +const p2pkh_1 = require('./p2pkh'); exports.p2pkh = p2pkh_1.p2pkh; -const p2sh_1 = require("./p2sh"); +const p2sh_1 = require('./p2sh'); exports.p2sh = p2sh_1.p2sh; -const p2wpkh_1 = require("./p2wpkh"); +const p2wpkh_1 = require('./p2wpkh'); exports.p2wpkh = p2wpkh_1.p2wpkh; -const p2wsh_1 = require("./p2wsh"); +const p2wsh_1 = require('./p2wsh'); exports.p2wsh = p2wsh_1.p2wsh; // TODO // witness commitment diff --git a/src/payments/lazy.js b/src/payments/lazy.js index d8494fd..1a71521 100644 --- a/src/payments/lazy.js +++ b/src/payments/lazy.js @@ -1,32 +1,31 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); function prop(object, name, f) { - Object.defineProperty(object, name, { + Object.defineProperty(object, name, { + configurable: true, + enumerable: true, + get() { + const _value = f.call(this); + this[name] = _value; + return _value; + }, + set(_value) { + Object.defineProperty(this, name, { configurable: true, enumerable: true, - get() { - const _value = f.call(this); - this[name] = _value; - return _value; - }, - set(_value) { - Object.defineProperty(this, name, { - configurable: true, - enumerable: true, - value: _value, - writable: true, - }); - }, - }); + value: _value, + writable: true, + }); + }, + }); } exports.prop = prop; function value(f) { - let _value; - return () => { - if (_value !== undefined) - return _value; - _value = f(); - return _value; - }; + let _value; + return () => { + if (_value !== undefined) return _value; + _value = f(); + return _value; + }; } exports.value = value; diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index 5e48432..1e7c6ba 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -1,141 +1,141 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const networks_1 = require("../networks"); -const bscript = require("../script"); -const lazy = require("./lazy"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const networks_1 = require('../networks'); +const bscript = require('../script'); +const lazy = require('./lazy'); const OPS = bscript.OPS; const typef = require('typeforce'); const ecc = require('tiny-secp256k1'); const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 function stacksEqual(a, b) { - if (a.length !== b.length) - return false; - return a.every((x, i) => { - return x.equals(b[i]); - }); + if (a.length !== b.length) return false; + return a.every((x, i) => { + return x.equals(b[i]); + }); } // input: OP_0 [signatures ...] // output: m [pubKeys ...] n OP_CHECKMULTISIG function p2ms(a, opts) { - if (!a.input && - !a.output && - !(a.pubkeys && a.m !== undefined) && - !a.signatures) - throw new TypeError('Not enough data'); - opts = Object.assign({ validate: true }, opts || {}); - function isAcceptableSignature(x) { - return (bscript.isCanonicalScriptSignature(x) || - (opts.allowIncomplete && x === OPS.OP_0) !== undefined); + if ( + !a.input && + !a.output && + !(a.pubkeys && a.m !== undefined) && + !a.signatures + ) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + function isAcceptableSignature(x) { + return ( + bscript.isCanonicalScriptSignature(x) || + (opts.allowIncomplete && x === OPS.OP_0) !== undefined + ); + } + typef( + { + network: typef.maybe(typef.Object), + m: typef.maybe(typef.Number), + n: typef.maybe(typef.Number), + output: typef.maybe(typef.Buffer), + pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), + signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), + input: typef.maybe(typef.Buffer), + }, + a, + ); + const network = a.network || networks_1.bitcoin; + const o = { network }; + let chunks = []; + let decoded = false; + function decode(output) { + if (decoded) return; + decoded = true; + chunks = bscript.decompile(output); + o.m = chunks[0] - OP_INT_BASE; + o.n = chunks[chunks.length - 2] - OP_INT_BASE; + o.pubkeys = chunks.slice(1, -2); + } + lazy.prop(o, 'output', () => { + if (!a.m) return; + if (!o.n) return; + if (!a.pubkeys) return; + return bscript.compile( + [].concat( + OP_INT_BASE + a.m, + a.pubkeys, + OP_INT_BASE + o.n, + OPS.OP_CHECKMULTISIG, + ), + ); + }); + lazy.prop(o, 'm', () => { + if (!o.output) return; + decode(o.output); + return o.m; + }); + lazy.prop(o, 'n', () => { + if (!o.pubkeys) return; + return o.pubkeys.length; + }); + lazy.prop(o, 'pubkeys', () => { + if (!a.output) return; + decode(a.output); + return o.pubkeys; + }); + lazy.prop(o, 'signatures', () => { + if (!a.input) return; + return bscript.decompile(a.input).slice(1); + }); + lazy.prop(o, 'input', () => { + if (!a.signatures) return; + return bscript.compile([OPS.OP_0].concat(a.signatures)); + }); + lazy.prop(o, 'witness', () => { + if (!o.input) return; + return []; + }); + // extended validation + if (opts.validate) { + if (a.output) { + decode(a.output); + if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid'); + if (!typef.Number(chunks[chunks.length - 2])) + throw new TypeError('Output is invalid'); + if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) + throw new TypeError('Output is invalid'); + if (o.m <= 0 || o.n > 16 || o.m > o.n || o.n !== chunks.length - 3) + throw new TypeError('Output is invalid'); + if (!o.pubkeys.every(x => ecc.isPoint(x))) + throw new TypeError('Output is invalid'); + if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch'); + if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch'); + if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys)) + throw new TypeError('Pubkeys mismatch'); } - typef({ - network: typef.maybe(typef.Object), - m: typef.maybe(typef.Number), - n: typef.maybe(typef.Number), - output: typef.maybe(typef.Buffer), - pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), - signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), - input: typef.maybe(typef.Buffer), - }, a); - const network = a.network || networks_1.bitcoin; - const o = { network }; - let chunks = []; - let decoded = false; - function decode(output) { - if (decoded) - return; - decoded = true; - chunks = bscript.decompile(output); - o.m = chunks[0] - OP_INT_BASE; - o.n = chunks[chunks.length - 2] - OP_INT_BASE; - o.pubkeys = chunks.slice(1, -2); + if (a.pubkeys) { + if (a.n !== undefined && a.n !== a.pubkeys.length) + throw new TypeError('Pubkey count mismatch'); + o.n = a.pubkeys.length; + if (o.n < o.m) throw new TypeError('Pubkey count cannot be less than m'); } - lazy.prop(o, 'output', () => { - if (!a.m) - return; - if (!o.n) - return; - if (!a.pubkeys) - return; - return bscript.compile([].concat(OP_INT_BASE + a.m, a.pubkeys, OP_INT_BASE + o.n, OPS.OP_CHECKMULTISIG)); - }); - lazy.prop(o, 'm', () => { - if (!o.output) - return; - decode(o.output); - return o.m; - }); - lazy.prop(o, 'n', () => { - if (!o.pubkeys) - return; - return o.pubkeys.length; - }); - lazy.prop(o, 'pubkeys', () => { - if (!a.output) - return; - decode(a.output); - return o.pubkeys; - }); - lazy.prop(o, 'signatures', () => { - if (!a.input) - return; - return bscript.decompile(a.input).slice(1); - }); - lazy.prop(o, 'input', () => { - if (!a.signatures) - return; - return bscript.compile([OPS.OP_0].concat(a.signatures)); - }); - lazy.prop(o, 'witness', () => { - if (!o.input) - return; - return []; - }); - // extended validation - if (opts.validate) { - if (a.output) { - decode(a.output); - if (!typef.Number(chunks[0])) - throw new TypeError('Output is invalid'); - if (!typef.Number(chunks[chunks.length - 2])) - throw new TypeError('Output is invalid'); - if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) - throw new TypeError('Output is invalid'); - if (o.m <= 0 || o.n > 16 || o.m > o.n || o.n !== chunks.length - 3) - throw new TypeError('Output is invalid'); - if (!o.pubkeys.every(x => ecc.isPoint(x))) - throw new TypeError('Output is invalid'); - if (a.m !== undefined && a.m !== o.m) - throw new TypeError('m mismatch'); - if (a.n !== undefined && a.n !== o.n) - throw new TypeError('n mismatch'); - if (a.pubkeys && !stacksEqual(a.pubkeys, o.pubkeys)) - throw new TypeError('Pubkeys mismatch'); - } - if (a.pubkeys) { - if (a.n !== undefined && a.n !== a.pubkeys.length) - throw new TypeError('Pubkey count mismatch'); - o.n = a.pubkeys.length; - if (o.n < o.m) - throw new TypeError('Pubkey count cannot be less than m'); - } - if (a.signatures) { - if (a.signatures.length < o.m) - throw new TypeError('Not enough signatures provided'); - if (a.signatures.length > o.m) - throw new TypeError('Too many signatures provided'); - } - if (a.input) { - if (a.input[0] !== OPS.OP_0) - throw new TypeError('Input is invalid'); - if (o.signatures.length === 0 || - !o.signatures.every(isAcceptableSignature)) - throw new TypeError('Input has invalid signature(s)'); - if (a.signatures && !stacksEqual(a.signatures, o.signatures)) - throw new TypeError('Signature mismatch'); - if (a.m !== undefined && a.m !== a.signatures.length) - throw new TypeError('Signature count mismatch'); - } + if (a.signatures) { + if (a.signatures.length < o.m) + throw new TypeError('Not enough signatures provided'); + if (a.signatures.length > o.m) + throw new TypeError('Too many signatures provided'); } - return Object.assign(o, a); + if (a.input) { + if (a.input[0] !== OPS.OP_0) throw new TypeError('Input is invalid'); + if ( + o.signatures.length === 0 || + !o.signatures.every(isAcceptableSignature) + ) + throw new TypeError('Input has invalid signature(s)'); + if (a.signatures && !stacksEqual(a.signatures, o.signatures)) + throw new TypeError('Signature mismatch'); + if (a.m !== undefined && a.m !== a.signatures.length) + throw new TypeError('Signature count mismatch'); + } + } + return Object.assign(o, a); } exports.p2ms = p2ms; diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index 81fe427..13356d1 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -1,75 +1,72 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const networks_1 = require("../networks"); -const bscript = require("../script"); -const lazy = require("./lazy"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const networks_1 = require('../networks'); +const bscript = require('../script'); +const lazy = require('./lazy'); const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); // input: {signature} // output: {pubKey} OP_CHECKSIG function p2pk(a, opts) { - if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature) - throw new TypeError('Not enough data'); - opts = Object.assign({ validate: true }, opts || {}); - typef({ - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - pubkey: typef.maybe(ecc.isPoint), - signature: typef.maybe(bscript.isCanonicalScriptSignature), - input: typef.maybe(typef.Buffer), - }, a); - const _chunks = lazy.value(() => { - return bscript.decompile(a.input); - }); - const network = a.network || networks_1.bitcoin; - const o = { network }; - lazy.prop(o, 'output', () => { - if (!a.pubkey) - return; - return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); - }); - lazy.prop(o, 'pubkey', () => { - if (!a.output) - return; - return a.output.slice(1, -1); - }); - lazy.prop(o, 'signature', () => { - if (!a.input) - return; - return _chunks()[0]; - }); - lazy.prop(o, 'input', () => { - if (!a.signature) - return; - return bscript.compile([a.signature]); - }); - lazy.prop(o, 'witness', () => { - if (!o.input) - return; - return []; - }); - // extended validation - if (opts.validate) { - if (a.output) { - if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) - throw new TypeError('Output is invalid'); - if (!ecc.isPoint(o.pubkey)) - throw new TypeError('Output pubkey is invalid'); - if (a.pubkey && !a.pubkey.equals(o.pubkey)) - throw new TypeError('Pubkey mismatch'); - } - if (a.signature) { - if (a.input && !a.input.equals(o.input)) - throw new TypeError('Signature mismatch'); - } - if (a.input) { - if (_chunks().length !== 1) - throw new TypeError('Input is invalid'); - if (!bscript.isCanonicalScriptSignature(o.signature)) - throw new TypeError('Input has invalid signature'); - } + if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef( + { + network: typef.maybe(typef.Object), + output: typef.maybe(typef.Buffer), + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + input: typef.maybe(typef.Buffer), + }, + a, + ); + const _chunks = lazy.value(() => { + return bscript.decompile(a.input); + }); + const network = a.network || networks_1.bitcoin; + const o = { network }; + lazy.prop(o, 'output', () => { + if (!a.pubkey) return; + return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); + }); + lazy.prop(o, 'pubkey', () => { + if (!a.output) return; + return a.output.slice(1, -1); + }); + lazy.prop(o, 'signature', () => { + if (!a.input) return; + return _chunks()[0]; + }); + lazy.prop(o, 'input', () => { + if (!a.signature) return; + return bscript.compile([a.signature]); + }); + lazy.prop(o, 'witness', () => { + if (!o.input) return; + return []; + }); + // extended validation + if (opts.validate) { + if (a.output) { + if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) + throw new TypeError('Output is invalid'); + if (!ecc.isPoint(o.pubkey)) + throw new TypeError('Output pubkey is invalid'); + if (a.pubkey && !a.pubkey.equals(o.pubkey)) + throw new TypeError('Pubkey mismatch'); } - return Object.assign(o, a); + if (a.signature) { + if (a.input && !a.input.equals(o.input)) + throw new TypeError('Signature mismatch'); + } + if (a.input) { + if (_chunks().length !== 1) throw new TypeError('Input is invalid'); + if (!bscript.isCanonicalScriptSignature(o.signature)) + throw new TypeError('Input has invalid signature'); + } + } + return Object.assign(o, a); } exports.p2pk = p2pk; diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index 9f06bde..ceb7093 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -1,9 +1,9 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const bcrypto = require("../crypto"); -const networks_1 = require("../networks"); -const bscript = require("../script"); -const lazy = require("./lazy"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const bcrypto = require('../crypto'); +const networks_1 = require('../networks'); +const bscript = require('../script'); +const lazy = require('./lazy'); const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); @@ -11,132 +11,122 @@ const bs58check = require('bs58check'); // input: {signature} {pubkey} // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG function p2pkh(a, opts) { - if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input) - throw new TypeError('Not enough data'); - opts = Object.assign({ validate: true }, opts || {}); - typef({ - network: typef.maybe(typef.Object), - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(20)), - output: typef.maybe(typef.BufferN(25)), - pubkey: typef.maybe(ecc.isPoint), - signature: typef.maybe(bscript.isCanonicalScriptSignature), - input: typef.maybe(typef.Buffer), - }, a); - const _address = lazy.value(() => { - const payload = bs58check.decode(a.address); - const version = payload.readUInt8(0); - const hash = payload.slice(1); - return { version, hash }; - }); - const _chunks = lazy.value(() => { - return bscript.decompile(a.input); - }); - const network = a.network || networks_1.bitcoin; - const o = { network }; - lazy.prop(o, 'address', () => { - if (!o.hash) - return; - const payload = Buffer.allocUnsafe(21); - payload.writeUInt8(network.pubKeyHash, 0); - o.hash.copy(payload, 1); - return bs58check.encode(payload); - }); - lazy.prop(o, 'hash', () => { - if (a.output) - return a.output.slice(3, 23); - if (a.address) - return _address().hash; - if (a.pubkey || o.pubkey) - return bcrypto.hash160(a.pubkey || o.pubkey); - }); - lazy.prop(o, 'output', () => { - if (!o.hash) - return; - return bscript.compile([ - OPS.OP_DUP, - OPS.OP_HASH160, - o.hash, - OPS.OP_EQUALVERIFY, - OPS.OP_CHECKSIG, - ]); - }); - lazy.prop(o, 'pubkey', () => { - if (!a.input) - return; - return _chunks()[1]; - }); - lazy.prop(o, 'signature', () => { - if (!a.input) - return; - return _chunks()[0]; - }); - lazy.prop(o, 'input', () => { - if (!a.pubkey) - return; - if (!a.signature) - return; - return bscript.compile([a.signature, a.pubkey]); - }); - lazy.prop(o, 'witness', () => { - if (!o.input) - return; - return []; - }); - // extended validation - if (opts.validate) { - let hash = Buffer.from([]); - if (a.address) { - if (_address().version !== network.pubKeyHash) - throw new TypeError('Invalid version or Network mismatch'); - if (_address().hash.length !== 20) - throw new TypeError('Invalid address'); - hash = _address().hash; - } - if (a.hash) { - if (hash.length > 0 && !hash.equals(a.hash)) - throw new TypeError('Hash mismatch'); - else - hash = a.hash; - } - if (a.output) { - if (a.output.length !== 25 || - a.output[0] !== OPS.OP_DUP || - a.output[1] !== OPS.OP_HASH160 || - a.output[2] !== 0x14 || - a.output[23] !== OPS.OP_EQUALVERIFY || - a.output[24] !== OPS.OP_CHECKSIG) - throw new TypeError('Output is invalid'); - const hash2 = a.output.slice(3, 23); - if (hash.length > 0 && !hash.equals(hash2)) - throw new TypeError('Hash mismatch'); - else - hash = hash2; - } - if (a.pubkey) { - const pkh = bcrypto.hash160(a.pubkey); - if (hash.length > 0 && !hash.equals(pkh)) - throw new TypeError('Hash mismatch'); - else - hash = pkh; - } - if (a.input) { - const chunks = _chunks(); - if (chunks.length !== 2) - throw new TypeError('Input is invalid'); - if (!bscript.isCanonicalScriptSignature(chunks[0])) - throw new TypeError('Input has invalid signature'); - if (!ecc.isPoint(chunks[1])) - throw new TypeError('Input has invalid pubkey'); - if (a.signature && !a.signature.equals(chunks[0])) - throw new TypeError('Signature mismatch'); - if (a.pubkey && !a.pubkey.equals(chunks[1])) - throw new TypeError('Pubkey mismatch'); - const pkh = bcrypto.hash160(chunks[1]); - if (hash.length > 0 && !hash.equals(pkh)) - throw new TypeError('Hash mismatch'); - } + if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef( + { + network: typef.maybe(typef.Object), + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + output: typef.maybe(typef.BufferN(25)), + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + input: typef.maybe(typef.Buffer), + }, + a, + ); + const _address = lazy.value(() => { + const payload = bs58check.decode(a.address); + const version = payload.readUInt8(0); + const hash = payload.slice(1); + return { version, hash }; + }); + const _chunks = lazy.value(() => { + return bscript.decompile(a.input); + }); + const network = a.network || networks_1.bitcoin; + const o = { network }; + lazy.prop(o, 'address', () => { + if (!o.hash) return; + const payload = Buffer.allocUnsafe(21); + payload.writeUInt8(network.pubKeyHash, 0); + o.hash.copy(payload, 1); + return bs58check.encode(payload); + }); + lazy.prop(o, 'hash', () => { + if (a.output) return a.output.slice(3, 23); + if (a.address) return _address().hash; + if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey); + }); + lazy.prop(o, 'output', () => { + if (!o.hash) return; + return bscript.compile([ + OPS.OP_DUP, + OPS.OP_HASH160, + o.hash, + OPS.OP_EQUALVERIFY, + OPS.OP_CHECKSIG, + ]); + }); + lazy.prop(o, 'pubkey', () => { + if (!a.input) return; + return _chunks()[1]; + }); + lazy.prop(o, 'signature', () => { + if (!a.input) return; + return _chunks()[0]; + }); + lazy.prop(o, 'input', () => { + if (!a.pubkey) return; + if (!a.signature) return; + return bscript.compile([a.signature, a.pubkey]); + }); + lazy.prop(o, 'witness', () => { + if (!o.input) return; + return []; + }); + // extended validation + if (opts.validate) { + let hash = Buffer.from([]); + if (a.address) { + if (_address().version !== network.pubKeyHash) + throw new TypeError('Invalid version or Network mismatch'); + if (_address().hash.length !== 20) throw new TypeError('Invalid address'); + hash = _address().hash; } - return Object.assign(o, a); + if (a.hash) { + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else hash = a.hash; + } + if (a.output) { + if ( + a.output.length !== 25 || + a.output[0] !== OPS.OP_DUP || + a.output[1] !== OPS.OP_HASH160 || + a.output[2] !== 0x14 || + a.output[23] !== OPS.OP_EQUALVERIFY || + a.output[24] !== OPS.OP_CHECKSIG + ) + throw new TypeError('Output is invalid'); + const hash2 = a.output.slice(3, 23); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; + } + if (a.pubkey) { + const pkh = bcrypto.hash160(a.pubkey); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + else hash = pkh; + } + if (a.input) { + const chunks = _chunks(); + if (chunks.length !== 2) throw new TypeError('Input is invalid'); + if (!bscript.isCanonicalScriptSignature(chunks[0])) + throw new TypeError('Input has invalid signature'); + if (!ecc.isPoint(chunks[1])) + throw new TypeError('Input has invalid pubkey'); + if (a.signature && !a.signature.equals(chunks[0])) + throw new TypeError('Signature mismatch'); + if (a.pubkey && !a.pubkey.equals(chunks[1])) + throw new TypeError('Pubkey mismatch'); + const pkh = bcrypto.hash160(chunks[1]); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + } + } + return Object.assign(o, a); } exports.p2pkh = p2pkh; diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index e419deb..5fe660a 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -1,185 +1,178 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const bcrypto = require("../crypto"); -const networks_1 = require("../networks"); -const bscript = require("../script"); -const lazy = require("./lazy"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const bcrypto = require('../crypto'); +const networks_1 = require('../networks'); +const bscript = require('../script'); +const lazy = require('./lazy'); const typef = require('typeforce'); const OPS = bscript.OPS; const bs58check = require('bs58check'); function stacksEqual(a, b) { - if (a.length !== b.length) - return false; - return a.every((x, i) => { - return x.equals(b[i]); - }); + if (a.length !== b.length) return false; + return a.every((x, i) => { + return x.equals(b[i]); + }); } // input: [redeemScriptSig ...] {redeemScript} // witness: <?> // output: OP_HASH160 {hash160(redeemScript)} OP_EQUAL function p2sh(a, opts) { - if (!a.address && !a.hash && !a.output && !a.redeem && !a.input) - throw new TypeError('Not enough data'); - opts = Object.assign({ validate: true }, opts || {}); - typef({ + if (!a.address && !a.hash && !a.output && !a.redeem && !a.input) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef( + { + network: typef.maybe(typef.Object), + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + output: typef.maybe(typef.BufferN(23)), + redeem: typef.maybe({ network: typef.maybe(typef.Object), - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(20)), - output: typef.maybe(typef.BufferN(23)), - redeem: typef.maybe({ - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - input: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)), - }), + output: typef.maybe(typef.Buffer), input: typef.maybe(typef.Buffer), witness: typef.maybe(typef.arrayOf(typef.Buffer)), - }, a); - let network = a.network; - if (!network) { - network = (a.redeem && a.redeem.network) || networks_1.bitcoin; + }), + input: typef.maybe(typef.Buffer), + witness: typef.maybe(typef.arrayOf(typef.Buffer)), + }, + a, + ); + let network = a.network; + if (!network) { + network = (a.redeem && a.redeem.network) || networks_1.bitcoin; + } + const o = { network }; + const _address = lazy.value(() => { + const payload = bs58check.decode(a.address); + const version = payload.readUInt8(0); + const hash = payload.slice(1); + return { version, hash }; + }); + const _chunks = lazy.value(() => { + return bscript.decompile(a.input); + }); + const _redeem = lazy.value(() => { + const chunks = _chunks(); + return { + network, + output: chunks[chunks.length - 1], + input: bscript.compile(chunks.slice(0, -1)), + witness: a.witness || [], + }; + }); + // output dependents + lazy.prop(o, 'address', () => { + if (!o.hash) return; + const payload = Buffer.allocUnsafe(21); + payload.writeUInt8(o.network.scriptHash, 0); + o.hash.copy(payload, 1); + return bs58check.encode(payload); + }); + lazy.prop(o, 'hash', () => { + // in order of least effort + if (a.output) return a.output.slice(2, 22); + if (a.address) return _address().hash; + if (o.redeem && o.redeem.output) return bcrypto.hash160(o.redeem.output); + }); + lazy.prop(o, 'output', () => { + if (!o.hash) return; + return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); + }); + // input dependents + lazy.prop(o, 'redeem', () => { + if (!a.input) return; + return _redeem(); + }); + lazy.prop(o, 'input', () => { + if (!a.redeem || !a.redeem.input || !a.redeem.output) return; + return bscript.compile( + [].concat(bscript.decompile(a.redeem.input), a.redeem.output), + ); + }); + lazy.prop(o, 'witness', () => { + if (o.redeem && o.redeem.witness) return o.redeem.witness; + if (o.input) return []; + }); + if (opts.validate) { + let hash = Buffer.from([]); + if (a.address) { + if (_address().version !== network.scriptHash) + throw new TypeError('Invalid version or Network mismatch'); + if (_address().hash.length !== 20) throw new TypeError('Invalid address'); + hash = _address().hash; } - const o = { network }; - const _address = lazy.value(() => { - const payload = bs58check.decode(a.address); - const version = payload.readUInt8(0); - const hash = payload.slice(1); - return { version, hash }; - }); - const _chunks = lazy.value(() => { - return bscript.decompile(a.input); - }); - const _redeem = lazy.value(() => { - const chunks = _chunks(); - return { - network, - output: chunks[chunks.length - 1], - input: bscript.compile(chunks.slice(0, -1)), - witness: a.witness || [], - }; - }); - // output dependents - lazy.prop(o, 'address', () => { - if (!o.hash) - return; - const payload = Buffer.allocUnsafe(21); - payload.writeUInt8(o.network.scriptHash, 0); - o.hash.copy(payload, 1); - return bs58check.encode(payload); - }); - lazy.prop(o, 'hash', () => { - // in order of least effort - if (a.output) - return a.output.slice(2, 22); - if (a.address) - return _address().hash; - if (o.redeem && o.redeem.output) - return bcrypto.hash160(o.redeem.output); - }); - lazy.prop(o, 'output', () => { - if (!o.hash) - return; - return bscript.compile([OPS.OP_HASH160, o.hash, OPS.OP_EQUAL]); - }); - // input dependents - lazy.prop(o, 'redeem', () => { - if (!a.input) - return; - return _redeem(); - }); - lazy.prop(o, 'input', () => { - if (!a.redeem || !a.redeem.input || !a.redeem.output) - return; - return bscript.compile([].concat(bscript.decompile(a.redeem.input), a.redeem.output)); - }); - lazy.prop(o, 'witness', () => { - if (o.redeem && o.redeem.witness) - return o.redeem.witness; - if (o.input) - return []; - }); - if (opts.validate) { - let hash = Buffer.from([]); - if (a.address) { - if (_address().version !== network.scriptHash) - throw new TypeError('Invalid version or Network mismatch'); - if (_address().hash.length !== 20) - throw new TypeError('Invalid address'); - hash = _address().hash; - } - if (a.hash) { - if (hash.length > 0 && !hash.equals(a.hash)) - throw new TypeError('Hash mismatch'); - else - hash = a.hash; - } - if (a.output) { - if (a.output.length !== 23 || - a.output[0] !== OPS.OP_HASH160 || - a.output[1] !== 0x14 || - a.output[22] !== OPS.OP_EQUAL) - throw new TypeError('Output is invalid'); - const hash2 = a.output.slice(2, 22); - if (hash.length > 0 && !hash.equals(hash2)) - throw new TypeError('Hash mismatch'); - else - hash = hash2; - } - // inlined to prevent 'no-inner-declarations' failing - const checkRedeem = (redeem) => { - // is the redeem output empty/invalid? - if (redeem.output) { - const decompile = bscript.decompile(redeem.output); - if (!decompile || decompile.length < 1) - throw new TypeError('Redeem.output too short'); - // match hash against other sources - const hash2 = bcrypto.hash160(redeem.output); - if (hash.length > 0 && !hash.equals(hash2)) - throw new TypeError('Hash mismatch'); - else - hash = hash2; - } - if (redeem.input) { - const hasInput = redeem.input.length > 0; - const hasWitness = redeem.witness && redeem.witness.length > 0; - if (!hasInput && !hasWitness) - throw new TypeError('Empty input'); - if (hasInput && hasWitness) - throw new TypeError('Input and witness provided'); - if (hasInput) { - const richunks = bscript.decompile(redeem.input); - if (!bscript.isPushOnly(richunks)) - throw new TypeError('Non push-only scriptSig'); - } - } - }; - if (a.input) { - const chunks = _chunks(); - if (!chunks || chunks.length < 1) - throw new TypeError('Input too short'); - if (!Buffer.isBuffer(_redeem().output)) - throw new TypeError('Input is invalid'); - checkRedeem(_redeem()); - } - if (a.redeem) { - if (a.redeem.network && a.redeem.network !== network) - throw new TypeError('Network mismatch'); - if (a.input) { - const redeem = _redeem(); - if (a.redeem.output && !a.redeem.output.equals(redeem.output)) - throw new TypeError('Redeem.output mismatch'); - if (a.redeem.input && !a.redeem.input.equals(redeem.input)) - throw new TypeError('Redeem.input mismatch'); - } - checkRedeem(a.redeem); - } - if (a.witness) { - if (a.redeem && - a.redeem.witness && - !stacksEqual(a.redeem.witness, a.witness)) - throw new TypeError('Witness and redeem.witness mismatch'); - } + if (a.hash) { + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else hash = a.hash; } - return Object.assign(o, a); + if (a.output) { + if ( + a.output.length !== 23 || + a.output[0] !== OPS.OP_HASH160 || + a.output[1] !== 0x14 || + a.output[22] !== OPS.OP_EQUAL + ) + throw new TypeError('Output is invalid'); + const hash2 = a.output.slice(2, 22); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; + } + // inlined to prevent 'no-inner-declarations' failing + const checkRedeem = redeem => { + // is the redeem output empty/invalid? + if (redeem.output) { + const decompile = bscript.decompile(redeem.output); + if (!decompile || decompile.length < 1) + throw new TypeError('Redeem.output too short'); + // match hash against other sources + const hash2 = bcrypto.hash160(redeem.output); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; + } + if (redeem.input) { + const hasInput = redeem.input.length > 0; + const hasWitness = redeem.witness && redeem.witness.length > 0; + if (!hasInput && !hasWitness) throw new TypeError('Empty input'); + if (hasInput && hasWitness) + throw new TypeError('Input and witness provided'); + if (hasInput) { + const richunks = bscript.decompile(redeem.input); + if (!bscript.isPushOnly(richunks)) + throw new TypeError('Non push-only scriptSig'); + } + } + }; + if (a.input) { + const chunks = _chunks(); + if (!chunks || chunks.length < 1) throw new TypeError('Input too short'); + if (!Buffer.isBuffer(_redeem().output)) + throw new TypeError('Input is invalid'); + checkRedeem(_redeem()); + } + if (a.redeem) { + if (a.redeem.network && a.redeem.network !== network) + throw new TypeError('Network mismatch'); + if (a.input) { + const redeem = _redeem(); + if (a.redeem.output && !a.redeem.output.equals(redeem.output)) + throw new TypeError('Redeem.output mismatch'); + if (a.redeem.input && !a.redeem.input.equals(redeem.input)) + throw new TypeError('Redeem.input mismatch'); + } + checkRedeem(a.redeem); + } + if (a.witness) { + if ( + a.redeem && + a.redeem.witness && + !stacksEqual(a.redeem.witness, a.witness) + ) + throw new TypeError('Witness and redeem.witness mismatch'); + } + } + return Object.assign(o, a); } exports.p2sh = p2sh; diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index 9e99610..9571e50 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -1,9 +1,9 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const bcrypto = require("../crypto"); -const networks_1 = require("../networks"); -const bscript = require("../script"); -const lazy = require("./lazy"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const bcrypto = require('../crypto'); +const networks_1 = require('../networks'); +const bscript = require('../script'); +const lazy = require('./lazy'); const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); @@ -13,126 +13,116 @@ const EMPTY_BUFFER = Buffer.alloc(0); // input: <> // output: OP_0 {pubKeyHash} function p2wpkh(a, opts) { - if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness) - throw new TypeError('Not enough data'); - opts = Object.assign({ validate: true }, opts || {}); - typef({ - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(20)), - input: typef.maybe(typef.BufferN(0)), - network: typef.maybe(typef.Object), - output: typef.maybe(typef.BufferN(22)), - pubkey: typef.maybe(ecc.isPoint), - signature: typef.maybe(bscript.isCanonicalScriptSignature), - witness: typef.maybe(typef.arrayOf(typef.Buffer)), - }, a); - const _address = lazy.value(() => { - const result = bech32.decode(a.address); - const version = result.words.shift(); - const data = bech32.fromWords(result.words); - return { - version, - prefix: result.prefix, - data: Buffer.from(data), - }; - }); - const network = a.network || networks_1.bitcoin; - const o = { network }; - lazy.prop(o, 'address', () => { - if (!o.hash) - return; - const words = bech32.toWords(o.hash); - words.unshift(0x00); - return bech32.encode(network.bech32, words); - }); - lazy.prop(o, 'hash', () => { - if (a.output) - return a.output.slice(2, 22); - if (a.address) - return _address().data; - if (a.pubkey || o.pubkey) - return bcrypto.hash160(a.pubkey || o.pubkey); - }); - lazy.prop(o, 'output', () => { - if (!o.hash) - return; - return bscript.compile([OPS.OP_0, o.hash]); - }); - lazy.prop(o, 'pubkey', () => { - if (a.pubkey) - return a.pubkey; - if (!a.witness) - return; - return a.witness[1]; - }); - lazy.prop(o, 'signature', () => { - if (!a.witness) - return; - return a.witness[0]; - }); - lazy.prop(o, 'input', () => { - if (!o.witness) - return; - return EMPTY_BUFFER; - }); - lazy.prop(o, 'witness', () => { - if (!a.pubkey) - return; - if (!a.signature) - return; - return [a.signature, a.pubkey]; - }); - // extended validation - if (opts.validate) { - let hash = Buffer.from([]); - if (a.address) { - if (network && network.bech32 !== _address().prefix) - throw new TypeError('Invalid prefix or Network mismatch'); - if (_address().version !== 0x00) - throw new TypeError('Invalid address version'); - if (_address().data.length !== 20) - throw new TypeError('Invalid address data'); - hash = _address().data; - } - if (a.hash) { - if (hash.length > 0 && !hash.equals(a.hash)) - throw new TypeError('Hash mismatch'); - else - hash = a.hash; - } - if (a.output) { - if (a.output.length !== 22 || - a.output[0] !== OPS.OP_0 || - a.output[1] !== 0x14) - throw new TypeError('Output is invalid'); - if (hash.length > 0 && !hash.equals(a.output.slice(2))) - throw new TypeError('Hash mismatch'); - else - hash = a.output.slice(2); - } - if (a.pubkey) { - const pkh = bcrypto.hash160(a.pubkey); - if (hash.length > 0 && !hash.equals(pkh)) - throw new TypeError('Hash mismatch'); - else - hash = pkh; - } - if (a.witness) { - if (a.witness.length !== 2) - throw new TypeError('Witness is invalid'); - if (!bscript.isCanonicalScriptSignature(a.witness[0])) - throw new TypeError('Witness has invalid signature'); - if (!ecc.isPoint(a.witness[1])) - throw new TypeError('Witness has invalid pubkey'); - if (a.signature && !a.signature.equals(a.witness[0])) - throw new TypeError('Signature mismatch'); - if (a.pubkey && !a.pubkey.equals(a.witness[1])) - throw new TypeError('Pubkey mismatch'); - const pkh = bcrypto.hash160(a.witness[1]); - if (hash.length > 0 && !hash.equals(pkh)) - throw new TypeError('Hash mismatch'); - } + if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef( + { + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(20)), + input: typef.maybe(typef.BufferN(0)), + network: typef.maybe(typef.Object), + output: typef.maybe(typef.BufferN(22)), + pubkey: typef.maybe(ecc.isPoint), + signature: typef.maybe(bscript.isCanonicalScriptSignature), + witness: typef.maybe(typef.arrayOf(typef.Buffer)), + }, + a, + ); + const _address = lazy.value(() => { + const result = bech32.decode(a.address); + const version = result.words.shift(); + const data = bech32.fromWords(result.words); + return { + version, + prefix: result.prefix, + data: Buffer.from(data), + }; + }); + const network = a.network || networks_1.bitcoin; + const o = { network }; + lazy.prop(o, 'address', () => { + if (!o.hash) return; + const words = bech32.toWords(o.hash); + words.unshift(0x00); + return bech32.encode(network.bech32, words); + }); + lazy.prop(o, 'hash', () => { + if (a.output) return a.output.slice(2, 22); + if (a.address) return _address().data; + if (a.pubkey || o.pubkey) return bcrypto.hash160(a.pubkey || o.pubkey); + }); + lazy.prop(o, 'output', () => { + if (!o.hash) return; + return bscript.compile([OPS.OP_0, o.hash]); + }); + lazy.prop(o, 'pubkey', () => { + if (a.pubkey) return a.pubkey; + if (!a.witness) return; + return a.witness[1]; + }); + lazy.prop(o, 'signature', () => { + if (!a.witness) return; + return a.witness[0]; + }); + lazy.prop(o, 'input', () => { + if (!o.witness) return; + return EMPTY_BUFFER; + }); + lazy.prop(o, 'witness', () => { + if (!a.pubkey) return; + if (!a.signature) return; + return [a.signature, a.pubkey]; + }); + // extended validation + if (opts.validate) { + let hash = Buffer.from([]); + if (a.address) { + if (network && network.bech32 !== _address().prefix) + throw new TypeError('Invalid prefix or Network mismatch'); + if (_address().version !== 0x00) + throw new TypeError('Invalid address version'); + if (_address().data.length !== 20) + throw new TypeError('Invalid address data'); + hash = _address().data; } - return Object.assign(o, a); + if (a.hash) { + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else hash = a.hash; + } + if (a.output) { + if ( + a.output.length !== 22 || + a.output[0] !== OPS.OP_0 || + a.output[1] !== 0x14 + ) + throw new TypeError('Output is invalid'); + if (hash.length > 0 && !hash.equals(a.output.slice(2))) + throw new TypeError('Hash mismatch'); + else hash = a.output.slice(2); + } + if (a.pubkey) { + const pkh = bcrypto.hash160(a.pubkey); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + else hash = pkh; + } + if (a.witness) { + if (a.witness.length !== 2) throw new TypeError('Witness is invalid'); + if (!bscript.isCanonicalScriptSignature(a.witness[0])) + throw new TypeError('Witness has invalid signature'); + if (!ecc.isPoint(a.witness[1])) + throw new TypeError('Witness has invalid pubkey'); + if (a.signature && !a.signature.equals(a.witness[0])) + throw new TypeError('Signature mismatch'); + if (a.pubkey && !a.pubkey.equals(a.witness[1])) + throw new TypeError('Pubkey mismatch'); + const pkh = bcrypto.hash160(a.witness[1]); + if (hash.length > 0 && !hash.equals(pkh)) + throw new TypeError('Hash mismatch'); + } + } + return Object.assign(o, a); } exports.p2wpkh = p2wpkh; diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index 0def430..9363718 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -1,177 +1,176 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const bcrypto = require("../crypto"); -const networks_1 = require("../networks"); -const bscript = require("../script"); -const lazy = require("./lazy"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const bcrypto = require('../crypto'); +const networks_1 = require('../networks'); +const bscript = require('../script'); +const lazy = require('./lazy'); const typef = require('typeforce'); const OPS = bscript.OPS; const bech32 = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); function stacksEqual(a, b) { - if (a.length !== b.length) - return false; - return a.every((x, i) => { - return x.equals(b[i]); - }); + if (a.length !== b.length) return false; + return a.every((x, i) => { + return x.equals(b[i]); + }); } // input: <> // witness: [redeemScriptSig ...] {redeemScript} // output: OP_0 {sha256(redeemScript)} function p2wsh(a, opts) { - if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness) - throw new TypeError('Not enough data'); - opts = Object.assign({ validate: true }, opts || {}); - typef({ + if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness) + throw new TypeError('Not enough data'); + opts = Object.assign({ validate: true }, opts || {}); + typef( + { + network: typef.maybe(typef.Object), + address: typef.maybe(typef.String), + hash: typef.maybe(typef.BufferN(32)), + output: typef.maybe(typef.BufferN(34)), + redeem: typef.maybe({ + input: typef.maybe(typef.Buffer), network: typef.maybe(typef.Object), - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(32)), - output: typef.maybe(typef.BufferN(34)), - redeem: typef.maybe({ - input: typef.maybe(typef.Buffer), - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)), - }), - input: typef.maybe(typef.BufferN(0)), + output: typef.maybe(typef.Buffer), witness: typef.maybe(typef.arrayOf(typef.Buffer)), - }, a); - const _address = lazy.value(() => { - const result = bech32.decode(a.address); - const version = result.words.shift(); - const data = bech32.fromWords(result.words); - return { - version, - prefix: result.prefix, - data: Buffer.from(data), - }; - }); - const _rchunks = lazy.value(() => { - return bscript.decompile(a.redeem.input); - }); - let network = a.network; - if (!network) { - network = (a.redeem && a.redeem.network) || networks_1.bitcoin; + }), + input: typef.maybe(typef.BufferN(0)), + witness: typef.maybe(typef.arrayOf(typef.Buffer)), + }, + a, + ); + const _address = lazy.value(() => { + const result = bech32.decode(a.address); + const version = result.words.shift(); + const data = bech32.fromWords(result.words); + return { + version, + prefix: result.prefix, + data: Buffer.from(data), + }; + }); + const _rchunks = lazy.value(() => { + return bscript.decompile(a.redeem.input); + }); + let network = a.network; + if (!network) { + network = (a.redeem && a.redeem.network) || networks_1.bitcoin; + } + const o = { network }; + lazy.prop(o, 'address', () => { + if (!o.hash) return; + const words = bech32.toWords(o.hash); + words.unshift(0x00); + return bech32.encode(network.bech32, words); + }); + lazy.prop(o, 'hash', () => { + if (a.output) return a.output.slice(2); + if (a.address) return _address().data; + if (o.redeem && o.redeem.output) return bcrypto.sha256(o.redeem.output); + }); + lazy.prop(o, 'output', () => { + if (!o.hash) return; + return bscript.compile([OPS.OP_0, o.hash]); + }); + lazy.prop(o, 'redeem', () => { + if (!a.witness) return; + return { + output: a.witness[a.witness.length - 1], + input: EMPTY_BUFFER, + witness: a.witness.slice(0, -1), + }; + }); + lazy.prop(o, 'input', () => { + if (!o.witness) return; + return EMPTY_BUFFER; + }); + lazy.prop(o, 'witness', () => { + // transform redeem input to witness stack? + if ( + a.redeem && + a.redeem.input && + a.redeem.input.length > 0 && + a.redeem.output && + a.redeem.output.length > 0 + ) { + const stack = bscript.toStack(_rchunks()); + // assign, and blank the existing input + o.redeem = Object.assign({ witness: stack }, a.redeem); + o.redeem.input = EMPTY_BUFFER; + return [].concat(stack, a.redeem.output); } - const o = { network }; - lazy.prop(o, 'address', () => { - if (!o.hash) - return; - const words = bech32.toWords(o.hash); - words.unshift(0x00); - return bech32.encode(network.bech32, words); - }); - lazy.prop(o, 'hash', () => { - if (a.output) - return a.output.slice(2); - if (a.address) - return _address().data; - if (o.redeem && o.redeem.output) - return bcrypto.sha256(o.redeem.output); - }); - lazy.prop(o, 'output', () => { - if (!o.hash) - return; - return bscript.compile([OPS.OP_0, o.hash]); - }); - lazy.prop(o, 'redeem', () => { - if (!a.witness) - return; - return { - output: a.witness[a.witness.length - 1], - input: EMPTY_BUFFER, - witness: a.witness.slice(0, -1), - }; - }); - lazy.prop(o, 'input', () => { - if (!o.witness) - return; - return EMPTY_BUFFER; - }); - lazy.prop(o, 'witness', () => { - // transform redeem input to witness stack? - if (a.redeem && - a.redeem.input && - a.redeem.input.length > 0 && - a.redeem.output && - a.redeem.output.length > 0) { - const stack = bscript.toStack(_rchunks()); - // assign, and blank the existing input - o.redeem = Object.assign({ witness: stack }, a.redeem); - o.redeem.input = EMPTY_BUFFER; - return [].concat(stack, a.redeem.output); - } - if (!a.redeem) - return; - if (!a.redeem.output) - return; - if (!a.redeem.witness) - return; - return [].concat(a.redeem.witness, a.redeem.output); - }); - // extended validation - if (opts.validate) { - let hash = Buffer.from([]); - if (a.address) { - if (_address().prefix !== network.bech32) - throw new TypeError('Invalid prefix or Network mismatch'); - if (_address().version !== 0x00) - throw new TypeError('Invalid address version'); - if (_address().data.length !== 32) - throw new TypeError('Invalid address data'); - hash = _address().data; - } - if (a.hash) { - if (hash.length > 0 && !hash.equals(a.hash)) - throw new TypeError('Hash mismatch'); - else - hash = a.hash; - } - if (a.output) { - if (a.output.length !== 34 || - a.output[0] !== OPS.OP_0 || - a.output[1] !== 0x20) - throw new TypeError('Output is invalid'); - const hash2 = a.output.slice(2); - if (hash.length > 0 && !hash.equals(hash2)) - throw new TypeError('Hash mismatch'); - else - hash = hash2; - } - if (a.redeem) { - if (a.redeem.network && a.redeem.network !== network) - throw new TypeError('Network mismatch'); - // is there two redeem sources? - if (a.redeem.input && - a.redeem.input.length > 0 && - a.redeem.witness && - a.redeem.witness.length > 0) - throw new TypeError('Ambiguous witness source'); - // is the redeem output non-empty? - if (a.redeem.output) { - if (bscript.decompile(a.redeem.output).length === 0) - throw new TypeError('Redeem.output is invalid'); - // match hash against other sources - const hash2 = bcrypto.sha256(a.redeem.output); - if (hash.length > 0 && !hash.equals(hash2)) - throw new TypeError('Hash mismatch'); - else - hash = hash2; - } - if (a.redeem.input && !bscript.isPushOnly(_rchunks())) - throw new TypeError('Non push-only scriptSig'); - if (a.witness && - a.redeem.witness && - !stacksEqual(a.witness, a.redeem.witness)) - throw new TypeError('Witness and redeem.witness mismatch'); - } - if (a.witness) { - if (a.redeem && - a.redeem.output && - !a.redeem.output.equals(a.witness[a.witness.length - 1])) - throw new TypeError('Witness and redeem.output mismatch'); - } + if (!a.redeem) return; + if (!a.redeem.output) return; + if (!a.redeem.witness) return; + return [].concat(a.redeem.witness, a.redeem.output); + }); + // extended validation + if (opts.validate) { + let hash = Buffer.from([]); + if (a.address) { + if (_address().prefix !== network.bech32) + throw new TypeError('Invalid prefix or Network mismatch'); + if (_address().version !== 0x00) + throw new TypeError('Invalid address version'); + if (_address().data.length !== 32) + throw new TypeError('Invalid address data'); + hash = _address().data; } - return Object.assign(o, a); + if (a.hash) { + if (hash.length > 0 && !hash.equals(a.hash)) + throw new TypeError('Hash mismatch'); + else hash = a.hash; + } + if (a.output) { + if ( + a.output.length !== 34 || + a.output[0] !== OPS.OP_0 || + a.output[1] !== 0x20 + ) + throw new TypeError('Output is invalid'); + const hash2 = a.output.slice(2); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; + } + if (a.redeem) { + if (a.redeem.network && a.redeem.network !== network) + throw new TypeError('Network mismatch'); + // is there two redeem sources? + if ( + a.redeem.input && + a.redeem.input.length > 0 && + a.redeem.witness && + a.redeem.witness.length > 0 + ) + throw new TypeError('Ambiguous witness source'); + // is the redeem output non-empty? + if (a.redeem.output) { + if (bscript.decompile(a.redeem.output).length === 0) + throw new TypeError('Redeem.output is invalid'); + // match hash against other sources + const hash2 = bcrypto.sha256(a.redeem.output); + if (hash.length > 0 && !hash.equals(hash2)) + throw new TypeError('Hash mismatch'); + else hash = hash2; + } + if (a.redeem.input && !bscript.isPushOnly(_rchunks())) + throw new TypeError('Non push-only scriptSig'); + if ( + a.witness && + a.redeem.witness && + !stacksEqual(a.witness, a.redeem.witness) + ) + throw new TypeError('Witness and redeem.witness mismatch'); + } + if (a.witness) { + if ( + a.redeem && + a.redeem.output && + !a.redeem.output.equals(a.witness[a.witness.length - 1]) + ) + throw new TypeError('Witness and redeem.output mismatch'); + } + } + return Object.assign(o, a); } exports.p2wsh = p2wsh; diff --git a/src/script.js b/src/script.js index ac334f8..39859dc 100644 --- a/src/script.js +++ b/src/script.js @@ -1,8 +1,8 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const scriptNumber = require("./script_number"); -const scriptSignature = require("./script_signature"); -const types = require("./types"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const scriptNumber = require('./script_number'); +const scriptSignature = require('./script_signature'); +const types = require('./types'); const bip66 = require('bip66'); const ecc = require('tiny-secp256k1'); const pushdata = require('pushdata-bitcoin'); @@ -11,179 +11,165 @@ exports.OPS = require('bitcoin-ops'); const REVERSE_OPS = require('bitcoin-ops/map'); const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1 function isOPInt(value) { - return (types.Number(value) && - (value === exports.OPS.OP_0 || - (value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) || - value === exports.OPS.OP_1NEGATE)); + return ( + types.Number(value) && + (value === exports.OPS.OP_0 || + (value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) || + value === exports.OPS.OP_1NEGATE) + ); } function isPushOnlyChunk(value) { - return types.Buffer(value) || isOPInt(value); + return types.Buffer(value) || isOPInt(value); } function isPushOnly(value) { - return types.Array(value) && value.every(isPushOnlyChunk); + return types.Array(value) && value.every(isPushOnlyChunk); } exports.isPushOnly = isPushOnly; function asMinimalOP(buffer) { - if (buffer.length === 0) - return exports.OPS.OP_0; - if (buffer.length !== 1) - return; - if (buffer[0] >= 1 && buffer[0] <= 16) - return OP_INT_BASE + buffer[0]; - if (buffer[0] === 0x81) - return exports.OPS.OP_1NEGATE; + if (buffer.length === 0) return exports.OPS.OP_0; + if (buffer.length !== 1) return; + if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0]; + if (buffer[0] === 0x81) return exports.OPS.OP_1NEGATE; } function chunksIsBuffer(buf) { - return Buffer.isBuffer(buf); + return Buffer.isBuffer(buf); } function chunksIsArray(buf) { - return types.Array(buf); + return types.Array(buf); } function singleChunkIsBuffer(buf) { - return Buffer.isBuffer(buf); + return Buffer.isBuffer(buf); } function compile(chunks) { - // TODO: remove me - if (chunksIsBuffer(chunks)) - return chunks; - typeforce(types.Array, chunks); - const bufferSize = chunks.reduce((accum, chunk) => { - // data chunk - if (singleChunkIsBuffer(chunk)) { - // adhere to BIP62.3, minimal push policy - if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { - return accum + 1; - } - return accum + pushdata.encodingLength(chunk.length) + chunk.length; - } - // opcode + // TODO: remove me + if (chunksIsBuffer(chunks)) return chunks; + typeforce(types.Array, chunks); + const bufferSize = chunks.reduce((accum, chunk) => { + // data chunk + if (singleChunkIsBuffer(chunk)) { + // adhere to BIP62.3, minimal push policy + if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) { return accum + 1; - }, 0.0); - const buffer = Buffer.allocUnsafe(bufferSize); - let offset = 0; - chunks.forEach(chunk => { - // data chunk - if (singleChunkIsBuffer(chunk)) { - // adhere to BIP62.3, minimal push policy - const opcode = asMinimalOP(chunk); - if (opcode !== undefined) { - buffer.writeUInt8(opcode, offset); - offset += 1; - return; - } - offset += pushdata.encode(buffer, chunk.length, offset); - chunk.copy(buffer, offset); - offset += chunk.length; - // opcode - } - else { - buffer.writeUInt8(chunk, offset); - offset += 1; - } - }); - if (offset !== buffer.length) - throw new Error('Could not decode chunks'); - return buffer; + } + return accum + pushdata.encodingLength(chunk.length) + chunk.length; + } + // opcode + return accum + 1; + }, 0.0); + const buffer = Buffer.allocUnsafe(bufferSize); + let offset = 0; + chunks.forEach(chunk => { + // data chunk + if (singleChunkIsBuffer(chunk)) { + // adhere to BIP62.3, minimal push policy + const opcode = asMinimalOP(chunk); + if (opcode !== undefined) { + buffer.writeUInt8(opcode, offset); + offset += 1; + return; + } + offset += pushdata.encode(buffer, chunk.length, offset); + chunk.copy(buffer, offset); + offset += chunk.length; + // opcode + } else { + buffer.writeUInt8(chunk, offset); + offset += 1; + } + }); + if (offset !== buffer.length) throw new Error('Could not decode chunks'); + return buffer; } exports.compile = compile; function decompile(buffer) { - // TODO: remove me - if (chunksIsArray(buffer)) - return buffer; - typeforce(types.Buffer, buffer); - const chunks = []; - let i = 0; - while (i < buffer.length) { - const opcode = buffer[i]; - // data chunk - if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) { - const d = pushdata.decode(buffer, i); - // did reading a pushDataInt fail? - if (d === null) - return null; - i += d.size; - // attempt to read too much data? - if (i + d.number > buffer.length) - return null; - const data = buffer.slice(i, i + d.number); - i += d.number; - // decompile minimally - const op = asMinimalOP(data); - if (op !== undefined) { - chunks.push(op); - } - else { - chunks.push(data); - } - // opcode - } - else { - chunks.push(opcode); - i += 1; - } + // TODO: remove me + if (chunksIsArray(buffer)) return buffer; + typeforce(types.Buffer, buffer); + const chunks = []; + let i = 0; + while (i < buffer.length) { + const opcode = buffer[i]; + // data chunk + if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) { + const d = pushdata.decode(buffer, i); + // did reading a pushDataInt fail? + if (d === null) return null; + i += d.size; + // attempt to read too much data? + if (i + d.number > buffer.length) return null; + const data = buffer.slice(i, i + d.number); + i += d.number; + // decompile minimally + const op = asMinimalOP(data); + if (op !== undefined) { + chunks.push(op); + } else { + chunks.push(data); + } + // opcode + } else { + chunks.push(opcode); + i += 1; } - return chunks; + } + return chunks; } exports.decompile = decompile; function toASM(chunks) { - if (chunksIsBuffer(chunks)) { - chunks = decompile(chunks); - } - return chunks - .map(chunk => { - // data? - if (singleChunkIsBuffer(chunk)) { - const op = asMinimalOP(chunk); - if (op === undefined) - return chunk.toString('hex'); - chunk = op; - } - // opcode! - return REVERSE_OPS[chunk]; + if (chunksIsBuffer(chunks)) { + chunks = decompile(chunks); + } + return chunks + .map(chunk => { + // data? + if (singleChunkIsBuffer(chunk)) { + const op = asMinimalOP(chunk); + if (op === undefined) return chunk.toString('hex'); + chunk = op; + } + // opcode! + return REVERSE_OPS[chunk]; }) - .join(' '); + .join(' '); } exports.toASM = toASM; function fromASM(asm) { - typeforce(types.String, asm); - return compile(asm.split(' ').map(chunkStr => { - // opcode? - if (exports.OPS[chunkStr] !== undefined) - return exports.OPS[chunkStr]; - typeforce(types.Hex, chunkStr); - // data! - return Buffer.from(chunkStr, 'hex'); - })); + typeforce(types.String, asm); + return compile( + asm.split(' ').map(chunkStr => { + // opcode? + if (exports.OPS[chunkStr] !== undefined) return exports.OPS[chunkStr]; + typeforce(types.Hex, chunkStr); + // data! + return Buffer.from(chunkStr, 'hex'); + }), + ); } exports.fromASM = fromASM; function toStack(chunks) { - chunks = decompile(chunks); - typeforce(isPushOnly, chunks); - return chunks.map(op => { - if (singleChunkIsBuffer(op)) - return op; - if (op === exports.OPS.OP_0) - return Buffer.allocUnsafe(0); - return scriptNumber.encode(op - OP_INT_BASE); - }); + chunks = decompile(chunks); + typeforce(isPushOnly, chunks); + return chunks.map(op => { + if (singleChunkIsBuffer(op)) return op; + if (op === exports.OPS.OP_0) return Buffer.allocUnsafe(0); + return scriptNumber.encode(op - OP_INT_BASE); + }); } exports.toStack = toStack; function isCanonicalPubKey(buffer) { - return ecc.isPoint(buffer); + return ecc.isPoint(buffer); } exports.isCanonicalPubKey = isCanonicalPubKey; function isDefinedHashType(hashType) { - const hashTypeMod = hashType & ~0x80; - // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE - return hashTypeMod > 0x00 && hashTypeMod < 0x04; + const hashTypeMod = hashType & ~0x80; + // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE + return hashTypeMod > 0x00 && hashTypeMod < 0x04; } exports.isDefinedHashType = isDefinedHashType; function isCanonicalScriptSignature(buffer) { - if (!Buffer.isBuffer(buffer)) - return false; - if (!isDefinedHashType(buffer[buffer.length - 1])) - return false; - return bip66.check(buffer.slice(0, -1)); + if (!Buffer.isBuffer(buffer)) return false; + if (!isDefinedHashType(buffer[buffer.length - 1])) return false; + return bip66.check(buffer.slice(0, -1)); } exports.isCanonicalScriptSignature = isCanonicalScriptSignature; // tslint:disable-next-line variable-name diff --git a/src/script_number.js b/src/script_number.js index 3a30a43..3f313af 100644 --- a/src/script_number.js +++ b/src/script_number.js @@ -1,65 +1,61 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); function decode(buffer, maxLength, minimal) { - maxLength = maxLength || 4; - minimal = minimal === undefined ? true : minimal; - const length = buffer.length; - if (length === 0) - return 0; - if (length > maxLength) - throw new TypeError('Script number overflow'); - if (minimal) { - if ((buffer[length - 1] & 0x7f) === 0) { - if (length <= 1 || (buffer[length - 2] & 0x80) === 0) - throw new Error('Non-minimally encoded script number'); - } + maxLength = maxLength || 4; + minimal = minimal === undefined ? true : minimal; + const length = buffer.length; + if (length === 0) return 0; + if (length > maxLength) throw new TypeError('Script number overflow'); + if (minimal) { + if ((buffer[length - 1] & 0x7f) === 0) { + if (length <= 1 || (buffer[length - 2] & 0x80) === 0) + throw new Error('Non-minimally encoded script number'); } - // 40-bit - if (length === 5) { - const a = buffer.readUInt32LE(0); - const b = buffer.readUInt8(4); - if (b & 0x80) - return -((b & ~0x80) * 0x100000000 + a); - return b * 0x100000000 + a; - } - // 32-bit / 24-bit / 16-bit / 8-bit - let result = 0; - for (let i = 0; i < length; ++i) { - result |= buffer[i] << (8 * i); - } - if (buffer[length - 1] & 0x80) - return -(result & ~(0x80 << (8 * (length - 1)))); - return result; + } + // 40-bit + if (length === 5) { + const a = buffer.readUInt32LE(0); + const b = buffer.readUInt8(4); + if (b & 0x80) return -((b & ~0x80) * 0x100000000 + a); + return b * 0x100000000 + a; + } + // 32-bit / 24-bit / 16-bit / 8-bit + let result = 0; + for (let i = 0; i < length; ++i) { + result |= buffer[i] << (8 * i); + } + if (buffer[length - 1] & 0x80) + return -(result & ~(0x80 << (8 * (length - 1)))); + return result; } exports.decode = decode; function scriptNumSize(i) { - return i > 0x7fffffff - ? 5 - : i > 0x7fffff - ? 4 - : i > 0x7fff - ? 3 - : i > 0x7f - ? 2 - : i > 0x00 - ? 1 - : 0; + return i > 0x7fffffff + ? 5 + : i > 0x7fffff + ? 4 + : i > 0x7fff + ? 3 + : i > 0x7f + ? 2 + : i > 0x00 + ? 1 + : 0; } function encode(_number) { - let value = Math.abs(_number); - const size = scriptNumSize(value); - const buffer = Buffer.allocUnsafe(size); - const negative = _number < 0; - for (let i = 0; i < size; ++i) { - buffer.writeUInt8(value & 0xff, i); - value >>= 8; - } - if (buffer[size - 1] & 0x80) { - buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1); - } - else if (negative) { - buffer[size - 1] |= 0x80; - } - return buffer; + let value = Math.abs(_number); + const size = scriptNumSize(value); + const buffer = Buffer.allocUnsafe(size); + const negative = _number < 0; + for (let i = 0; i < size; ++i) { + buffer.writeUInt8(value & 0xff, i); + value >>= 8; + } + if (buffer[size - 1] & 0x80) { + buffer.writeUInt8(negative ? 0x80 : 0x00, size - 1); + } else if (negative) { + buffer[size - 1] |= 0x80; + } + return buffer; } exports.encode = encode; diff --git a/src/script_signature.js b/src/script_signature.js index a1f2f22..fb52fe9 100644 --- a/src/script_signature.js +++ b/src/script_signature.js @@ -1,53 +1,52 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const types = require("./types"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const types = require('./types'); const bip66 = require('bip66'); const typeforce = require('typeforce'); const ZERO = Buffer.alloc(1, 0); function toDER(x) { - let i = 0; - while (x[i] === 0) - ++i; - if (i === x.length) - return ZERO; - x = x.slice(i); - if (x[0] & 0x80) - return Buffer.concat([ZERO, x], 1 + x.length); - return x; + let i = 0; + while (x[i] === 0) ++i; + if (i === x.length) return ZERO; + x = x.slice(i); + if (x[0] & 0x80) return Buffer.concat([ZERO, x], 1 + x.length); + return x; } function fromDER(x) { - if (x[0] === 0x00) - x = x.slice(1); - const buffer = Buffer.alloc(32, 0); - const bstart = Math.max(0, 32 - x.length); - x.copy(buffer, bstart); - return buffer; + if (x[0] === 0x00) x = x.slice(1); + const buffer = Buffer.alloc(32, 0); + const bstart = Math.max(0, 32 - x.length); + x.copy(buffer, bstart); + return buffer; } // BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed) function decode(buffer) { - const hashType = buffer.readUInt8(buffer.length - 1); - const hashTypeMod = hashType & ~0x80; - if (hashTypeMod <= 0 || hashTypeMod >= 4) - throw new Error('Invalid hashType ' + hashType); - const decoded = bip66.decode(buffer.slice(0, -1)); - const r = fromDER(decoded.r); - const s = fromDER(decoded.s); - const signature = Buffer.concat([r, s], 64); - return { signature, hashType }; + const hashType = buffer.readUInt8(buffer.length - 1); + const hashTypeMod = hashType & ~0x80; + if (hashTypeMod <= 0 || hashTypeMod >= 4) + throw new Error('Invalid hashType ' + hashType); + const decoded = bip66.decode(buffer.slice(0, -1)); + const r = fromDER(decoded.r); + const s = fromDER(decoded.s); + const signature = Buffer.concat([r, s], 64); + return { signature, hashType }; } exports.decode = decode; function encode(signature, hashType) { - typeforce({ - signature: types.BufferN(64), - hashType: types.UInt8, - }, { signature, hashType }); - const hashTypeMod = hashType & ~0x80; - if (hashTypeMod <= 0 || hashTypeMod >= 4) - throw new Error('Invalid hashType ' + hashType); - const hashTypeBuffer = Buffer.allocUnsafe(1); - hashTypeBuffer.writeUInt8(hashType, 0); - const r = toDER(signature.slice(0, 32)); - const s = toDER(signature.slice(32, 64)); - return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]); + typeforce( + { + signature: types.BufferN(64), + hashType: types.UInt8, + }, + { signature, hashType }, + ); + const hashTypeMod = hashType & ~0x80; + if (hashTypeMod <= 0 || hashTypeMod >= 4) + throw new Error('Invalid hashType ' + hashType); + const hashTypeBuffer = Buffer.allocUnsafe(1); + hashTypeBuffer.writeUInt8(hashType, 0); + const r = toDER(signature.slice(0, 32)); + const s = toDER(signature.slice(32, 64)); + return Buffer.concat([bip66.encode(r, s), hashTypeBuffer]); } exports.encode = encode; diff --git a/src/templates/multisig/index.js b/src/templates/multisig/index.js index 85a15b9..b8cd6c4 100644 --- a/src/templates/multisig/index.js +++ b/src/templates/multisig/index.js @@ -1,6 +1,6 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const input = require("./input"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const input = require('./input'); exports.input = input; -const output = require("./output"); +const output = require('./output'); exports.output = output; diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js index 4b4f395..403c2f7 100644 --- a/src/templates/multisig/input.js +++ b/src/templates/multisig/input.js @@ -1,23 +1,23 @@ -"use strict"; +'use strict'; // OP_0 [signatures ...] -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); -const script_1 = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); +const script_1 = require('../../script'); function partialSignature(value) { - return (value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value)); + return ( + value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value) + ); } function check(script, allowIncomplete) { - const chunks = bscript.decompile(script); - if (chunks.length < 2) - return false; - if (chunks[0] !== script_1.OPS.OP_0) - return false; - if (allowIncomplete) { - return chunks.slice(1).every(partialSignature); - } - return chunks.slice(1).every(bscript.isCanonicalScriptSignature); + const chunks = bscript.decompile(script); + if (chunks.length < 2) return false; + if (chunks[0] !== script_1.OPS.OP_0) return false; + if (allowIncomplete) { + return chunks.slice(1).every(partialSignature); + } + return chunks.slice(1).every(bscript.isCanonicalScriptSignature); } exports.check = check; check.toJSON = () => { - return 'multisig input'; + return 'multisig input'; }; diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.js index c79fe9b..0896605 100644 --- a/src/templates/multisig/output.js +++ b/src/templates/multisig/output.js @@ -1,36 +1,27 @@ -"use strict"; +'use strict'; // m [pubKeys ...] n OP_CHECKMULTISIG -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); -const script_1 = require("../../script"); -const types = require("../../types"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); +const script_1 = require('../../script'); +const types = require('../../types'); const OP_INT_BASE = script_1.OPS.OP_RESERVED; // OP_1 - 1 function check(script, allowIncomplete) { - const chunks = bscript.decompile(script); - if (chunks.length < 4) - return false; - if (chunks[chunks.length - 1] !== script_1.OPS.OP_CHECKMULTISIG) - return false; - if (!types.Number(chunks[0])) - return false; - if (!types.Number(chunks[chunks.length - 2])) - return false; - const m = chunks[0] - OP_INT_BASE; - const n = chunks[chunks.length - 2] - OP_INT_BASE; - if (m <= 0) - return false; - if (n > 16) - return false; - if (m > n) - return false; - if (n !== chunks.length - 3) - return false; - if (allowIncomplete) - return true; - const keys = chunks.slice(1, -2); - return keys.every(bscript.isCanonicalPubKey); + const chunks = bscript.decompile(script); + if (chunks.length < 4) return false; + if (chunks[chunks.length - 1] !== script_1.OPS.OP_CHECKMULTISIG) return false; + if (!types.Number(chunks[0])) return false; + if (!types.Number(chunks[chunks.length - 2])) return false; + const m = chunks[0] - OP_INT_BASE; + const n = chunks[chunks.length - 2] - OP_INT_BASE; + if (m <= 0) return false; + if (n > 16) return false; + if (m > n) return false; + if (n !== chunks.length - 3) return false; + if (allowIncomplete) return true; + const keys = chunks.slice(1, -2); + return keys.every(bscript.isCanonicalPubKey); } exports.check = check; check.toJSON = () => { - return 'multi-sig output'; + return 'multi-sig output'; }; diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js index 29bee7a..50355d3 100644 --- a/src/templates/nulldata.js +++ b/src/templates/nulldata.js @@ -1,15 +1,15 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); // OP_RETURN {data} -const bscript = require("../script"); +const bscript = require('../script'); const OPS = bscript.OPS; function check(script) { - const buffer = bscript.compile(script); - return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; + const buffer = bscript.compile(script); + return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; } exports.check = check; check.toJSON = () => { - return 'null data output'; + return 'null data output'; }; const output = { check }; exports.output = output; diff --git a/src/templates/pubkey/index.js b/src/templates/pubkey/index.js index 85a15b9..b8cd6c4 100644 --- a/src/templates/pubkey/index.js +++ b/src/templates/pubkey/index.js @@ -1,6 +1,6 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const input = require("./input"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const input = require('./input'); exports.input = input; -const output = require("./output"); +const output = require('./output'); exports.output = output; diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js index 479fdc5..9715b80 100644 --- a/src/templates/pubkey/input.js +++ b/src/templates/pubkey/input.js @@ -1,13 +1,12 @@ -"use strict"; +'use strict'; // {signature} -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); function check(script) { - const chunks = bscript.decompile(script); - return (chunks.length === 1 && - bscript.isCanonicalScriptSignature(chunks[0])); + const chunks = bscript.decompile(script); + return chunks.length === 1 && bscript.isCanonicalScriptSignature(chunks[0]); } exports.check = check; check.toJSON = () => { - return 'pubKey input'; + return 'pubKey input'; }; diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.js index 1f17990..2edb731 100644 --- a/src/templates/pubkey/output.js +++ b/src/templates/pubkey/output.js @@ -1,15 +1,17 @@ -"use strict"; +'use strict'; // {pubKey} OP_CHECKSIG -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); -const script_1 = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); +const script_1 = require('../../script'); function check(script) { - const chunks = bscript.decompile(script); - return (chunks.length === 2 && - bscript.isCanonicalPubKey(chunks[0]) && - chunks[1] === script_1.OPS.OP_CHECKSIG); + const chunks = bscript.decompile(script); + return ( + chunks.length === 2 && + bscript.isCanonicalPubKey(chunks[0]) && + chunks[1] === script_1.OPS.OP_CHECKSIG + ); } exports.check = check; check.toJSON = () => { - return 'pubKey output'; + return 'pubKey output'; }; diff --git a/src/templates/pubkeyhash/index.js b/src/templates/pubkeyhash/index.js index 85a15b9..b8cd6c4 100644 --- a/src/templates/pubkeyhash/index.js +++ b/src/templates/pubkeyhash/index.js @@ -1,6 +1,6 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const input = require("./input"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const input = require('./input'); exports.input = input; -const output = require("./output"); +const output = require('./output'); exports.output = output; diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js index 7de30ec..14d72cc 100644 --- a/src/templates/pubkeyhash/input.js +++ b/src/templates/pubkeyhash/input.js @@ -1,14 +1,16 @@ -"use strict"; +'use strict'; // {signature} {pubKey} -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); function check(script) { - const chunks = bscript.decompile(script); - return (chunks.length === 2 && - bscript.isCanonicalScriptSignature(chunks[0]) && - bscript.isCanonicalPubKey(chunks[1])); + const chunks = bscript.decompile(script); + return ( + chunks.length === 2 && + bscript.isCanonicalScriptSignature(chunks[0]) && + bscript.isCanonicalPubKey(chunks[1]) + ); } exports.check = check; check.toJSON = () => { - return 'pubKeyHash input'; + return 'pubKeyHash input'; }; diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.js index 5ee692b..079e1ed 100644 --- a/src/templates/pubkeyhash/output.js +++ b/src/templates/pubkeyhash/output.js @@ -1,18 +1,20 @@ -"use strict"; +'use strict'; // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); -const script_1 = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); +const script_1 = require('../../script'); function check(script) { - const buffer = bscript.compile(script); - return (buffer.length === 25 && - buffer[0] === script_1.OPS.OP_DUP && - buffer[1] === script_1.OPS.OP_HASH160 && - buffer[2] === 0x14 && - buffer[23] === script_1.OPS.OP_EQUALVERIFY && - buffer[24] === script_1.OPS.OP_CHECKSIG); + const buffer = bscript.compile(script); + return ( + buffer.length === 25 && + buffer[0] === script_1.OPS.OP_DUP && + buffer[1] === script_1.OPS.OP_HASH160 && + buffer[2] === 0x14 && + buffer[23] === script_1.OPS.OP_EQUALVERIFY && + buffer[24] === script_1.OPS.OP_CHECKSIG + ); } exports.check = check; check.toJSON = () => { - return 'pubKeyHash output'; + return 'pubKeyHash output'; }; diff --git a/src/templates/scripthash/index.js b/src/templates/scripthash/index.js index 85a15b9..b8cd6c4 100644 --- a/src/templates/scripthash/index.js +++ b/src/templates/scripthash/index.js @@ -1,6 +1,6 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const input = require("./input"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const input = require('./input'); exports.input = input; -const output = require("./output"); +const output = require('./output'); exports.output = output; diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js index 488b931..999cc83 100644 --- a/src/templates/scripthash/input.js +++ b/src/templates/scripthash/input.js @@ -1,44 +1,50 @@ -"use strict"; +'use strict'; // <scriptSig> {serialized scriptPubKey script} -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); -const p2ms = require("../multisig"); -const p2pk = require("../pubkey"); -const p2pkh = require("../pubkeyhash"); -const p2wpkho = require("../witnesspubkeyhash/output"); -const p2wsho = require("../witnessscripthash/output"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); +const p2ms = require('../multisig'); +const p2pk = require('../pubkey'); +const p2pkh = require('../pubkeyhash'); +const p2wpkho = require('../witnesspubkeyhash/output'); +const p2wsho = require('../witnessscripthash/output'); function check(script, allowIncomplete) { - const chunks = bscript.decompile(script); - if (chunks.length < 1) - return false; - const lastChunk = chunks[chunks.length - 1]; - if (!Buffer.isBuffer(lastChunk)) - return false; - const scriptSigChunks = bscript.decompile(bscript.compile(chunks.slice(0, -1))); - const redeemScriptChunks = bscript.decompile(lastChunk); - // is redeemScript a valid script? - if (!redeemScriptChunks) - return false; - // is redeemScriptSig push only? - if (!bscript.isPushOnly(scriptSigChunks)) - return false; - // is witness? - if (chunks.length === 1) { - return (p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks)); - } - // match types - if (p2pkh.input.check(scriptSigChunks) && - p2pkh.output.check(redeemScriptChunks)) - return true; - if (p2ms.input.check(scriptSigChunks, allowIncomplete) && - p2ms.output.check(redeemScriptChunks)) - return true; - if (p2pk.input.check(scriptSigChunks) && - p2pk.output.check(redeemScriptChunks)) - return true; - return false; + const chunks = bscript.decompile(script); + if (chunks.length < 1) return false; + const lastChunk = chunks[chunks.length - 1]; + if (!Buffer.isBuffer(lastChunk)) return false; + const scriptSigChunks = bscript.decompile( + bscript.compile(chunks.slice(0, -1)), + ); + const redeemScriptChunks = bscript.decompile(lastChunk); + // is redeemScript a valid script? + if (!redeemScriptChunks) return false; + // is redeemScriptSig push only? + if (!bscript.isPushOnly(scriptSigChunks)) return false; + // is witness? + if (chunks.length === 1) { + return ( + p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks) + ); + } + // match types + if ( + p2pkh.input.check(scriptSigChunks) && + p2pkh.output.check(redeemScriptChunks) + ) + return true; + if ( + p2ms.input.check(scriptSigChunks, allowIncomplete) && + p2ms.output.check(redeemScriptChunks) + ) + return true; + if ( + p2pk.input.check(scriptSigChunks) && + p2pk.output.check(redeemScriptChunks) + ) + return true; + return false; } exports.check = check; check.toJSON = () => { - return 'scriptHash input'; + return 'scriptHash input'; }; diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.js index bf1246a..3797003 100644 --- a/src/templates/scripthash/output.js +++ b/src/templates/scripthash/output.js @@ -1,16 +1,18 @@ -"use strict"; +'use strict'; // OP_HASH160 {scriptHash} OP_EQUAL -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); -const script_1 = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); +const script_1 = require('../../script'); function check(script) { - const buffer = bscript.compile(script); - return (buffer.length === 23 && - buffer[0] === script_1.OPS.OP_HASH160 && - buffer[1] === 0x14 && - buffer[22] === script_1.OPS.OP_EQUAL); + const buffer = bscript.compile(script); + return ( + buffer.length === 23 && + buffer[0] === script_1.OPS.OP_HASH160 && + buffer[1] === 0x14 && + buffer[22] === script_1.OPS.OP_EQUAL + ); } exports.check = check; check.toJSON = () => { - return 'scriptHash output'; + return 'scriptHash output'; }; diff --git a/src/templates/witnesscommitment/index.js b/src/templates/witnesscommitment/index.js index aff0618..099ac72 100644 --- a/src/templates/witnesscommitment/index.js +++ b/src/templates/witnesscommitment/index.js @@ -1,4 +1,4 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const output = require("./output"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const output = require('./output'); exports.output = output; diff --git a/src/templates/witnesscommitment/output.js b/src/templates/witnesscommitment/output.js index f4d6af0..fb1d59c 100644 --- a/src/templates/witnesscommitment/output.js +++ b/src/templates/witnesscommitment/output.js @@ -1,32 +1,34 @@ -"use strict"; +'use strict'; // OP_RETURN {aa21a9ed} {commitment} -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); -const script_1 = require("../../script"); -const types = require("../../types"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); +const script_1 = require('../../script'); +const types = require('../../types'); const typeforce = require('typeforce'); const HEADER = Buffer.from('aa21a9ed', 'hex'); function check(script) { - const buffer = bscript.compile(script); - return (buffer.length > 37 && - buffer[0] === script_1.OPS.OP_RETURN && - buffer[1] === 0x24 && - buffer.slice(2, 6).equals(HEADER)); + const buffer = bscript.compile(script); + return ( + buffer.length > 37 && + buffer[0] === script_1.OPS.OP_RETURN && + buffer[1] === 0x24 && + buffer.slice(2, 6).equals(HEADER) + ); } exports.check = check; check.toJSON = () => { - return 'Witness commitment output'; + return 'Witness commitment output'; }; function encode(commitment) { - typeforce(types.Hash256bit, commitment); - const buffer = Buffer.allocUnsafe(36); - HEADER.copy(buffer, 0); - commitment.copy(buffer, 4); - return bscript.compile([script_1.OPS.OP_RETURN, buffer]); + typeforce(types.Hash256bit, commitment); + const buffer = Buffer.allocUnsafe(36); + HEADER.copy(buffer, 0); + commitment.copy(buffer, 4); + return bscript.compile([script_1.OPS.OP_RETURN, buffer]); } exports.encode = encode; function decode(buffer) { - typeforce(check, buffer); - return bscript.decompile(buffer)[1].slice(4, 36); + typeforce(check, buffer); + return bscript.decompile(buffer)[1].slice(4, 36); } exports.decode = decode; diff --git a/src/templates/witnesspubkeyhash/index.js b/src/templates/witnesspubkeyhash/index.js index 85a15b9..b8cd6c4 100644 --- a/src/templates/witnesspubkeyhash/index.js +++ b/src/templates/witnesspubkeyhash/index.js @@ -1,6 +1,6 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const input = require("./input"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const input = require('./input'); exports.input = input; -const output = require("./output"); +const output = require('./output'); exports.output = output; diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.js index 3d589e9..4343584 100644 --- a/src/templates/witnesspubkeyhash/input.js +++ b/src/templates/witnesspubkeyhash/input.js @@ -1,17 +1,19 @@ -"use strict"; +'use strict'; // {signature} {pubKey} -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); function isCompressedCanonicalPubKey(pubKey) { - return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; + return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; } function check(script) { - const chunks = bscript.decompile(script); - return (chunks.length === 2 && - bscript.isCanonicalScriptSignature(chunks[0]) && - isCompressedCanonicalPubKey(chunks[1])); + const chunks = bscript.decompile(script); + return ( + chunks.length === 2 && + bscript.isCanonicalScriptSignature(chunks[0]) && + isCompressedCanonicalPubKey(chunks[1]) + ); } exports.check = check; check.toJSON = () => { - return 'witnessPubKeyHash input'; + return 'witnessPubKeyHash input'; }; diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.js index 69aab11..ea5ed1e 100644 --- a/src/templates/witnesspubkeyhash/output.js +++ b/src/templates/witnesspubkeyhash/output.js @@ -1,13 +1,17 @@ -"use strict"; +'use strict'; // OP_0 {pubKeyHash} -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); -const script_1 = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); +const script_1 = require('../../script'); function check(script) { - const buffer = bscript.compile(script); - return buffer.length === 22 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x14; + const buffer = bscript.compile(script); + return ( + buffer.length === 22 && + buffer[0] === script_1.OPS.OP_0 && + buffer[1] === 0x14 + ); } exports.check = check; check.toJSON = () => { - return 'Witness pubKeyHash output'; + return 'Witness pubKeyHash output'; }; diff --git a/src/templates/witnessscripthash/index.js b/src/templates/witnessscripthash/index.js index 85a15b9..b8cd6c4 100644 --- a/src/templates/witnessscripthash/index.js +++ b/src/templates/witnessscripthash/index.js @@ -1,6 +1,6 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const input = require("./input"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const input = require('./input'); exports.input = input; -const output = require("./output"); +const output = require('./output'); exports.output = output; diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js index 3f5c002..f69a810 100644 --- a/src/templates/witnessscripthash/input.js +++ b/src/templates/witnessscripthash/input.js @@ -1,36 +1,39 @@ -"use strict"; +'use strict'; // <scriptSig> {serialized scriptPubKey script} -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); const typeforce = require('typeforce'); -const p2ms = require("../multisig"); -const p2pk = require("../pubkey"); -const p2pkh = require("../pubkeyhash"); +const p2ms = require('../multisig'); +const p2pk = require('../pubkey'); +const p2pkh = require('../pubkeyhash'); function check(chunks, allowIncomplete) { - typeforce(typeforce.Array, chunks); - if (chunks.length < 1) - return false; - const witnessScript = chunks[chunks.length - 1]; - if (!Buffer.isBuffer(witnessScript)) - return false; - const witnessScriptChunks = bscript.decompile(witnessScript); - // is witnessScript a valid script? - if (!witnessScriptChunks || witnessScriptChunks.length === 0) - return false; - const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); - // match types - if (p2pkh.input.check(witnessRawScriptSig) && - p2pkh.output.check(witnessScriptChunks)) - return true; - if (p2ms.input.check(witnessRawScriptSig, allowIncomplete) && - p2ms.output.check(witnessScriptChunks)) - return true; - if (p2pk.input.check(witnessRawScriptSig) && - p2pk.output.check(witnessScriptChunks)) - return true; - return false; + typeforce(typeforce.Array, chunks); + if (chunks.length < 1) return false; + const witnessScript = chunks[chunks.length - 1]; + if (!Buffer.isBuffer(witnessScript)) return false; + const witnessScriptChunks = bscript.decompile(witnessScript); + // is witnessScript a valid script? + if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false; + const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); + // match types + if ( + p2pkh.input.check(witnessRawScriptSig) && + p2pkh.output.check(witnessScriptChunks) + ) + return true; + if ( + p2ms.input.check(witnessRawScriptSig, allowIncomplete) && + p2ms.output.check(witnessScriptChunks) + ) + return true; + if ( + p2pk.input.check(witnessRawScriptSig) && + p2pk.output.check(witnessScriptChunks) + ) + return true; + return false; } exports.check = check; check.toJSON = () => { - return 'witnessScriptHash input'; + return 'witnessScriptHash input'; }; diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.js index a6d4d95..f69a429 100644 --- a/src/templates/witnessscripthash/output.js +++ b/src/templates/witnessscripthash/output.js @@ -1,13 +1,17 @@ -"use strict"; +'use strict'; // OP_0 {scriptHash} -Object.defineProperty(exports, "__esModule", { value: true }); -const bscript = require("../../script"); -const script_1 = require("../../script"); +Object.defineProperty(exports, '__esModule', { value: true }); +const bscript = require('../../script'); +const script_1 = require('../../script'); function check(script) { - const buffer = bscript.compile(script); - return buffer.length === 34 && buffer[0] === script_1.OPS.OP_0 && buffer[1] === 0x20; + const buffer = bscript.compile(script); + return ( + buffer.length === 34 && + buffer[0] === script_1.OPS.OP_0 && + buffer[1] === 0x20 + ); } exports.check = check; check.toJSON = () => { - return 'Witness scriptHash output'; + return 'Witness scriptHash output'; }; diff --git a/src/transaction.js b/src/transaction.js index c6c847a..c4e6506 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,446 +1,472 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const bufferutils = require("./bufferutils"); -const bufferutils_1 = require("./bufferutils"); -const bcrypto = require("./crypto"); -const bscript = require("./script"); -const script_1 = require("./script"); -const types = require("./types"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const bufferutils = require('./bufferutils'); +const bufferutils_1 = require('./bufferutils'); +const bcrypto = require('./crypto'); +const bscript = require('./script'); +const script_1 = require('./script'); +const types = require('./types'); const typeforce = require('typeforce'); const varuint = require('varuint-bitcoin'); function varSliceSize(someScript) { - const length = someScript.length; - return varuint.encodingLength(length) + length; + const length = someScript.length; + return varuint.encodingLength(length) + length; } function vectorSize(someVector) { - const length = someVector.length; - return (varuint.encodingLength(length) + - someVector.reduce((sum, witness) => { - return sum + varSliceSize(witness); - }, 0)); + const length = someVector.length; + return ( + varuint.encodingLength(length) + + someVector.reduce((sum, witness) => { + return sum + varSliceSize(witness); + }, 0) + ); } const EMPTY_SCRIPT = Buffer.allocUnsafe(0); const EMPTY_WITNESS = []; -const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'); -const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'); +const ZERO = Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000000', + 'hex', +); +const ONE = Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000001', + 'hex', +); const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex'); const BLANK_OUTPUT = { - script: EMPTY_SCRIPT, - valueBuffer: VALUE_UINT64_MAX, + script: EMPTY_SCRIPT, + valueBuffer: VALUE_UINT64_MAX, }; function isOutput(out) { - return out.value !== undefined; + return out.value !== undefined; } class Transaction { - constructor() { - this.version = 1; - this.locktime = 0; - this.ins = []; - this.outs = []; + constructor() { + this.version = 1; + this.locktime = 0; + this.ins = []; + this.outs = []; + } + static fromBuffer(buffer, _NO_STRICT) { + let offset = 0; + function readSlice(n) { + offset += n; + return buffer.slice(offset - n, offset); } - static fromBuffer(buffer, _NO_STRICT) { - let offset = 0; - function readSlice(n) { - offset += n; - return buffer.slice(offset - n, offset); - } - function readUInt32() { - const i = buffer.readUInt32LE(offset); - offset += 4; - return i; - } - function readInt32() { - const i = buffer.readInt32LE(offset); - offset += 4; - return i; - } - function readUInt64() { - const i = bufferutils.readUInt64LE(buffer, offset); - offset += 8; - return i; - } - function readVarInt() { - const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; - return vi; - } - function readVarSlice() { - return readSlice(readVarInt()); - } - function readVector() { - const count = readVarInt(); - const vector = []; - for (let i = 0; i < count; i++) - vector.push(readVarSlice()); - return vector; - } - const tx = new Transaction(); - tx.version = readInt32(); - const marker = buffer.readUInt8(offset); - const flag = buffer.readUInt8(offset + 1); - let hasWitnesses = false; - if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && - flag === Transaction.ADVANCED_TRANSACTION_FLAG) { - offset += 2; - hasWitnesses = true; - } - const vinLen = readVarInt(); - for (let i = 0; i < vinLen; ++i) { - tx.ins.push({ - hash: readSlice(32), - index: readUInt32(), - script: readVarSlice(), - sequence: readUInt32(), - witness: EMPTY_WITNESS, - }); - } - const voutLen = readVarInt(); - for (let i = 0; i < voutLen; ++i) { - tx.outs.push({ - value: readUInt64(), - script: readVarSlice(), - }); - } - if (hasWitnesses) { - for (let i = 0; i < vinLen; ++i) { - tx.ins[i].witness = readVector(); - } - // was this pointless? - if (!tx.hasWitnesses()) - throw new Error('Transaction has superfluous witness data'); - } - tx.locktime = readUInt32(); - if (_NO_STRICT) - return tx; - if (offset !== buffer.length) - throw new Error('Transaction has unexpected data'); - return tx; + function readUInt32() { + const i = buffer.readUInt32LE(offset); + offset += 4; + return i; } - static fromHex(hex) { - return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false); + function readInt32() { + const i = buffer.readInt32LE(offset); + offset += 4; + return i; } - static isCoinbaseHash(buffer) { - typeforce(types.Hash256bit, buffer); - for (let i = 0; i < 32; ++i) { - if (buffer[i] !== 0) - return false; - } - return true; + function readUInt64() { + const i = bufferutils.readUInt64LE(buffer, offset); + offset += 8; + return i; } - isCoinbase() { - return (this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash)); + function readVarInt() { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; } - addInput(hash, index, sequence, scriptSig) { - typeforce(types.tuple(types.Hash256bit, types.UInt32, types.maybe(types.UInt32), types.maybe(types.Buffer)), arguments); - if (types.Null(sequence)) { - sequence = Transaction.DEFAULT_SEQUENCE; - } - // Add the input and return the input's index - return (this.ins.push({ - hash, - index, - script: scriptSig || EMPTY_SCRIPT, - sequence: sequence, - witness: EMPTY_WITNESS, - }) - 1); + function readVarSlice() { + return readSlice(readVarInt()); } - addOutput(scriptPubKey, value) { - typeforce(types.tuple(types.Buffer, types.Satoshi), arguments); - // Add the output and return the output's index - return (this.outs.push({ - script: scriptPubKey, - value, - }) - 1); + function readVector() { + const count = readVarInt(); + const vector = []; + for (let i = 0; i < count; i++) vector.push(readVarSlice()); + return vector; } - hasWitnesses() { - return this.ins.some(x => { - return x.witness.length !== 0; - }); + const tx = new Transaction(); + tx.version = readInt32(); + const marker = buffer.readUInt8(offset); + const flag = buffer.readUInt8(offset + 1); + let hasWitnesses = false; + if ( + marker === Transaction.ADVANCED_TRANSACTION_MARKER && + flag === Transaction.ADVANCED_TRANSACTION_FLAG + ) { + offset += 2; + hasWitnesses = true; } - weight() { - const base = this.__byteLength(false); - const total = this.__byteLength(true); - return base * 3 + total; + const vinLen = readVarInt(); + for (let i = 0; i < vinLen; ++i) { + tx.ins.push({ + hash: readSlice(32), + index: readUInt32(), + script: readVarSlice(), + sequence: readUInt32(), + witness: EMPTY_WITNESS, + }); } - virtualSize() { - return Math.ceil(this.weight() / 4); + const voutLen = readVarInt(); + for (let i = 0; i < voutLen; ++i) { + tx.outs.push({ + value: readUInt64(), + script: readVarSlice(), + }); } - byteLength() { - return this.__byteLength(true); + if (hasWitnesses) { + for (let i = 0; i < vinLen; ++i) { + tx.ins[i].witness = readVector(); + } + // was this pointless? + if (!tx.hasWitnesses()) + throw new Error('Transaction has superfluous witness data'); } - clone() { - const newTx = new Transaction(); - newTx.version = this.version; - newTx.locktime = this.locktime; - newTx.ins = this.ins.map(txIn => { - return { - hash: txIn.hash, - index: txIn.index, - script: txIn.script, - sequence: txIn.sequence, - witness: txIn.witness, - }; - }); - newTx.outs = this.outs.map(txOut => { - return { - script: txOut.script, - value: txOut.value, - }; - }); - return newTx; + tx.locktime = readUInt32(); + if (_NO_STRICT) return tx; + if (offset !== buffer.length) + throw new Error('Transaction has unexpected data'); + return tx; + } + static fromHex(hex) { + return Transaction.fromBuffer(Buffer.from(hex, 'hex'), false); + } + static isCoinbaseHash(buffer) { + typeforce(types.Hash256bit, buffer); + for (let i = 0; i < 32; ++i) { + if (buffer[i] !== 0) return false; } - /** - * Hash transaction for signing a specific input. - * - * Bitcoin uses a different hash for each signed transaction input. - * This method copies the transaction, makes the necessary changes based on the - * hashType, and then hashes the result. - * This hash can then be used to sign the provided transaction input. - */ - hashForSignature(inIndex, prevOutScript, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments); - // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 - if (inIndex >= this.ins.length) - return ONE; - // ignore OP_CODESEPARATOR - const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(x => { - return x !== script_1.OPS.OP_CODESEPARATOR; - })); - const txTmp = this.clone(); - // SIGHASH_NONE: ignore all outputs? (wildcard payee) - if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { - txTmp.outs = []; - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach((input, i) => { - if (i === inIndex) - return; - input.sequence = 0; - }); - // SIGHASH_SINGLE: ignore all outputs, except at the same index? - } - else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { - // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 - if (inIndex >= this.outs.length) - return ONE; - // truncate outputs after - txTmp.outs.length = inIndex + 1; - // "blank" outputs before - for (let i = 0; i < inIndex; i++) { - txTmp.outs[i] = BLANK_OUTPUT; - } - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach((input, y) => { - if (y === inIndex) - return; - input.sequence = 0; - }); - } - // SIGHASH_ANYONECANPAY: ignore inputs entirely? - if (hashType & Transaction.SIGHASH_ANYONECANPAY) { - txTmp.ins = [txTmp.ins[inIndex]]; - txTmp.ins[0].script = ourScript; - // SIGHASH_ALL: only ignore input scripts - } - else { - // "blank" others input scripts - txTmp.ins.forEach(input => { - input.script = EMPTY_SCRIPT; - }); - txTmp.ins[inIndex].script = ourScript; - } - // serialize and hash - const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4); - buffer.writeInt32LE(hashType, buffer.length - 4); - txTmp.__toBuffer(buffer, 0, false); - return bcrypto.hash256(buffer); + return true; + } + isCoinbase() { + return ( + this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) + ); + } + addInput(hash, index, sequence, scriptSig) { + typeforce( + types.tuple( + types.Hash256bit, + types.UInt32, + types.maybe(types.UInt32), + types.maybe(types.Buffer), + ), + arguments, + ); + if (types.Null(sequence)) { + sequence = Transaction.DEFAULT_SEQUENCE; } - hashForWitnessV0(inIndex, prevOutScript, value, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments); - let tbuffer = Buffer.from([]); - let toffset = 0; - function writeSlice(slice) { - toffset += slice.copy(tbuffer, toffset); - } - function writeUInt32(i) { - toffset = tbuffer.writeUInt32LE(i, toffset); - } - function writeUInt64(i) { - toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset); - } - function writeVarInt(i) { - varuint.encode(i, tbuffer, toffset); - toffset += varuint.encode.bytes; - } - function writeVarSlice(slice) { - writeVarInt(slice.length); - writeSlice(slice); - } - let hashOutputs = ZERO; - let hashPrevouts = ZERO; - let hashSequence = ZERO; - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { - tbuffer = Buffer.allocUnsafe(36 * this.ins.length); - toffset = 0; - this.ins.forEach(txIn => { - writeSlice(txIn.hash); - writeUInt32(txIn.index); - }); - hashPrevouts = bcrypto.hash256(tbuffer); - } - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && - (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - tbuffer = Buffer.allocUnsafe(4 * this.ins.length); - toffset = 0; - this.ins.forEach(txIn => { - writeUInt32(txIn.sequence); - }); - hashSequence = bcrypto.hash256(tbuffer); - } - if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { - const txOutsSize = this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script); - }, 0); - tbuffer = Buffer.allocUnsafe(txOutsSize); - toffset = 0; - this.outs.forEach(out => { - writeUInt64(out.value); - writeVarSlice(out.script); - }); - hashOutputs = bcrypto.hash256(tbuffer); - } - else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && - inIndex < this.outs.length) { - const output = this.outs[inIndex]; - tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); - toffset = 0; - writeUInt64(output.value); - writeVarSlice(output.script); - hashOutputs = bcrypto.hash256(tbuffer); - } - tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)); - toffset = 0; - const input = this.ins[inIndex]; - writeUInt32(this.version); - writeSlice(hashPrevouts); - writeSlice(hashSequence); - writeSlice(input.hash); - writeUInt32(input.index); - writeVarSlice(prevOutScript); - writeUInt64(value); - writeUInt32(input.sequence); - writeSlice(hashOutputs); - writeUInt32(this.locktime); - writeUInt32(hashType); - return bcrypto.hash256(tbuffer); + // Add the input and return the input's index + return ( + this.ins.push({ + hash, + index, + script: scriptSig || EMPTY_SCRIPT, + sequence: sequence, + witness: EMPTY_WITNESS, + }) - 1 + ); + } + addOutput(scriptPubKey, value) { + typeforce(types.tuple(types.Buffer, types.Satoshi), arguments); + // Add the output and return the output's index + return ( + this.outs.push({ + script: scriptPubKey, + value, + }) - 1 + ); + } + hasWitnesses() { + return this.ins.some(x => { + return x.witness.length !== 0; + }); + } + weight() { + const base = this.__byteLength(false); + const total = this.__byteLength(true); + return base * 3 + total; + } + virtualSize() { + return Math.ceil(this.weight() / 4); + } + byteLength() { + return this.__byteLength(true); + } + clone() { + const newTx = new Transaction(); + newTx.version = this.version; + newTx.locktime = this.locktime; + newTx.ins = this.ins.map(txIn => { + return { + hash: txIn.hash, + index: txIn.index, + script: txIn.script, + sequence: txIn.sequence, + witness: txIn.witness, + }; + }); + newTx.outs = this.outs.map(txOut => { + return { + script: txOut.script, + value: txOut.value, + }; + }); + return newTx; + } + /** + * Hash transaction for signing a specific input. + * + * Bitcoin uses a different hash for each signed transaction input. + * This method copies the transaction, makes the necessary changes based on the + * hashType, and then hashes the result. + * This hash can then be used to sign the provided transaction input. + */ + hashForSignature(inIndex, prevOutScript, hashType) { + typeforce( + types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), + arguments, + ); + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 + if (inIndex >= this.ins.length) return ONE; + // ignore OP_CODESEPARATOR + const ourScript = bscript.compile( + bscript.decompile(prevOutScript).filter(x => { + return x !== script_1.OPS.OP_CODESEPARATOR; + }), + ); + const txTmp = this.clone(); + // SIGHASH_NONE: ignore all outputs? (wildcard payee) + if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { + txTmp.outs = []; + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, i) => { + if (i === inIndex) return; + input.sequence = 0; + }); + // SIGHASH_SINGLE: ignore all outputs, except at the same index? + } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 + if (inIndex >= this.outs.length) return ONE; + // truncate outputs after + txTmp.outs.length = inIndex + 1; + // "blank" outputs before + for (let i = 0; i < inIndex; i++) { + txTmp.outs[i] = BLANK_OUTPUT; + } + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, y) => { + if (y === inIndex) return; + input.sequence = 0; + }); } - getHash(forWitness) { - // wtxid for coinbase is always 32 bytes of 0x00 - if (forWitness && this.isCoinbase()) - return Buffer.alloc(32, 0); - return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness)); + // SIGHASH_ANYONECANPAY: ignore inputs entirely? + if (hashType & Transaction.SIGHASH_ANYONECANPAY) { + txTmp.ins = [txTmp.ins[inIndex]]; + txTmp.ins[0].script = ourScript; + // SIGHASH_ALL: only ignore input scripts + } else { + // "blank" others input scripts + txTmp.ins.forEach(input => { + input.script = EMPTY_SCRIPT; + }); + txTmp.ins[inIndex].script = ourScript; } - getId() { - // transaction hash's are displayed in reverse order - return bufferutils_1.reverseBuffer(this.getHash(false)).toString('hex'); + // serialize and hash + const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4); + buffer.writeInt32LE(hashType, buffer.length - 4); + txTmp.__toBuffer(buffer, 0, false); + return bcrypto.hash256(buffer); + } + hashForWitnessV0(inIndex, prevOutScript, value, hashType) { + typeforce( + types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), + arguments, + ); + let tbuffer = Buffer.from([]); + let toffset = 0; + function writeSlice(slice) { + toffset += slice.copy(tbuffer, toffset); } - toBuffer(buffer, initialOffset) { - return this.__toBuffer(buffer, initialOffset, true); + function writeUInt32(i) { + toffset = tbuffer.writeUInt32LE(i, toffset); } - toHex() { - return this.toBuffer(undefined, undefined).toString('hex'); + function writeUInt64(i) { + toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset); } - setInputScript(index, scriptSig) { - typeforce(types.tuple(types.Number, types.Buffer), arguments); - this.ins[index].script = scriptSig; + function writeVarInt(i) { + varuint.encode(i, tbuffer, toffset); + toffset += varuint.encode.bytes; } - setWitness(index, witness) { - typeforce(types.tuple(types.Number, [types.Buffer]), arguments); - this.ins[index].witness = witness; + function writeVarSlice(slice) { + writeVarInt(slice.length); + writeSlice(slice); } - __byteLength(_ALLOW_WITNESS) { - const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); - return ((hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce((sum, input) => { - return sum + 40 + varSliceSize(input.script); - }, 0) + - this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script); - }, 0) + - (hasWitnesses - ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness); - }, 0) - : 0)); + let hashOutputs = ZERO; + let hashPrevouts = ZERO; + let hashSequence = ZERO; + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { + tbuffer = Buffer.allocUnsafe(36 * this.ins.length); + toffset = 0; + this.ins.forEach(txIn => { + writeSlice(txIn.hash); + writeUInt32(txIn.index); + }); + hashPrevouts = bcrypto.hash256(tbuffer); } - __toBuffer(buffer, initialOffset, _ALLOW_WITNESS) { - if (!buffer) - buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS)); - let offset = initialOffset || 0; - function writeSlice(slice) { - offset += slice.copy(buffer, offset); - } - function writeUInt8(i) { - offset = buffer.writeUInt8(i, offset); - } - function writeUInt32(i) { - offset = buffer.writeUInt32LE(i, offset); - } - function writeInt32(i) { - offset = buffer.writeInt32LE(i, offset); - } - function writeUInt64(i) { - offset = bufferutils.writeUInt64LE(buffer, i, offset); - } - function writeVarInt(i) { - varuint.encode(i, buffer, offset); - offset += varuint.encode.bytes; - } - function writeVarSlice(slice) { - writeVarInt(slice.length); - writeSlice(slice); - } - function writeVector(vector) { - writeVarInt(vector.length); - vector.forEach(writeVarSlice); - } - writeInt32(this.version); - const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); - if (hasWitnesses) { - writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); - writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); - } - writeVarInt(this.ins.length); - this.ins.forEach(txIn => { - writeSlice(txIn.hash); - writeUInt32(txIn.index); - writeVarSlice(txIn.script); - writeUInt32(txIn.sequence); - }); - writeVarInt(this.outs.length); - this.outs.forEach(txOut => { - if (isOutput(txOut)) { - writeUInt64(txOut.value); - } - else { - writeSlice(txOut.valueBuffer); - } - writeVarSlice(txOut.script); - }); - if (hasWitnesses) { - this.ins.forEach(input => { - writeVector(input.witness); - }); - } - writeUInt32(this.locktime); - // avoid slicing unless necessary - if (initialOffset !== undefined) - return buffer.slice(initialOffset, offset); - return buffer; + if ( + !(hashType & Transaction.SIGHASH_ANYONECANPAY) && + (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE + ) { + tbuffer = Buffer.allocUnsafe(4 * this.ins.length); + toffset = 0; + this.ins.forEach(txIn => { + writeUInt32(txIn.sequence); + }); + hashSequence = bcrypto.hash256(tbuffer); } + if ( + (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE + ) { + const txOutsSize = this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0); + tbuffer = Buffer.allocUnsafe(txOutsSize); + toffset = 0; + this.outs.forEach(out => { + writeUInt64(out.value); + writeVarSlice(out.script); + }); + hashOutputs = bcrypto.hash256(tbuffer); + } else if ( + (hashType & 0x1f) === Transaction.SIGHASH_SINGLE && + inIndex < this.outs.length + ) { + const output = this.outs[inIndex]; + tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); + toffset = 0; + writeUInt64(output.value); + writeVarSlice(output.script); + hashOutputs = bcrypto.hash256(tbuffer); + } + tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)); + toffset = 0; + const input = this.ins[inIndex]; + writeUInt32(this.version); + writeSlice(hashPrevouts); + writeSlice(hashSequence); + writeSlice(input.hash); + writeUInt32(input.index); + writeVarSlice(prevOutScript); + writeUInt64(value); + writeUInt32(input.sequence); + writeSlice(hashOutputs); + writeUInt32(this.locktime); + writeUInt32(hashType); + return bcrypto.hash256(tbuffer); + } + getHash(forWitness) { + // wtxid for coinbase is always 32 bytes of 0x00 + if (forWitness && this.isCoinbase()) return Buffer.alloc(32, 0); + return bcrypto.hash256(this.__toBuffer(undefined, undefined, forWitness)); + } + getId() { + // transaction hash's are displayed in reverse order + return bufferutils_1.reverseBuffer(this.getHash(false)).toString('hex'); + } + toBuffer(buffer, initialOffset) { + return this.__toBuffer(buffer, initialOffset, true); + } + toHex() { + return this.toBuffer(undefined, undefined).toString('hex'); + } + setInputScript(index, scriptSig) { + typeforce(types.tuple(types.Number, types.Buffer), arguments); + this.ins[index].script = scriptSig; + } + setWitness(index, witness) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments); + this.ins[index].witness = witness; + } + __byteLength(_ALLOW_WITNESS) { + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); + return ( + (hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script); + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0) + + (hasWitnesses + ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) + : 0) + ); + } + __toBuffer(buffer, initialOffset, _ALLOW_WITNESS) { + if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS)); + let offset = initialOffset || 0; + function writeSlice(slice) { + offset += slice.copy(buffer, offset); + } + function writeUInt8(i) { + offset = buffer.writeUInt8(i, offset); + } + function writeUInt32(i) { + offset = buffer.writeUInt32LE(i, offset); + } + function writeInt32(i) { + offset = buffer.writeInt32LE(i, offset); + } + function writeUInt64(i) { + offset = bufferutils.writeUInt64LE(buffer, i, offset); + } + function writeVarInt(i) { + varuint.encode(i, buffer, offset); + offset += varuint.encode.bytes; + } + function writeVarSlice(slice) { + writeVarInt(slice.length); + writeSlice(slice); + } + function writeVector(vector) { + writeVarInt(vector.length); + vector.forEach(writeVarSlice); + } + writeInt32(this.version); + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); + if (hasWitnesses) { + writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); + writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); + } + writeVarInt(this.ins.length); + this.ins.forEach(txIn => { + writeSlice(txIn.hash); + writeUInt32(txIn.index); + writeVarSlice(txIn.script); + writeUInt32(txIn.sequence); + }); + writeVarInt(this.outs.length); + this.outs.forEach(txOut => { + if (isOutput(txOut)) { + writeUInt64(txOut.value); + } else { + writeSlice(txOut.valueBuffer); + } + writeVarSlice(txOut.script); + }); + if (hasWitnesses) { + this.ins.forEach(input => { + writeVector(input.witness); + }); + } + writeUInt32(this.locktime); + // avoid slicing unless necessary + if (initialOffset !== undefined) return buffer.slice(initialOffset, offset); + return buffer; + } } Transaction.DEFAULT_SEQUENCE = 0xffffffff; Transaction.SIGHASH_ALL = 0x01; diff --git a/src/transaction_builder.js b/src/transaction_builder.js index d3b2673..a4dcdf8 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -1,720 +1,739 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const baddress = require("./address"); -const bufferutils_1 = require("./bufferutils"); -const classify = require("./classify"); -const bcrypto = require("./crypto"); -const ECPair = require("./ecpair"); -const networks = require("./networks"); -const payments = require("./payments"); -const bscript = require("./script"); -const script_1 = require("./script"); -const transaction_1 = require("./transaction"); -const types = require("./types"); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const baddress = require('./address'); +const bufferutils_1 = require('./bufferutils'); +const classify = require('./classify'); +const bcrypto = require('./crypto'); +const ECPair = require('./ecpair'); +const networks = require('./networks'); +const payments = require('./payments'); +const bscript = require('./script'); +const script_1 = require('./script'); +const transaction_1 = require('./transaction'); +const types = require('./types'); const typeforce = require('typeforce'); const SCRIPT_TYPES = classify.types; function txIsString(tx) { - return typeof tx === 'string' || tx instanceof String; + return typeof tx === 'string' || tx instanceof String; } function txIsTransaction(tx) { - return tx instanceof transaction_1.Transaction; + return tx instanceof transaction_1.Transaction; } class TransactionBuilder { - // WARNING: maximumFeeRate is __NOT__ to be relied on, - // it's just another potential safety mechanism (safety in-depth) - constructor(network = networks.bitcoin, maximumFeeRate = 2500) { - this.network = network; - this.maximumFeeRate = maximumFeeRate; - this.__PREV_TX_SET = {}; - this.__INPUTS = []; - this.__TX = new transaction_1.Transaction(); - this.__TX.version = 2; + // WARNING: maximumFeeRate is __NOT__ to be relied on, + // it's just another potential safety mechanism (safety in-depth) + constructor(network = networks.bitcoin, maximumFeeRate = 2500) { + this.network = network; + this.maximumFeeRate = maximumFeeRate; + this.__PREV_TX_SET = {}; + this.__INPUTS = []; + this.__TX = new transaction_1.Transaction(); + this.__TX.version = 2; + } + static fromTransaction(transaction, network) { + const txb = new TransactionBuilder(network); + // Copy transaction fields + txb.setVersion(transaction.version); + txb.setLockTime(transaction.locktime); + // Copy outputs (done first to avoid signature invalidation) + transaction.outs.forEach(txOut => { + txb.addOutput(txOut.script, txOut.value); + }); + // Copy inputs + transaction.ins.forEach(txIn => { + txb.__addInputUnsafe(txIn.hash, txIn.index, { + sequence: txIn.sequence, + script: txIn.script, + witness: txIn.witness, + }); + }); + // fix some things not possible through the public API + txb.__INPUTS.forEach((input, i) => { + fixMultisigOrder(input, transaction, i); + }); + return txb; + } + setLockTime(locktime) { + typeforce(types.UInt32, locktime); + // if any signatures exist, throw + if ( + this.__INPUTS.some(input => { + if (!input.signatures) return false; + return input.signatures.some(s => s !== undefined); + }) + ) { + throw new Error('No, this would invalidate signatures'); } - static fromTransaction(transaction, network) { - const txb = new TransactionBuilder(network); - // Copy transaction fields - txb.setVersion(transaction.version); - txb.setLockTime(transaction.locktime); - // Copy outputs (done first to avoid signature invalidation) - transaction.outs.forEach(txOut => { - txb.addOutput(txOut.script, txOut.value); + this.__TX.locktime = locktime; + } + setVersion(version) { + typeforce(types.UInt32, version); + // XXX: this might eventually become more complex depending on what the versions represent + this.__TX.version = version; + } + addInput(txHash, vout, sequence, prevOutScript) { + if (!this.__canModifyInputs()) { + throw new Error('No, this would invalidate signatures'); + } + let value; + // is it a hex string? + if (txIsString(txHash)) { + // transaction hashs's are displayed in reverse order, un-reverse it + txHash = bufferutils_1.reverseBuffer(Buffer.from(txHash, 'hex')); + // is it a Transaction object? + } else if (txIsTransaction(txHash)) { + const txOut = txHash.outs[vout]; + prevOutScript = txOut.script; + value = txOut.value; + txHash = txHash.getHash(false); + } + return this.__addInputUnsafe(txHash, vout, { + sequence, + prevOutScript, + value, + }); + } + addOutput(scriptPubKey, value) { + if (!this.__canModifyOutputs()) { + throw new Error('No, this would invalidate signatures'); + } + // Attempt to get a script if it's a base58 or bech32 address string + if (typeof scriptPubKey === 'string') { + scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); + } + return this.__TX.addOutput(scriptPubKey, value); + } + build() { + return this.__build(false); + } + buildIncomplete() { + return this.__build(true); + } + sign(vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) { + // TODO: remove keyPair.network matching in 4.0.0 + if (keyPair.network && keyPair.network !== this.network) + throw new TypeError('Inconsistent network'); + if (!this.__INPUTS[vin]) throw new Error('No input at index: ' + vin); + hashType = hashType || transaction_1.Transaction.SIGHASH_ALL; + if (this.__needsOutputs(hashType)) + throw new Error('Transaction needs outputs'); + const input = this.__INPUTS[vin]; + // if redeemScript was previously provided, enforce consistency + if ( + input.redeemScript !== undefined && + redeemScript && + !input.redeemScript.equals(redeemScript) + ) { + throw new Error('Inconsistent redeemScript'); + } + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey(); + if (!canSign(input)) { + if (witnessValue !== undefined) { + if (input.value !== undefined && input.value !== witnessValue) + throw new Error('Input did not match witnessValue'); + typeforce(types.Satoshi, witnessValue); + input.value = witnessValue; + } + if (!canSign(input)) { + const prepared = prepareInput( + input, + ourPubKey, + redeemScript, + witnessScript, + ); + // updates inline + Object.assign(input, prepared); + } + if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); + } + // ready to sign + let signatureHash; + if (input.hasWitness) { + signatureHash = this.__TX.hashForWitnessV0( + vin, + input.signScript, + input.value, + hashType, + ); + } else { + signatureHash = this.__TX.hashForSignature( + vin, + input.signScript, + hashType, + ); + } + // enforce in order signing of public keys + const signed = input.pubkeys.some((pubKey, i) => { + if (!ourPubKey.equals(pubKey)) return false; + if (input.signatures[i]) throw new Error('Signature already exists'); + // TODO: add tests + if (ourPubKey.length !== 33 && input.hasWitness) { + throw new Error( + 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', + ); + } + const signature = keyPair.sign(signatureHash); + input.signatures[i] = bscript.signature.encode(signature, hashType); + return true; + }); + if (!signed) throw new Error('Key pair cannot sign for this input'); + } + __addInputUnsafe(txHash, vout, options) { + if (transaction_1.Transaction.isCoinbaseHash(txHash)) { + throw new Error('coinbase inputs not supported'); + } + const prevTxOut = txHash.toString('hex') + ':' + vout; + if (this.__PREV_TX_SET[prevTxOut] !== undefined) + throw new Error('Duplicate TxOut: ' + prevTxOut); + let input = {}; + // derive what we can from the scriptSig + if (options.script !== undefined) { + input = expandInput(options.script, options.witness || []); + } + // if an input value was given, retain it + if (options.value !== undefined) { + input.value = options.value; + } + // derive what we can from the previous transactions output script + if (!input.prevOutScript && options.prevOutScript) { + let prevOutType; + if (!input.pubkeys && !input.signatures) { + const expanded = expandOutput(options.prevOutScript); + if (expanded.pubkeys) { + input.pubkeys = expanded.pubkeys; + input.signatures = expanded.signatures; + } + prevOutType = expanded.type; + } + input.prevOutScript = options.prevOutScript; + input.prevOutType = prevOutType || classify.output(options.prevOutScript); + } + const vin = this.__TX.addInput( + txHash, + vout, + options.sequence, + options.scriptSig, + ); + this.__INPUTS[vin] = input; + this.__PREV_TX_SET[prevTxOut] = true; + return vin; + } + __build(allowIncomplete) { + if (!allowIncomplete) { + if (!this.__TX.ins.length) throw new Error('Transaction has no inputs'); + if (!this.__TX.outs.length) throw new Error('Transaction has no outputs'); + } + const tx = this.__TX.clone(); + // create script signatures from inputs + this.__INPUTS.forEach((input, i) => { + if (!input.prevOutType && !allowIncomplete) + throw new Error('Transaction is not complete'); + const result = build(input.prevOutType, input, allowIncomplete); + if (!result) { + if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) + throw new Error('Unknown input type'); + if (!allowIncomplete) throw new Error('Not enough information'); + return; + } + tx.setInputScript(i, result.input); + tx.setWitness(i, result.witness); + }); + if (!allowIncomplete) { + // do not rely on this, its merely a last resort + if (this.__overMaximumFees(tx.virtualSize())) { + throw new Error('Transaction has absurd fees'); + } + } + return tx; + } + __canModifyInputs() { + return this.__INPUTS.every(input => { + if (!input.signatures) return true; + return input.signatures.every(signature => { + if (!signature) return true; + const hashType = signatureHashType(signature); + // if SIGHASH_ANYONECANPAY is set, signatures would not + // be invalidated by more inputs + return ( + (hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY) !== 0 + ); + }); + }); + } + __needsOutputs(signingHashType) { + if (signingHashType === transaction_1.Transaction.SIGHASH_ALL) { + return this.__TX.outs.length === 0; + } + // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs + // .build() will fail, but .buildIncomplete() is OK + return ( + this.__TX.outs.length === 0 && + this.__INPUTS.some(input => { + if (!input.signatures) return false; + return input.signatures.some(signature => { + if (!signature) return false; // no signature, no issue + const hashType = signatureHashType(signature); + if (hashType & transaction_1.Transaction.SIGHASH_NONE) return false; // SIGHASH_NONE doesn't care about outputs + return true; // SIGHASH_* does care }); - // Copy inputs - transaction.ins.forEach(txIn => { - txb.__addInputUnsafe(txIn.hash, txIn.index, { - sequence: txIn.sequence, - script: txIn.script, - witness: txIn.witness, - }); - }); - // fix some things not possible through the public API - txb.__INPUTS.forEach((input, i) => { - fixMultisigOrder(input, transaction, i); - }); - return txb; - } - setLockTime(locktime) { - typeforce(types.UInt32, locktime); - // if any signatures exist, throw - if (this.__INPUTS.some(input => { - if (!input.signatures) - return false; - return input.signatures.some(s => s !== undefined); - })) { - throw new Error('No, this would invalidate signatures'); + }) + ); + } + __canModifyOutputs() { + const nInputs = this.__TX.ins.length; + const nOutputs = this.__TX.outs.length; + return this.__INPUTS.every(input => { + if (input.signatures === undefined) return true; + return input.signatures.every(signature => { + if (!signature) return true; + const hashType = signatureHashType(signature); + const hashTypeMod = hashType & 0x1f; + if (hashTypeMod === transaction_1.Transaction.SIGHASH_NONE) return true; + if (hashTypeMod === transaction_1.Transaction.SIGHASH_SINGLE) { + // if SIGHASH_SINGLE is set, and nInputs > nOutputs + // some signatures would be invalidated by the addition + // of more outputs + return nInputs <= nOutputs; } - this.__TX.locktime = locktime; - } - setVersion(version) { - typeforce(types.UInt32, version); - // XXX: this might eventually become more complex depending on what the versions represent - this.__TX.version = version; - } - addInput(txHash, vout, sequence, prevOutScript) { - if (!this.__canModifyInputs()) { - throw new Error('No, this would invalidate signatures'); - } - let value; - // is it a hex string? - if (txIsString(txHash)) { - // transaction hashs's are displayed in reverse order, un-reverse it - txHash = bufferutils_1.reverseBuffer(Buffer.from(txHash, 'hex')); - // is it a Transaction object? - } - else if (txIsTransaction(txHash)) { - const txOut = txHash.outs[vout]; - prevOutScript = txOut.script; - value = txOut.value; - txHash = txHash.getHash(false); - } - return this.__addInputUnsafe(txHash, vout, { - sequence, - prevOutScript, - value, - }); - } - addOutput(scriptPubKey, value) { - if (!this.__canModifyOutputs()) { - throw new Error('No, this would invalidate signatures'); - } - // Attempt to get a script if it's a base58 or bech32 address string - if (typeof scriptPubKey === 'string') { - scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); - } - return this.__TX.addOutput(scriptPubKey, value); - } - build() { - return this.__build(false); - } - buildIncomplete() { - return this.__build(true); - } - sign(vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) { - // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== this.network) - throw new TypeError('Inconsistent network'); - if (!this.__INPUTS[vin]) - throw new Error('No input at index: ' + vin); - hashType = hashType || transaction_1.Transaction.SIGHASH_ALL; - if (this.__needsOutputs(hashType)) - throw new Error('Transaction needs outputs'); - const input = this.__INPUTS[vin]; - // if redeemScript was previously provided, enforce consistency - if (input.redeemScript !== undefined && - redeemScript && - !input.redeemScript.equals(redeemScript)) { - throw new Error('Inconsistent redeemScript'); - } - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey(); - if (!canSign(input)) { - if (witnessValue !== undefined) { - if (input.value !== undefined && input.value !== witnessValue) - throw new Error('Input did not match witnessValue'); - typeforce(types.Satoshi, witnessValue); - input.value = witnessValue; - } - if (!canSign(input)) { - const prepared = prepareInput(input, ourPubKey, redeemScript, witnessScript); - // updates inline - Object.assign(input, prepared); - } - if (!canSign(input)) - throw Error(input.prevOutType + ' not supported'); - } - // ready to sign - let signatureHash; - if (input.hasWitness) { - signatureHash = this.__TX.hashForWitnessV0(vin, input.signScript, input.value, hashType); - } - else { - signatureHash = this.__TX.hashForSignature(vin, input.signScript, hashType); - } - // enforce in order signing of public keys - const signed = input.pubkeys.some((pubKey, i) => { - if (!ourPubKey.equals(pubKey)) - return false; - if (input.signatures[i]) - throw new Error('Signature already exists'); - // TODO: add tests - if (ourPubKey.length !== 33 && input.hasWitness) { - throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH'); - } - const signature = keyPair.sign(signatureHash); - input.signatures[i] = bscript.signature.encode(signature, hashType); - return true; - }); - if (!signed) - throw new Error('Key pair cannot sign for this input'); - } - __addInputUnsafe(txHash, vout, options) { - if (transaction_1.Transaction.isCoinbaseHash(txHash)) { - throw new Error('coinbase inputs not supported'); - } - const prevTxOut = txHash.toString('hex') + ':' + vout; - if (this.__PREV_TX_SET[prevTxOut] !== undefined) - throw new Error('Duplicate TxOut: ' + prevTxOut); - let input = {}; - // derive what we can from the scriptSig - if (options.script !== undefined) { - input = expandInput(options.script, options.witness || []); - } - // if an input value was given, retain it - if (options.value !== undefined) { - input.value = options.value; - } - // derive what we can from the previous transactions output script - if (!input.prevOutScript && options.prevOutScript) { - let prevOutType; - if (!input.pubkeys && !input.signatures) { - const expanded = expandOutput(options.prevOutScript); - if (expanded.pubkeys) { - input.pubkeys = expanded.pubkeys; - input.signatures = expanded.signatures; - } - prevOutType = expanded.type; - } - input.prevOutScript = options.prevOutScript; - input.prevOutType = prevOutType || classify.output(options.prevOutScript); - } - const vin = this.__TX.addInput(txHash, vout, options.sequence, options.scriptSig); - this.__INPUTS[vin] = input; - this.__PREV_TX_SET[prevTxOut] = true; - return vin; - } - __build(allowIncomplete) { - if (!allowIncomplete) { - if (!this.__TX.ins.length) - throw new Error('Transaction has no inputs'); - if (!this.__TX.outs.length) - throw new Error('Transaction has no outputs'); - } - const tx = this.__TX.clone(); - // create script signatures from inputs - this.__INPUTS.forEach((input, i) => { - if (!input.prevOutType && !allowIncomplete) - throw new Error('Transaction is not complete'); - const result = build(input.prevOutType, input, allowIncomplete); - if (!result) { - if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) - throw new Error('Unknown input type'); - if (!allowIncomplete) - throw new Error('Not enough information'); - return; - } - tx.setInputScript(i, result.input); - tx.setWitness(i, result.witness); - }); - if (!allowIncomplete) { - // do not rely on this, its merely a last resort - if (this.__overMaximumFees(tx.virtualSize())) { - throw new Error('Transaction has absurd fees'); - } - } - return tx; - } - __canModifyInputs() { - return this.__INPUTS.every(input => { - if (!input.signatures) - return true; - return input.signatures.every(signature => { - if (!signature) - return true; - const hashType = signatureHashType(signature); - // if SIGHASH_ANYONECANPAY is set, signatures would not - // be invalidated by more inputs - return (hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY) !== 0; - }); - }); - } - __needsOutputs(signingHashType) { - if (signingHashType === transaction_1.Transaction.SIGHASH_ALL) { - return this.__TX.outs.length === 0; - } - // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs - // .build() will fail, but .buildIncomplete() is OK - return (this.__TX.outs.length === 0 && - this.__INPUTS.some(input => { - if (!input.signatures) - return false; - return input.signatures.some(signature => { - if (!signature) - return false; // no signature, no issue - const hashType = signatureHashType(signature); - if (hashType & transaction_1.Transaction.SIGHASH_NONE) - return false; // SIGHASH_NONE doesn't care about outputs - return true; // SIGHASH_* does care - }); - })); - } - __canModifyOutputs() { - const nInputs = this.__TX.ins.length; - const nOutputs = this.__TX.outs.length; - return this.__INPUTS.every(input => { - if (input.signatures === undefined) - return true; - return input.signatures.every(signature => { - if (!signature) - return true; - const hashType = signatureHashType(signature); - const hashTypeMod = hashType & 0x1f; - if (hashTypeMod === transaction_1.Transaction.SIGHASH_NONE) - return true; - if (hashTypeMod === transaction_1.Transaction.SIGHASH_SINGLE) { - // if SIGHASH_SINGLE is set, and nInputs > nOutputs - // some signatures would be invalidated by the addition - // of more outputs - return nInputs <= nOutputs; - } - return false; - }); - }); - } - __overMaximumFees(bytes) { - // not all inputs will have .value defined - const incoming = this.__INPUTS.reduce((a, x) => a + (x.value >>> 0), 0); - // but all outputs do, and if we have any input value - // we can immediately determine if the outputs are too small - const outgoing = this.__TX.outs.reduce((a, x) => a + x.value, 0); - const fee = incoming - outgoing; - const feeRate = fee / bytes; - return feeRate > this.maximumFeeRate; - } + return false; + }); + }); + } + __overMaximumFees(bytes) { + // not all inputs will have .value defined + const incoming = this.__INPUTS.reduce((a, x) => a + (x.value >>> 0), 0); + // but all outputs do, and if we have any input value + // we can immediately determine if the outputs are too small + const outgoing = this.__TX.outs.reduce((a, x) => a + x.value, 0); + const fee = incoming - outgoing; + const feeRate = fee / bytes; + return feeRate > this.maximumFeeRate; + } } exports.TransactionBuilder = TransactionBuilder; function expandInput(scriptSig, witnessStack, type, scriptPubKey) { - if (scriptSig.length === 0 && witnessStack.length === 0) - return {}; - if (!type) { - let ssType = classify.input(scriptSig, true); - let wsType = classify.witness(witnessStack, true); - if (ssType === SCRIPT_TYPES.NONSTANDARD) - ssType = undefined; - if (wsType === SCRIPT_TYPES.NONSTANDARD) - wsType = undefined; - type = ssType || wsType; + if (scriptSig.length === 0 && witnessStack.length === 0) return {}; + if (!type) { + let ssType = classify.input(scriptSig, true); + let wsType = classify.witness(witnessStack, true); + if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined; + if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined; + type = ssType || wsType; + } + switch (type) { + case SCRIPT_TYPES.P2WPKH: { + const { output, pubkey, signature } = payments.p2wpkh({ + witness: witnessStack, + }); + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2WPKH, + pubkeys: [pubkey], + signatures: [signature], + }; } - switch (type) { - case SCRIPT_TYPES.P2WPKH: { - const { output, pubkey, signature } = payments.p2wpkh({ - witness: witnessStack, - }); - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2WPKH, - pubkeys: [pubkey], - signatures: [signature], - }; - } - case SCRIPT_TYPES.P2PKH: { - const { output, pubkey, signature } = payments.p2pkh({ - input: scriptSig, - }); - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2PKH, - pubkeys: [pubkey], - signatures: [signature], - }; - } - case SCRIPT_TYPES.P2PK: { - const { signature } = payments.p2pk({ input: scriptSig }); - return { - prevOutType: SCRIPT_TYPES.P2PK, - pubkeys: [undefined], - signatures: [signature], - }; - } - case SCRIPT_TYPES.P2MS: { - const { m, pubkeys, signatures } = payments.p2ms({ - input: scriptSig, - output: scriptPubKey, - }, { allowIncomplete: true }); - return { - prevOutType: SCRIPT_TYPES.P2MS, - pubkeys, - signatures, - maxSignatures: m, - }; - } + case SCRIPT_TYPES.P2PKH: { + const { output, pubkey, signature } = payments.p2pkh({ + input: scriptSig, + }); + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2PKH, + pubkeys: [pubkey], + signatures: [signature], + }; } - if (type === SCRIPT_TYPES.P2SH) { - const { output, redeem } = payments.p2sh({ - input: scriptSig, - witness: witnessStack, - }); - const outputType = classify.output(redeem.output); - const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output); - if (!expanded.prevOutType) - return {}; - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2SH, - redeemScript: redeem.output, - redeemScriptType: expanded.prevOutType, - witnessScript: expanded.witnessScript, - witnessScriptType: expanded.witnessScriptType, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - }; + case SCRIPT_TYPES.P2PK: { + const { signature } = payments.p2pk({ input: scriptSig }); + return { + prevOutType: SCRIPT_TYPES.P2PK, + pubkeys: [undefined], + signatures: [signature], + }; } - if (type === SCRIPT_TYPES.P2WSH) { - const { output, redeem } = payments.p2wsh({ - input: scriptSig, - witness: witnessStack, - }); - const outputType = classify.output(redeem.output); - let expanded; - if (outputType === SCRIPT_TYPES.P2WPKH) { - expanded = expandInput(redeem.input, redeem.witness, outputType); - } - else { - expanded = expandInput(bscript.compile(redeem.witness), [], outputType, redeem.output); - } - if (!expanded.prevOutType) - return {}; - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2WSH, - witnessScript: redeem.output, - witnessScriptType: expanded.prevOutType, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - }; + case SCRIPT_TYPES.P2MS: { + const { m, pubkeys, signatures } = payments.p2ms( + { + input: scriptSig, + output: scriptPubKey, + }, + { allowIncomplete: true }, + ); + return { + prevOutType: SCRIPT_TYPES.P2MS, + pubkeys, + signatures, + maxSignatures: m, + }; } + } + if (type === SCRIPT_TYPES.P2SH) { + const { output, redeem } = payments.p2sh({ + input: scriptSig, + witness: witnessStack, + }); + const outputType = classify.output(redeem.output); + const expanded = expandInput( + redeem.input, + redeem.witness, + outputType, + redeem.output, + ); + if (!expanded.prevOutType) return {}; return { - prevOutType: SCRIPT_TYPES.NONSTANDARD, - prevOutScript: scriptSig, + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2SH, + redeemScript: redeem.output, + redeemScriptType: expanded.prevOutType, + witnessScript: expanded.witnessScript, + witnessScriptType: expanded.witnessScriptType, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, }; + } + if (type === SCRIPT_TYPES.P2WSH) { + const { output, redeem } = payments.p2wsh({ + input: scriptSig, + witness: witnessStack, + }); + const outputType = classify.output(redeem.output); + let expanded; + if (outputType === SCRIPT_TYPES.P2WPKH) { + expanded = expandInput(redeem.input, redeem.witness, outputType); + } else { + expanded = expandInput( + bscript.compile(redeem.witness), + [], + outputType, + redeem.output, + ); + } + if (!expanded.prevOutType) return {}; + return { + prevOutScript: output, + prevOutType: SCRIPT_TYPES.P2WSH, + witnessScript: redeem.output, + witnessScriptType: expanded.prevOutType, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, + }; + } + return { + prevOutType: SCRIPT_TYPES.NONSTANDARD, + prevOutScript: scriptSig, + }; } // could be done in expandInput, but requires the original Transaction for hashForSignature function fixMultisigOrder(input, transaction, vin) { - if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) - return; - if (input.pubkeys.length === input.signatures.length) - return; - const unmatched = input.signatures.concat(); - input.signatures = input.pubkeys.map(pubKey => { - const keyPair = ECPair.fromPublicKey(pubKey); - let match; - // check for a signature - unmatched.some((signature, i) => { - // skip if undefined || OP_0 - if (!signature) - return false; - // TODO: avoid O(n) hashForSignature - const parsed = bscript.signature.decode(signature); - const hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType); - // skip if signature does not match pubKey - if (!keyPair.verify(hash, parsed.signature)) - return false; - // remove matched signature from unmatched - unmatched[i] = undefined; - match = signature; - return true; - }); - return match; + if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) + return; + if (input.pubkeys.length === input.signatures.length) return; + const unmatched = input.signatures.concat(); + input.signatures = input.pubkeys.map(pubKey => { + const keyPair = ECPair.fromPublicKey(pubKey); + let match; + // check for a signature + unmatched.some((signature, i) => { + // skip if undefined || OP_0 + if (!signature) return false; + // TODO: avoid O(n) hashForSignature + const parsed = bscript.signature.decode(signature); + const hash = transaction.hashForSignature( + vin, + input.redeemScript, + parsed.hashType, + ); + // skip if signature does not match pubKey + if (!keyPair.verify(hash, parsed.signature)) return false; + // remove matched signature from unmatched + unmatched[i] = undefined; + match = signature; + return true; }); + return match; + }); } function expandOutput(script, ourPubKey) { - typeforce(types.Buffer, script); - const type = classify.output(script); - switch (type) { - case SCRIPT_TYPES.P2PKH: { - if (!ourPubKey) - return { type }; - // does our hash160(pubKey) match the output scripts? - const pkh1 = payments.p2pkh({ output: script }).hash; - const pkh2 = bcrypto.hash160(ourPubKey); - if (!pkh1.equals(pkh2)) - return { type }; - return { - type, - pubkeys: [ourPubKey], - signatures: [undefined], - }; - } - case SCRIPT_TYPES.P2WPKH: { - if (!ourPubKey) - return { type }; - // does our hash160(pubKey) match the output scripts? - const wpkh1 = payments.p2wpkh({ output: script }).hash; - const wpkh2 = bcrypto.hash160(ourPubKey); - if (!wpkh1.equals(wpkh2)) - return { type }; - return { - type, - pubkeys: [ourPubKey], - signatures: [undefined], - }; - } - case SCRIPT_TYPES.P2PK: { - const p2pk = payments.p2pk({ output: script }); - return { - type, - pubkeys: [p2pk.pubkey], - signatures: [undefined], - }; - } - case SCRIPT_TYPES.P2MS: { - const p2ms = payments.p2ms({ output: script }); - return { - type, - pubkeys: p2ms.pubkeys, - signatures: p2ms.pubkeys.map(() => undefined), - maxSignatures: p2ms.m, - }; - } - } - return { type }; -} -function prepareInput(input, ourPubKey, redeemScript, witnessScript) { - if (redeemScript && witnessScript) { - const p2wsh = payments.p2wsh({ - redeem: { output: witnessScript }, - }); - const p2wshAlt = payments.p2wsh({ output: redeemScript }); - const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); - const p2shAlt = payments.p2sh({ redeem: p2wsh }); - // enforces P2SH(P2WSH(...)) - if (!p2wsh.hash.equals(p2wshAlt.hash)) - throw new Error('Witness script inconsistent with prevOutScript'); - if (!p2sh.hash.equals(p2shAlt.hash)) - throw new Error('Redeem script inconsistent with prevOutScript'); - const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); - if (!expanded.pubkeys) - throw new Error(expanded.type + - ' not supported as witnessScript (' + - bscript.toASM(witnessScript) + - ')'); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - const signScript = witnessScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) - throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure'); - return { - redeemScript, - redeemScriptType: SCRIPT_TYPES.P2WSH, - witnessScript, - witnessScriptType: expanded.type, - prevOutType: SCRIPT_TYPES.P2SH, - prevOutScript: p2sh.output, - hasWitness: true, - signScript, - signType: expanded.type, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - if (redeemScript) { - const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); - if (input.prevOutScript) { - let p2shAlt; - try { - p2shAlt = payments.p2sh({ output: input.prevOutScript }); - } - catch (e) { - throw new Error('PrevOutScript must be P2SH'); - } - if (!p2sh.hash.equals(p2shAlt.hash)) - throw new Error('Redeem script inconsistent with prevOutScript'); - } - const expanded = expandOutput(p2sh.redeem.output, ourPubKey); - if (!expanded.pubkeys) - throw new Error(expanded.type + - ' not supported as redeemScript (' + - bscript.toASM(redeemScript) + - ')'); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - let signScript = redeemScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output; - } - return { - redeemScript, - redeemScriptType: expanded.type, - prevOutType: SCRIPT_TYPES.P2SH, - prevOutScript: p2sh.output, - hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, - signScript, - signType: expanded.type, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - if (witnessScript) { - const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }); - if (input.prevOutScript) { - const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }); - if (!p2wsh.hash.equals(p2wshAlt.hash)) - throw new Error('Witness script inconsistent with prevOutScript'); - } - const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); - if (!expanded.pubkeys) - throw new Error(expanded.type + - ' not supported as witnessScript (' + - bscript.toASM(witnessScript) + - ')'); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - const signScript = witnessScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) - throw new Error('P2WSH(P2WPKH) is a consensus failure'); - return { - witnessScript, - witnessScriptType: expanded.type, - prevOutType: SCRIPT_TYPES.P2WSH, - prevOutScript: p2wsh.output, - hasWitness: true, - signScript, - signType: expanded.type, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - if (input.prevOutType && input.prevOutScript) { - // embedded scripts are not possible without extra information - if (input.prevOutType === SCRIPT_TYPES.P2SH) - throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript'); - if (input.prevOutType === SCRIPT_TYPES.P2WSH) - throw new Error('PrevOutScript is ' + input.prevOutType + ', requires witnessScript'); - if (!input.prevOutScript) - throw new Error('PrevOutScript is missing'); - const expanded = expandOutput(input.prevOutScript, ourPubKey); - if (!expanded.pubkeys) - throw new Error(expanded.type + - ' not supported (' + - bscript.toASM(input.prevOutScript) + - ')'); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - let signScript = input.prevOutScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }) - .output; - } - return { - prevOutType: expanded.type, - prevOutScript: input.prevOutScript, - hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, - signScript, - signType: expanded.type, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output; - return { - prevOutType: SCRIPT_TYPES.P2PKH, - prevOutScript, - hasWitness: false, - signScript: prevOutScript, - signType: SCRIPT_TYPES.P2PKH, + typeforce(types.Buffer, script); + const type = classify.output(script); + switch (type) { + case SCRIPT_TYPES.P2PKH: { + if (!ourPubKey) return { type }; + // does our hash160(pubKey) match the output scripts? + const pkh1 = payments.p2pkh({ output: script }).hash; + const pkh2 = bcrypto.hash160(ourPubKey); + if (!pkh1.equals(pkh2)) return { type }; + return { + type, pubkeys: [ourPubKey], signatures: [undefined], + }; + } + case SCRIPT_TYPES.P2WPKH: { + if (!ourPubKey) return { type }; + // does our hash160(pubKey) match the output scripts? + const wpkh1 = payments.p2wpkh({ output: script }).hash; + const wpkh2 = bcrypto.hash160(ourPubKey); + if (!wpkh1.equals(wpkh2)) return { type }; + return { + type, + pubkeys: [ourPubKey], + signatures: [undefined], + }; + } + case SCRIPT_TYPES.P2PK: { + const p2pk = payments.p2pk({ output: script }); + return { + type, + pubkeys: [p2pk.pubkey], + signatures: [undefined], + }; + } + case SCRIPT_TYPES.P2MS: { + const p2ms = payments.p2ms({ output: script }); + return { + type, + pubkeys: p2ms.pubkeys, + signatures: p2ms.pubkeys.map(() => undefined), + maxSignatures: p2ms.m, + }; + } + } + return { type }; +} +function prepareInput(input, ourPubKey, redeemScript, witnessScript) { + if (redeemScript && witnessScript) { + const p2wsh = payments.p2wsh({ + redeem: { output: witnessScript }, + }); + const p2wshAlt = payments.p2wsh({ output: redeemScript }); + const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); + const p2shAlt = payments.p2sh({ redeem: p2wsh }); + // enforces P2SH(P2WSH(...)) + if (!p2wsh.hash.equals(p2wshAlt.hash)) + throw new Error('Witness script inconsistent with prevOutScript'); + if (!p2sh.hash.equals(p2shAlt.hash)) + throw new Error('Redeem script inconsistent with prevOutScript'); + const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); + if (!expanded.pubkeys) + throw new Error( + expanded.type + + ' not supported as witnessScript (' + + bscript.toASM(witnessScript) + + ')', + ); + if (input.signatures && input.signatures.some(x => x !== undefined)) { + expanded.signatures = input.signatures; + } + const signScript = witnessScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) + throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure'); + return { + redeemScript, + redeemScriptType: SCRIPT_TYPES.P2WSH, + witnessScript, + witnessScriptType: expanded.type, + prevOutType: SCRIPT_TYPES.P2SH, + prevOutScript: p2sh.output, + hasWitness: true, + signScript, + signType: expanded.type, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures, }; + } + if (redeemScript) { + const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); + if (input.prevOutScript) { + let p2shAlt; + try { + p2shAlt = payments.p2sh({ output: input.prevOutScript }); + } catch (e) { + throw new Error('PrevOutScript must be P2SH'); + } + if (!p2sh.hash.equals(p2shAlt.hash)) + throw new Error('Redeem script inconsistent with prevOutScript'); + } + const expanded = expandOutput(p2sh.redeem.output, ourPubKey); + if (!expanded.pubkeys) + throw new Error( + expanded.type + + ' not supported as redeemScript (' + + bscript.toASM(redeemScript) + + ')', + ); + if (input.signatures && input.signatures.some(x => x !== undefined)) { + expanded.signatures = input.signatures; + } + let signScript = redeemScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) { + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output; + } + return { + redeemScript, + redeemScriptType: expanded.type, + prevOutType: SCRIPT_TYPES.P2SH, + prevOutScript: p2sh.output, + hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, + signScript, + signType: expanded.type, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures, + }; + } + if (witnessScript) { + const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }); + if (input.prevOutScript) { + const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }); + if (!p2wsh.hash.equals(p2wshAlt.hash)) + throw new Error('Witness script inconsistent with prevOutScript'); + } + const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); + if (!expanded.pubkeys) + throw new Error( + expanded.type + + ' not supported as witnessScript (' + + bscript.toASM(witnessScript) + + ')', + ); + if (input.signatures && input.signatures.some(x => x !== undefined)) { + expanded.signatures = input.signatures; + } + const signScript = witnessScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) + throw new Error('P2WSH(P2WPKH) is a consensus failure'); + return { + witnessScript, + witnessScriptType: expanded.type, + prevOutType: SCRIPT_TYPES.P2WSH, + prevOutScript: p2wsh.output, + hasWitness: true, + signScript, + signType: expanded.type, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures, + }; + } + if (input.prevOutType && input.prevOutScript) { + // embedded scripts are not possible without extra information + if (input.prevOutType === SCRIPT_TYPES.P2SH) + throw new Error( + 'PrevOutScript is ' + input.prevOutType + ', requires redeemScript', + ); + if (input.prevOutType === SCRIPT_TYPES.P2WSH) + throw new Error( + 'PrevOutScript is ' + input.prevOutType + ', requires witnessScript', + ); + if (!input.prevOutScript) throw new Error('PrevOutScript is missing'); + const expanded = expandOutput(input.prevOutScript, ourPubKey); + if (!expanded.pubkeys) + throw new Error( + expanded.type + + ' not supported (' + + bscript.toASM(input.prevOutScript) + + ')', + ); + if (input.signatures && input.signatures.some(x => x !== undefined)) { + expanded.signatures = input.signatures; + } + let signScript = input.prevOutScript; + if (expanded.type === SCRIPT_TYPES.P2WPKH) { + signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output; + } + return { + prevOutType: expanded.type, + prevOutScript: input.prevOutScript, + hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, + signScript, + signType: expanded.type, + pubkeys: expanded.pubkeys, + signatures: expanded.signatures, + maxSignatures: expanded.maxSignatures, + }; + } + const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output; + return { + prevOutType: SCRIPT_TYPES.P2PKH, + prevOutScript, + hasWitness: false, + signScript: prevOutScript, + signType: SCRIPT_TYPES.P2PKH, + pubkeys: [ourPubKey], + signatures: [undefined], + }; } function build(type, input, allowIncomplete) { - const pubkeys = (input.pubkeys || []); - let signatures = (input.signatures || []); - switch (type) { - case SCRIPT_TYPES.P2PKH: { - if (pubkeys.length === 0) - break; - if (signatures.length === 0) - break; - return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] }); - } - case SCRIPT_TYPES.P2WPKH: { - if (pubkeys.length === 0) - break; - if (signatures.length === 0) - break; - return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] }); - } - case SCRIPT_TYPES.P2PK: { - if (pubkeys.length === 0) - break; - if (signatures.length === 0) - break; - return payments.p2pk({ signature: signatures[0] }); - } - case SCRIPT_TYPES.P2MS: { - const m = input.maxSignatures; - if (allowIncomplete) { - signatures = signatures.map(x => x || script_1.OPS.OP_0); - } - else { - signatures = signatures.filter(x => x); - } - // if the transaction is not not complete (complete), or if signatures.length === m, validate - // otherwise, the number of OP_0's may be >= m, so don't validate (boo) - const validate = !allowIncomplete || m === signatures.length; - return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate }); - } - case SCRIPT_TYPES.P2SH: { - const redeem = build(input.redeemScriptType, input, allowIncomplete); - if (!redeem) - return; - return payments.p2sh({ - redeem: { - output: redeem.output || input.redeemScript, - input: redeem.input, - witness: redeem.witness, - }, - }); - } - case SCRIPT_TYPES.P2WSH: { - const redeem = build(input.witnessScriptType, input, allowIncomplete); - if (!redeem) - return; - return payments.p2wsh({ - redeem: { - output: input.witnessScript, - input: redeem.input, - witness: redeem.witness, - }, - }); - } + const pubkeys = input.pubkeys || []; + let signatures = input.signatures || []; + switch (type) { + case SCRIPT_TYPES.P2PKH: { + if (pubkeys.length === 0) break; + if (signatures.length === 0) break; + return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] }); } + case SCRIPT_TYPES.P2WPKH: { + if (pubkeys.length === 0) break; + if (signatures.length === 0) break; + return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] }); + } + case SCRIPT_TYPES.P2PK: { + if (pubkeys.length === 0) break; + if (signatures.length === 0) break; + return payments.p2pk({ signature: signatures[0] }); + } + case SCRIPT_TYPES.P2MS: { + const m = input.maxSignatures; + if (allowIncomplete) { + signatures = signatures.map(x => x || script_1.OPS.OP_0); + } else { + signatures = signatures.filter(x => x); + } + // if the transaction is not not complete (complete), or if signatures.length === m, validate + // otherwise, the number of OP_0's may be >= m, so don't validate (boo) + const validate = !allowIncomplete || m === signatures.length; + return payments.p2ms( + { m, pubkeys, signatures }, + { allowIncomplete, validate }, + ); + } + case SCRIPT_TYPES.P2SH: { + const redeem = build(input.redeemScriptType, input, allowIncomplete); + if (!redeem) return; + return payments.p2sh({ + redeem: { + output: redeem.output || input.redeemScript, + input: redeem.input, + witness: redeem.witness, + }, + }); + } + case SCRIPT_TYPES.P2WSH: { + const redeem = build(input.witnessScriptType, input, allowIncomplete); + if (!redeem) return; + return payments.p2wsh({ + redeem: { + output: input.witnessScript, + input: redeem.input, + witness: redeem.witness, + }, + }); + } + } } function canSign(input) { - return (input.signScript !== undefined && - input.signType !== undefined && - input.pubkeys !== undefined && - input.signatures !== undefined && - input.signatures.length === input.pubkeys.length && - input.pubkeys.length > 0 && - (input.hasWitness === false || input.value !== undefined)); + return ( + input.signScript !== undefined && + input.signType !== undefined && + input.pubkeys !== undefined && + input.signatures !== undefined && + input.signatures.length === input.pubkeys.length && + input.pubkeys.length > 0 && + (input.hasWitness === false || input.value !== undefined) + ); } function signatureHashType(buffer) { - return buffer.readUInt8(buffer.length - 1); + return buffer.readUInt8(buffer.length - 1); } diff --git a/src/types.js b/src/types.js index dbe7ca5..be95266 100644 --- a/src/types.js +++ b/src/types.js @@ -1,35 +1,35 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); const typeforce = require('typeforce'); const UINT31_MAX = Math.pow(2, 31) - 1; function UInt31(value) { - return typeforce.UInt32(value) && value <= UINT31_MAX; + return typeforce.UInt32(value) && value <= UINT31_MAX; } exports.UInt31 = UInt31; function BIP32Path(value) { - return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); + return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); } exports.BIP32Path = BIP32Path; BIP32Path.toJSON = () => { - return 'BIP32 derivation path'; + return 'BIP32 derivation path'; }; const SATOSHI_MAX = 21 * 1e14; function Satoshi(value) { - return typeforce.UInt53(value) && value <= SATOSHI_MAX; + return typeforce.UInt53(value) && value <= SATOSHI_MAX; } exports.Satoshi = Satoshi; // external dependent types exports.ECPoint = typeforce.quacksLike('Point'); // exposed, external API exports.Network = typeforce.compile({ - messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), - bip32: { - public: typeforce.UInt32, - private: typeforce.UInt32, - }, - pubKeyHash: typeforce.UInt8, - scriptHash: typeforce.UInt8, - wif: typeforce.UInt8, + messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), + bip32: { + public: typeforce.UInt32, + private: typeforce.UInt32, + }, + pubKeyHash: typeforce.UInt8, + scriptHash: typeforce.UInt8, + wif: typeforce.UInt8, }); exports.Buffer256bit = typeforce.BufferN(32); exports.Hash160bit = typeforce.BufferN(20); From 67aa87e28df35c414b0650d8a0b79998e58a64a7 Mon Sep 17 00:00:00 2001 From: jolestar <jolestar@gmail.com> Date: Tue, 23 Apr 2019 13:55:51 +0800 Subject: [PATCH 284/568] fix bug:ECPair.verify should return boolean fECPair.verify should return boolean, and js implements indeed return boolean. --- types/ecpair.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index a5ae716..2cc2c27 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -12,7 +12,7 @@ export interface ECPairInterface { publicKey?: Buffer; toWIF(): string; sign(hash: Buffer): Buffer; - verify(hash: Buffer, signature: Buffer): Buffer; + verify(hash: Buffer, signature: Buffer): boolean; getPublicKey?(): Buffer; } declare class ECPair implements ECPairInterface { @@ -25,7 +25,7 @@ declare class ECPair implements ECPairInterface { readonly publicKey: Buffer | undefined; toWIF(): string; sign(hash: Buffer): Buffer; - verify(hash: Buffer, signature: Buffer): Buffer; + verify(hash: Buffer, signature: Buffer): boolean; } declare function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair; declare function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair; From 00ca4c2ec7a112ca5f036409d98cfedaa24ee0cf Mon Sep 17 00:00:00 2001 From: Jonathan Underwood <junderwood@bitcoinbank.co.jp> Date: Tue, 23 Apr 2019 15:10:01 +0900 Subject: [PATCH 285/568] Fix ts files from Buffer to boolean --- ts_src/ecpair.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index 3941afa..5c31e7f 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -26,7 +26,7 @@ export interface ECPairInterface { publicKey?: Buffer; toWIF(): string; sign(hash: Buffer): Buffer; - verify(hash: Buffer, signature: Buffer): Buffer; + verify(hash: Buffer, signature: Buffer): boolean; getPublicKey?(): Buffer; } @@ -66,7 +66,7 @@ class ECPair implements ECPairInterface { return ecc.sign(hash, this.__D); } - verify(hash: Buffer, signature: Buffer): Buffer { + verify(hash: Buffer, signature: Buffer): boolean { return ecc.verify(hash, this.publicKey, signature); } } From 56c876e979a24fbdaa6c90cad3bafe302f9f9f24 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 25 Apr 2019 19:07:46 +0900 Subject: [PATCH 286/568] 5.0.3 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f56a619..b69c70b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.2", + "version": "5.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 591e98a..2387bed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.2", + "version": "5.0.3", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 68339fda4de72cc40fa8db6602595b4e1a7e8577 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Mon, 13 May 2019 16:35:02 +0700 Subject: [PATCH 287/568] Remove source files before rebuilding --- package-lock.json | 25 +++++++++++++++++++++++++ package.json | 4 +++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index b69c70b..2b0f37e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1845,6 +1845,31 @@ "path-parse": "^1.0.5" } }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", diff --git a/package.json b/package.json index 2387bed..b667ba0 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "bitcoinjs" ], "scripts": { - "build": "tsc -p ./tsconfig.json", + "clean": "rimraf src", + "build": "npm run clean && tsc -p ./tsconfig.json", "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", @@ -71,6 +72,7 @@ "nyc": "^13.3.0", "prettier": "1.16.4", "proxyquire": "^2.0.1", + "rimraf": "^2.6.3", "tslint": "5.13.1", "typescript": "3.2.2" }, From 5ef135f9e429925c51461d11c3000ce6d1275793 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Mon, 13 May 2019 16:39:08 +0700 Subject: [PATCH 288/568] Ensure build scripts are in alphabetical order --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b667ba0..a26330d 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "bitcoinjs" ], "scripts": { - "clean": "rimraf src", "build": "npm run clean && tsc -p ./tsconfig.json", + "clean": "rimraf src", "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", From 1c75c0203809ced5ace402ca211c2c12bb9db4bc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 16 May 2019 16:29:23 +0900 Subject: [PATCH 289/568] Fix publicKey type on ECPairInterface --- ts_src/ecpair.ts | 11 ++++++----- types/ecpair.d.ts | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index 5c31e7f..18c0c9e 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -22,8 +22,8 @@ interface ECPairOptions { export interface ECPairInterface { compressed: boolean; network: Network; + publicKey: Buffer; privateKey?: Buffer; - publicKey?: Buffer; toWIF(): string; sign(hash: Buffer): Buffer; verify(hash: Buffer, signature: Buffer): boolean; @@ -51,8 +51,9 @@ class ECPair implements ECPairInterface { return this.__D; } - get publicKey(): Buffer | undefined { - if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__D, this.compressed); + get publicKey(): Buffer { + if (!this.__Q) + this.__Q = ecc.pointFromScalar(this.__D, this.compressed) as Buffer; return this.__Q; } @@ -77,13 +78,13 @@ function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair { throw new TypeError('Private key not in range [1, n)'); typeforce(isOptions, options); - return new ECPair(buffer, undefined, options as ECPairOptions); + return new ECPair(buffer, undefined, options); } function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair { typeforce(ecc.isPoint, buffer); typeforce(isOptions, options); - return new ECPair(undefined, buffer, options as ECPairOptions); + return new ECPair(undefined, buffer, options); } function fromWIF(wifString: string, network?: Network | Network[]): ECPair { diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index 2cc2c27..896025b 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -8,8 +8,8 @@ interface ECPairOptions { export interface ECPairInterface { compressed: boolean; network: Network; + publicKey: Buffer; privateKey?: Buffer; - publicKey?: Buffer; toWIF(): string; sign(hash: Buffer): Buffer; verify(hash: Buffer, signature: Buffer): boolean; @@ -22,7 +22,7 @@ declare class ECPair implements ECPairInterface { network: Network; constructor(__D?: Buffer | undefined, __Q?: Buffer | undefined, options?: ECPairOptions); readonly privateKey: Buffer | undefined; - readonly publicKey: Buffer | undefined; + readonly publicKey: Buffer; toWIF(): string; sign(hash: Buffer): Buffer; verify(hash: Buffer, signature: Buffer): boolean; From 5a381ba582856a348d8182508a15a2584894cefa Mon Sep 17 00:00:00 2001 From: Agent of User <git@agentofuser.com> Date: Fri, 17 May 2019 08:02:53 -0400 Subject: [PATCH 290/568] Bump tiny-secp256k1 to version supporting node 12 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b0f37e..f5bb788 100644 --- a/package-lock.json +++ b/package-lock.json @@ -731,9 +731,9 @@ "dev": true }, "nan": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==" + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" }, "nyc": { "version": "13.3.0", @@ -1936,15 +1936,15 @@ } }, "tiny-secp256k1": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.0.1.tgz", - "integrity": "sha512-Wz2kMPWtCI5XBftFeF3bUL8uz2+VlasniKwOkRPjvL7h1QVd9rbhrve/HWUu747kJKzVf1XHonzcdM4Ut8fvww==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.1.tgz", + "integrity": "sha512-jA9WalQuhKun1svJrAVi9Vu8aUWKMfR7nMV903kHjrHTTY/IFa0petSq+Jk/Mv447dGD9LC8fGsmGRubBbcNng==", "requires": { "bindings": "^1.3.0", "bn.js": "^4.11.8", "create-hmac": "^1.1.7", "elliptic": "^6.4.0", - "nan": "^2.10.0" + "nan": "^2.13.2" } }, "to-fast-properties": { diff --git a/package.json b/package.json index a26330d..1f6f83a 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "merkle-lib": "^2.0.10", "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", - "tiny-secp256k1": "^1.0.0", + "tiny-secp256k1": "^1.1.1", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" From 4d9c1b81bdbe7fbf04767117ac315cc1533c7f66 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 21 May 2019 15:13:38 +0900 Subject: [PATCH 291/568] Update packages npm audit --- package-lock.json | 2342 +++++++++++++++++++++------------------------ package.json | 6 +- 2 files changed, 1073 insertions(+), 1275 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a1d1d3..6e84754 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,12 +14,12 @@ } }, "@babel/generator": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.4.tgz", - "integrity": "sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", + "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", "dev": true, "requires": { - "@babel/types": "^7.3.4", + "@babel/types": "^7.4.4", "jsesc": "^2.5.1", "lodash": "^4.17.11", "source-map": "^0.5.0", @@ -47,12 +47,12 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "^7.4.4" } }, "@babel/highlight": { @@ -64,45 +64,37 @@ "chalk": "^2.0.0", "esutils": "^2.0.2", "js-tokens": "^4.0.0" - }, - "dependencies": { - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - } } }, "@babel/parser": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.4.tgz", - "integrity": "sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", + "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", "dev": true }, "@babel/template": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", - "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.2.2", - "@babel/types": "^7.2.2" + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" } }, "@babel/traverse": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.3.4.tgz", - "integrity": "sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", + "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.3.4", + "@babel/generator": "^7.4.4", "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.3.4", - "@babel/types": "^7.3.4", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.11" @@ -126,9 +118,9 @@ } }, "@babel/types": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.4.tgz", - "integrity": "sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", + "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -142,15 +134,33 @@ "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" }, "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "dev": true, + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, "argparse": { @@ -162,38 +172,6 @@ "sprintf-js": "~1.0.2" } }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -222,15 +200,15 @@ } }, "bip32": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.0.tgz", - "integrity": "sha512-dYuSVcH43KXLsmiOlud62m5ZP3KG8UBUAbP+wMq8VKuhPvxOOM8VOXRs6ppcJB4iUJtYjmg7VuzTlr8waMusfg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.3.tgz", + "integrity": "sha512-Tg4dHUXiYBkJyCQq4g++C2PqKcZRveVqy7cKxyl88Uai7MmmknFGaF88odYrXcXk5EMyrlXLuAMC3yEiLxRnNA==", "requires": { "@types/node": "10.12.18", "bs58check": "^2.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "tiny-secp256k1": "^1.0.0", + "tiny-secp256k1": "^1.1.0", "typeforce": "^1.11.5", "wif": "^2.0.6" } @@ -323,6 +301,24 @@ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, + "caching-transform": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", + "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "dev": true, + "requires": { + "hasha": "^3.0.0", + "make-dir": "^2.0.0", + "package-hash": "^3.0.0", + "write-file-atomic": "^2.4.2" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -332,17 +328,6 @@ "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - } } }, "cipher-base": { @@ -354,6 +339,17 @@ "safe-buffer": "^5.0.1" } }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -375,12 +371,40 @@ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cp-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", + "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "make-dir": "^2.0.0", + "nested-error-stacks": "^2.0.0", + "pify": "^4.0.1", + "safe-buffer": "^5.0.1" + } + }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -406,6 +430,16 @@ "sha.js": "^2.4.8" } }, + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -415,6 +449,21 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "dev": true, + "requires": { + "strip-bom": "^3.0.0" + } + }, "dhttp": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dhttp/-/dhttp-3.0.3.tgz", @@ -444,6 +493,36 @@ "minimalistic-crypto-utils": "^1.0.0" } }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -462,6 +541,36 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -477,12 +586,57 @@ "merge-descriptors": "~1.0.0" } }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "foreground-child": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "dev": true, + "requires": { + "cross-spawn": "^4", + "signal-exit": "^3.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -498,9 +652,15 @@ } }, "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", "dev": true }, "growl": { @@ -509,13 +669,24 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "handlebars": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "has-flag": { @@ -542,6 +713,15 @@ "minimalistic-assert": "^1.0.1" } }, + "hasha": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", + "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "dev": true, + "requires": { + "is-stream": "^1.0.1" + } + }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -564,6 +744,18 @@ "integrity": "sha512-j1jog3tDfhpWlqbVbh29qc7FG7w+NT4ed+QQFGqvww83+50AzzretB7wykZGOe28mBdvCYH3GdHaVWJQ2lJ/4w==", "dev": true }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -579,43 +771,157 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, "is-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", "dev": true }, - "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, - "istanbul-lib-instrument": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", - "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", + "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", "dev": true, "requires": { - "@babel/generator": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "istanbul-lib-coverage": "^2.0.3", - "semver": "^5.5.0" + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", + "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "dev": true, + "requires": { + "handlebars": "^4.1.2" } }, "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.2.tgz", - "integrity": "sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -628,12 +934,92 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -644,17 +1030,51 @@ "safe-buffer": "^5.1.2" } }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "merkle-lib": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", "integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY=" }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, "minimaldata": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/minimaldata/-/minimaldata-1.0.2.tgz", @@ -735,296 +1155,93 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nested-error-stacks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", + "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, "nyc": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-13.3.0.tgz", - "integrity": "sha512-P+FwIuro2aFG6B0Esd9ZDWUd51uZrAEoGutqZxzrVmYl3qSfkLgcQpBPBjtDFsUQLFY1dvTQJPOyeqr8S9GF8w==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", + "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", "dev": true, "requires": { "archy": "^1.0.0", - "arrify": "^1.0.1", - "caching-transform": "^3.0.1", + "caching-transform": "^3.0.2", "convert-source-map": "^1.6.0", - "find-cache-dir": "^2.0.0", + "cp-file": "^6.2.0", + "find-cache-dir": "^2.1.0", "find-up": "^3.0.0", "foreground-child": "^1.5.6", "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.3", - "istanbul-lib-hook": "^2.0.3", - "istanbul-lib-instrument": "^3.1.0", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.2", - "istanbul-reports": "^2.1.1", - "make-dir": "^1.3.0", + "istanbul-lib-coverage": "^2.0.5", + "istanbul-lib-hook": "^2.0.7", + "istanbul-lib-instrument": "^3.3.0", + "istanbul-lib-report": "^2.0.8", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^2.2.4", + "js-yaml": "^3.13.1", + "make-dir": "^2.1.0", "merge-source-map": "^1.1.0", "resolve-from": "^4.0.0", "rimraf": "^2.6.3", "signal-exit": "^3.0.2", "spawn-wrap": "^1.4.2", - "test-exclude": "^5.1.0", + "test-exclude": "^5.2.3", "uuid": "^3.3.2", - "yargs": "^12.0.5", - "yargs-parser": "^11.1.1" + "yargs": "^13.2.2", + "yargs-parser": "^13.0.0" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "append-transform": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "async": { - "version": "2.6.2", - "resolved": false, - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": false, - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "caching-transform": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha512-Y1KTLNwSPd4ljsDrFOtyXVmm7Gnk42yQitNq43AhE+cwUR/e4T+rmOHs1IPtzBg8066GBJfTOj1rQYFSWSsH2g==", - "dev": true, - "requires": { - "hasha": "^3.0.0", - "make-dir": "^1.3.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.3.0" - } - }, - "camelcase": { - "version": "5.0.0", - "resolved": false, - "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "commander": { - "version": "2.17.1", - "resolved": false, - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", - "dev": true, - "optional": true - }, - "commondir": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": false, - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "convert-source-map": { - "version": "1.6.0", - "resolved": false, - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cross-spawn": { - "version": "4.0.2", - "resolved": false, - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "debug": { - "version": "4.1.1", - "resolved": false, - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - } - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": false, - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": false, - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": false, - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": false, - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } - }, - "find-cache-dir": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^3.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "foreground-child": { - "version": "1.5.6", - "resolved": false, - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", - "dev": true, - "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "glob": { - "version": "7.1.3", - "resolved": false, - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1034,875 +1251,6 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": false, - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "handlebars": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==", - "dev": true, - "requires": { - "async": "^2.5.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": false, - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "hasha": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", - "dev": true, - "requires": { - "is-stream": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": false, - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": false, - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": false, - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": false, - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": false, - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "2.0.3", - "resolved": false, - "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-report": { - "version": "2.0.4", - "resolved": false, - "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "supports-color": "^6.0.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": false, - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "rimraf": "^2.6.2", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": false, - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", - "dev": true, - "requires": { - "handlebars": "^4.1.0" - } - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": false, - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": false, - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": false, - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": false, - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": false, - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "mem": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^1.0.0", - "p-is-promise": "^2.0.0" - } - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": false, - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": false, - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.10", - "resolved": false, - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": false, - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": false, - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "ms": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": false, - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": false, - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": false, - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": false, - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": false, - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": false, - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", - "dev": true - }, - "p-limit": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", - "dev": true - }, - "package-hash": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "resolve": { - "version": "1.10.0", - "resolved": false, - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "rimraf": { - "version": "2.6.3", - "resolved": false, - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": false, - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "semver": { - "version": "5.6.0", - "resolved": false, - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "spawn-wrap": { - "version": "1.4.2", - "resolved": false, - "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", - "dev": true, - "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" - } - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": false, - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": false, - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.3", - "resolved": false, - "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "test-exclude": { - "version": "5.1.0", - "resolved": false, - "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^1.0.1" - } - }, - "uglify-js": { - "version": "3.4.9", - "resolved": false, - "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", - "dev": true, - "optional": true, - "requires": { - "commander": "~2.17.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": false, - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "uuid": { - "version": "3.3.2", - "resolved": false, - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": false, - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": false, - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wordwrap": { - "version": "0.0.3", - "resolved": false, - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write-file-atomic": { - "version": "2.4.2", - "resolved": false, - "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": false, - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": false, - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": false, - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, @@ -1915,18 +1263,138 @@ "wrappy": "1" } }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", + "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^3.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "pbkdf2": { "version": "3.0.17", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", @@ -1940,6 +1408,21 @@ "sha.js": "^2.4.8" } }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, "prettier": { "version": "1.16.4", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", @@ -1957,6 +1440,22 @@ "resolve": "~1.8.1" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "pushdata-bitcoin": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz", @@ -1973,6 +1472,48 @@ "safe-buffer": "^5.1.0" } }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "resolve": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", @@ -1982,6 +1523,12 @@ "path-parse": "^1.0.5" } }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -2022,9 +1569,15 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "sha.js": { @@ -2036,12 +1589,79 @@ "safe-buffer": "^5.0.1" } }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "spawn-wrap": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", + "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", + "dev": true, + "requires": { + "foreground-child": "^1.5.6", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.2", + "which": "^1.3.0" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -2054,15 +1674,38 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", @@ -2072,6 +1715,34 @@ "has-flag": "^3.0.0" } }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "tiny-secp256k1": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.1.tgz", @@ -2103,24 +1774,24 @@ "dev": true }, "tslint": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.13.1.tgz", - "integrity": "sha512-fplQqb2miLbcPhyHoMV4FU9PtNRbgmm/zI5d3SZwwmJQM6V0eodju+hplpyfhLWpmwrDNfNYU57uYRb8s0zZoQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.16.0.tgz", + "integrity": "sha512-UxG2yNxJ5pgGwmMzPMYh/CCnCnh0HfPgtlVRDs1ykZklufFBL1ZoTlWFRz2NQjcoEiDoRp+JyT0lhBbbH/obyA==", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", + "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", "chalk": "^2.3.0", "commander": "^2.12.1", "diff": "^3.2.0", "glob": "^7.1.1", - "js-yaml": "^3.7.0", + "js-yaml": "^3.13.0", "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "resolve": "^1.3.2", "semver": "^5.3.0", "tslib": "^1.8.0", - "tsutils": "^2.27.2" + "tsutils": "^2.29.0" } }, "tsutils": { @@ -2143,12 +1814,55 @@ "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", "dev": true }, + "uglify-js": { + "version": "3.5.14", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.14.tgz", + "integrity": "sha512-dgyjIw8KFK6AyVl5vm2tEqPewv5TKGEiiVFLI1LbF+oHua/Njd8tZk3lIbF1AWU1rNdEg7scaceADb4zqCcWXg==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, "unorm": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz", "integrity": "sha512-sMfSWoiRaXXeDZSXC+YRZ23H4xchQpwxjpw1tmfR+kgbBCaOgln4NI0LXejJIhnBuKINrB3WRn+ZI8IWssirVw==", "dev": true }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "varuint-bitcoin": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz", @@ -2157,6 +1871,21 @@ "safe-buffer": "^5.1.1" } }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, "wif": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", @@ -2165,11 +1894,80 @@ "bs58check": "<3.0.0" } }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true + }, + "write-file-atomic": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", + "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + } + }, + "yargs-parser": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.0.tgz", + "integrity": "sha512-Yq+32PrijHRri0vVKQEm+ys8mbqWjLiwQkMFNXEENutzLPP0bE4Lcd4iA3OQY5HF+GD3xXxf0MEHb8E4/SA3AA==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } } diff --git a/package.json b/package.json index 1f6f83a..5c1d3d2 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip32": "^2.0.0", + "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", "bs58check": "^2.0.0", @@ -69,11 +69,11 @@ "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^5.2.0", - "nyc": "^13.3.0", + "nyc": "^14.1.1", "prettier": "1.16.4", "proxyquire": "^2.0.1", "rimraf": "^2.6.3", - "tslint": "5.13.1", + "tslint": "^5.16.0", "typescript": "3.2.2" }, "license": "MIT" From 04b1f50a2f9c53716b942e43ec4e3160e81db539 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 21 May 2019 15:23:18 +0900 Subject: [PATCH 292/568] Add js format check to CI --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98429f5..b24a248 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "coverage": "npm run build && npm run nobuild:coverage", "format": "npm run prettier -- --write", "formatjs": "npm run prettierjs -- --write > /dev/null 2>&1", - "format:ci": "npm run prettier -- --check", + "format:ci": "npm run prettier -- --check && npm run prettierjs -- --check", "gitdiff:ci": "npm run build && git diff --exit-code", "integration": "npm run build && npm run nobuild:integration", "lint": "tslint -p tsconfig.json -c tslint.json", From 267b0c5d00603f6ce6675b7b3d48889a8c1de237 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 21 May 2019 16:26:00 +0900 Subject: [PATCH 293/568] Easier var names --- test/integration/csv.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/integration/csv.js b/test/integration/csv.js index 4cd54ab..5900c74 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -19,17 +19,17 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { const hashType = bitcoin.Transaction.SIGHASH_ALL // IF MTP (from when confirmed) > seconds, aQ can redeem - function csvCheckSigOutput (aQ, bQ, sequence) { + function csvCheckSigOutput (_alice, _bob, sequence) { return bitcoin.script.fromASM(` OP_IF ${bitcoin.script.number.encode(sequence).toString('hex')} OP_CHECKSEQUENCEVERIFY OP_DROP OP_ELSE - ${bQ.publicKey.toString('hex')} + ${_bob.publicKey.toString('hex')} OP_CHECKSIGVERIFY OP_ENDIF - ${aQ.publicKey.toString('hex')} + ${_alice.publicKey.toString('hex')} OP_CHECKSIG `.trim().replace(/\s+/g, ' ')) } @@ -40,7 +40,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example // Note: bitcoinjs-lib will not offer specific support for problems with // advanced script usages such as below. Use at your own risk. - function complexCsvOutput (aQ, bQ, cQ, dQ, sequence1, sequence2) { + function complexCsvOutput (_alice, _bob, _charles, _dave, sequence1, sequence2) { return bitcoin.script.fromASM(` OP_IF OP_IF @@ -49,20 +49,20 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { ${bitcoin.script.number.encode(sequence1).toString('hex')} OP_CHECKSEQUENCEVERIFY OP_DROP - ${aQ.publicKey.toString('hex')} + ${_alice.publicKey.toString('hex')} OP_CHECKSIGVERIFY OP_1 OP_ENDIF - ${bQ.publicKey.toString('hex')} - ${cQ.publicKey.toString('hex')} - ${dQ.publicKey.toString('hex')} + ${_bob.publicKey.toString('hex')} + ${_charles.publicKey.toString('hex')} + ${_dave.publicKey.toString('hex')} OP_3 OP_CHECKMULTISIG OP_ELSE ${bitcoin.script.number.encode(sequence2).toString('hex')} OP_CHECKSEQUENCEVERIFY OP_DROP - ${aQ.publicKey.toString('hex')} + ${_alice.publicKey.toString('hex')} OP_CHECKSIG OP_ENDIF `.trim().replace(/\s+/g, ' ')) From 3145d3f9ec199b9f2ce2aee68fccea7ff9419807 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 21 May 2019 16:27:42 +0900 Subject: [PATCH 294/568] Fix comments --- test/integration/csv.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/csv.js b/test/integration/csv.js index 5900c74..f213c6c 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -18,7 +18,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { const hashType = bitcoin.Transaction.SIGHASH_ALL - // IF MTP (from when confirmed) > seconds, aQ can redeem + // IF MTP (from when confirmed) > seconds, _alice can redeem function csvCheckSigOutput (_alice, _bob, sequence) { return bitcoin.script.fromASM(` OP_IF @@ -34,9 +34,9 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { `.trim().replace(/\s+/g, ' ')) } - // 2 of 3 multisig of bQ, cQ, dQ, - // but after sequence1 time, aQ can allow the multisig to become 1 of 3. - // but after sequence2 time, aQ can sign for the output all by themself. + // 2 of 3 multisig of _bob, _charles, _dave, + // but after sequence1 time, _alice can allow the multisig to become 1 of 3. + // but after sequence2 time, _alice can sign for the output all by themself. // Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example // Note: bitcoinjs-lib will not offer specific support for problems with // advanced script usages such as below. Use at your own risk. From 0b37eaa19b8b294677d11bacadb2636bd1bade7a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 21 May 2019 16:40:00 +0900 Subject: [PATCH 295/568] Update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd856e1..0a323b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 5.0.4 +__added__ +- low R value support for ECPair, bip32, and TransactionBuilder (default off) via `txb.setLowR()` (#1385) + +__fixed__ +- Fixed Various TypeScript types that have been pushed out since v5.0.0 (#1388) + # 5.0.0 __added__ - TypeScript support (#1319) From dfd25045efa34f708608e88983e312f2fd710d23 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 21 May 2019 16:41:47 +0900 Subject: [PATCH 296/568] Fix rimraf to delete types folder as well --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 126c135..cbb900c 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ ], "scripts": { "build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs", - "clean": "rimraf src", + "clean": "rimraf src types", "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", From 119e8397f9748a53e1f9847a4b58631b398fc2ee Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 21 May 2019 16:45:18 +0900 Subject: [PATCH 297/568] 5.0.4 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6e84754..5c9a042 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.3", + "version": "5.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 126c135..e0118cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.3", + "version": "5.0.4", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From c6c59c7c68bb353e8e4674daf78a674448bd88ca Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 23 May 2019 07:22:21 +0900 Subject: [PATCH 298/568] Expose more interfaces to top level --- ts_src/index.ts | 3 ++- types/index.d.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ts_src/index.ts b/ts_src/index.ts index 4e76c96..1068839 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -14,7 +14,8 @@ export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; +export { ECPairInterface } from './ecpair'; export { Network } from './networks'; -export { Payment, PaymentOpts } from './payments'; +export { Payment, PaymentOpts, Stack, StackElement } from './payments'; export { OpCode } from './script'; export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/types/index.d.ts b/types/index.d.ts index d21454a..28046df 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -11,7 +11,8 @@ export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; +export { ECPairInterface } from './ecpair'; export { Network } from './networks'; -export { Payment, PaymentOpts } from './payments'; +export { Payment, PaymentOpts, Stack, StackElement } from './payments'; export { OpCode } from './script'; export { Input as TxInput, Output as TxOutput } from './transaction'; From 73b6f5585026bc3721e87fd67e0125fe1998256e Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 23 May 2019 07:23:16 +0900 Subject: [PATCH 299/568] 5.0.5 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5c9a042..d769ef5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.4", + "version": "5.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 59af841..b8ec5f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.4", + "version": "5.0.5", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 17f5f35569f0311789da6cd3ef95e9d3a2759ac2 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 12 Jun 2019 19:33:18 +0900 Subject: [PATCH 300/568] Migrate to stricter type checks during sign --- package.json | 2 +- src/transaction_builder.js | 112 ++++++++++++++++++++++++++++++- ts_src/transaction_builder.ts | 116 ++++++++++++++++++++++++++++++++- types/transaction_builder.d.ts | 12 +++- 4 files changed, 238 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b8ec5f3..7882308 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "lint": "tslint -p tsconfig.json -c tslint.json", "nobuild:coverage-report": "nyc report --reporter=lcov", "nobuild:coverage-html": "nyc report --reporter=html", - "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", + "nobuild:coverage": "nyc --check-coverage --branches 85 --functions 90 --lines 90 mocha", "nobuild:integration": "mocha --timeout 50000 test/integration/", "nobuild:unit": "mocha", "prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore", diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 9abf242..73acd3a 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -13,6 +13,20 @@ const transaction_1 = require('./transaction'); const types = require('./types'); const typeforce = require('typeforce'); const SCRIPT_TYPES = classify.types; +const PREVOUT_TYPES = new Set([ + // Raw + 'p2pkh', + 'p2pk', + 'p2wpkh', + 'p2ms', + // P2SH wrapped + 'p2sh-p2wpkh', + 'p2sh-p2ms', + // P2WSH wrapped + 'p2wsh-p2ms', + // P2SH-P2WSH wrapper + 'p2sh-p2wsh-p2ms', +]); function txIsString(tx) { return typeof tx === 'string' || tx instanceof String; } @@ -118,7 +132,103 @@ class TransactionBuilder { buildIncomplete() { return this.__build(true); } - sign(vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) { + sign( + signParams, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, + ) { + let vin; + if (typeof signParams === 'number') { + console.warn( + 'DEPRECATED: TransactionBuilder sign method arguments ' + + 'will change in v6, please use the TxbSignArg interface', + ); + vin = signParams; + } else if (typeof signParams === 'object') { + if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { + throw new TypeError( + `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, + ); + } + typeforce(typeforce.tuple(typeforce.Number, typeforce.Object), [ + signParams.vin, + signParams.keyPair, + ]); + vin = signParams.vin; + keyPair = signParams.keyPair; + const prevOutType = (this.__INPUTS[vin] || []).prevOutType; + switch (signParams.prevOutScriptType) { + case 'p2pkh': + if (prevOutType !== 'pubkeyhash') { + throw new TypeError(`input #${vin} is not of type p2pkh`); + } + break; + case 'p2pk': + if (prevOutType !== 'pubkey') { + throw new TypeError(`input #${vin} is not of type p2pk`); + } + break; + case 'p2wpkh': + if (prevOutType !== 'witnesspubkeyhash') { + throw new TypeError(`input #${vin} is not of type p2wpkh`); + } + typeforce(typeforce.Buffer, signParams.witnessScript); + typeforce(typeforce.Satoshi, signParams.witnessValue); + witnessScript = signParams.witnessScript; + witnessValue = signParams.witnessValue; + break; + case 'p2ms': + if (prevOutType !== 'multisig') { + throw new TypeError(`input #${vin} is not of type p2ms`); + } + break; + case 'p2sh-p2wpkh': + if (prevOutType !== 'scripthash') { + throw new TypeError(`input #${vin} is not of type p2sh-p2wpkh`); + } + typeforce(typeforce.Buffer, signParams.witnessScript); + typeforce(typeforce.Buffer, signParams.redeemScript); + typeforce(typeforce.Satoshi, signParams.witnessValue); + witnessScript = signParams.witnessScript; + redeemScript = signParams.redeemScript; + witnessValue = signParams.witnessValue; + break; + case 'p2sh-p2ms': + if (prevOutType !== 'scripthash') { + throw new TypeError(`input #${vin} is not of type p2sh-p2ms`); + } + typeforce(typeforce.Buffer, signParams.redeemScript); + redeemScript = signParams.redeemScript; + break; + case 'p2wsh-p2ms': + if (prevOutType !== 'witnessscripthash') { + throw new TypeError(`input #${vin} is not of type p2wsh-p2ms`); + } + typeforce(typeforce.Buffer, signParams.witnessScript); + typeforce(typeforce.Satoshi, signParams.witnessValue); + witnessScript = signParams.witnessScript; + witnessValue = signParams.witnessValue; + break; + case 'p2sh-p2wsh-p2ms': + if (prevOutType !== 'scripthash') { + throw new TypeError(`input #${vin} is not of type p2sh-p2wsh-p2ms`); + } + typeforce(typeforce.Buffer, signParams.witnessScript); + typeforce(typeforce.Buffer, signParams.redeemScript); + typeforce(typeforce.Satoshi, signParams.witnessValue); + witnessScript = signParams.witnessScript; + redeemScript = signParams.redeemScript; + witnessValue = signParams.witnessValue; + break; + } + } else { + throw new TypeError( + 'TransactionBuilder sign first arg must be TxbSignArg or number', + ); + } // TODO: remove keyPair.network matching in 4.0.0 if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network'); diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 0665719..b4d794d 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -16,6 +16,21 @@ const typeforce = require('typeforce'); const SCRIPT_TYPES = classify.types; +const PREVOUT_TYPES: Set<string> = new Set([ + // Raw + 'p2pkh', + 'p2pk', + 'p2wpkh', + 'p2ms', + // P2SH wrapped + 'p2sh-p2wpkh', + 'p2sh-p2ms', + // P2WSH wrapped + 'p2wsh-p2ms', + // P2SH-P2WSH wrapper + 'p2sh-p2wsh-p2ms', +]); + type MaybeBuffer = Buffer | undefined; type TxbSignatures = Buffer[] | MaybeBuffer[]; type TxbPubkeys = MaybeBuffer[]; @@ -50,6 +65,16 @@ interface TxbOutput { maxSignatures?: number; } +interface TxbSignArg { + prevOutScriptType: string; + vin: number; + keyPair: ECPairInterface; + redeemScript?: Buffer; + hashType?: number; + witnessValue?: number; + witnessScript?: Buffer; +} + function txIsString(tx: Buffer | string | Transaction): tx is string { return typeof tx === 'string' || tx instanceof String; } @@ -197,13 +222,102 @@ export class TransactionBuilder { } sign( - vin: number, + signParams: number | TxbSignArg, keyPair: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer, ): void { + let vin: number; + if (typeof signParams === 'number') { + console.warn( + 'DEPRECATED: TransactionBuilder sign method arguments ' + + 'will change in v6, please use the TxbSignArg interface', + ); + vin = signParams; + } else if (typeof signParams === 'object') { + if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { + throw new TypeError( + `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, + ); + } + typeforce(typeforce.tuple(typeforce.Number, typeforce.Object), [ + signParams.vin, + signParams.keyPair, + ]); + vin = signParams.vin; + keyPair = signParams.keyPair; + const prevOutType = (this.__INPUTS[vin] || []).prevOutType; + switch (signParams.prevOutScriptType) { + case 'p2pkh': + if (prevOutType !== 'pubkeyhash') { + throw new TypeError(`input #${vin} is not of type p2pkh`); + } + break; + case 'p2pk': + if (prevOutType !== 'pubkey') { + throw new TypeError(`input #${vin} is not of type p2pk`); + } + break; + case 'p2wpkh': + if (prevOutType !== 'witnesspubkeyhash') { + throw new TypeError(`input #${vin} is not of type p2wpkh`); + } + typeforce(typeforce.Buffer, signParams.witnessScript); + typeforce(typeforce.Satoshi, signParams.witnessValue); + witnessScript = signParams.witnessScript; + witnessValue = signParams.witnessValue; + break; + case 'p2ms': + if (prevOutType !== 'multisig') { + throw new TypeError(`input #${vin} is not of type p2ms`); + } + break; + case 'p2sh-p2wpkh': + if (prevOutType !== 'scripthash') { + throw new TypeError(`input #${vin} is not of type p2sh-p2wpkh`); + } + typeforce(typeforce.Buffer, signParams.witnessScript); + typeforce(typeforce.Buffer, signParams.redeemScript); + typeforce(typeforce.Satoshi, signParams.witnessValue); + witnessScript = signParams.witnessScript; + redeemScript = signParams.redeemScript; + witnessValue = signParams.witnessValue; + break; + case 'p2sh-p2ms': + if (prevOutType !== 'scripthash') { + throw new TypeError(`input #${vin} is not of type p2sh-p2ms`); + } + typeforce(typeforce.Buffer, signParams.redeemScript); + redeemScript = signParams.redeemScript; + break; + case 'p2wsh-p2ms': + if (prevOutType !== 'witnessscripthash') { + throw new TypeError(`input #${vin} is not of type p2wsh-p2ms`); + } + typeforce(typeforce.Buffer, signParams.witnessScript); + typeforce(typeforce.Satoshi, signParams.witnessValue); + witnessScript = signParams.witnessScript; + witnessValue = signParams.witnessValue; + break; + case 'p2sh-p2wsh-p2ms': + if (prevOutType !== 'scripthash') { + throw new TypeError(`input #${vin} is not of type p2sh-p2wsh-p2ms`); + } + typeforce(typeforce.Buffer, signParams.witnessScript); + typeforce(typeforce.Buffer, signParams.redeemScript); + typeforce(typeforce.Satoshi, signParams.witnessValue); + witnessScript = signParams.witnessScript; + redeemScript = signParams.redeemScript; + witnessValue = signParams.witnessValue; + break; + } + } else { + throw new TypeError( + 'TransactionBuilder sign first arg must be TxbSignArg or number', + ); + } // TODO: remove keyPair.network matching in 4.0.0 if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network'); diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts index f993807..9441d6b 100644 --- a/types/transaction_builder.d.ts +++ b/types/transaction_builder.d.ts @@ -2,6 +2,15 @@ import { ECPairInterface } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; +interface TxbSignArg { + prevOutScriptType: string; + vin: number; + keyPair: ECPairInterface; + redeemScript?: Buffer; + hashType?: number; + witnessValue?: number; + witnessScript?: Buffer; +} export declare class TransactionBuilder { network: Network; maximumFeeRate: number; @@ -18,7 +27,7 @@ export declare class TransactionBuilder { addOutput(scriptPubKey: string | Buffer, value: number): number; build(): Transaction; buildIncomplete(): Transaction; - sign(vin: number, keyPair: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void; + sign(signParams: number | TxbSignArg, keyPair: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void; private __addInputUnsafe; private __build; private __canModifyInputs; @@ -26,3 +35,4 @@ export declare class TransactionBuilder { private __canModifyOutputs; private __overMaximumFees; } +export {}; From 969b3a5e18b329effa91f186989a52be67f3df06 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 13 Jun 2019 13:07:00 +0900 Subject: [PATCH 301/568] Fix tests to use new sign method --- package.json | 2 +- src/transaction_builder.js | 206 +++++++++++++++++++---- test/fixtures/transaction_builder.json | 216 ++++++++++++++++++------- test/integration/_regtest.js | 6 +- test/integration/payments.js | 31 +++- test/integration/transactions.js | 100 ++++++++++-- test/transaction_builder.js | 134 ++++++++++++--- ts_src/transaction_builder.ts | 211 ++++++++++++++++++++---- types/transaction_builder.d.ts | 2 +- 9 files changed, 744 insertions(+), 164 deletions(-) diff --git a/package.json b/package.json index 7882308..b8ec5f3 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "lint": "tslint -p tsconfig.json -c tslint.json", "nobuild:coverage-report": "nyc report --reporter=lcov", "nobuild:coverage-html": "nyc report --reporter=html", - "nobuild:coverage": "nyc --check-coverage --branches 85 --functions 90 --lines 90 mocha", + "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", "nobuild:integration": "mocha --timeout 50000 test/integration/", "nobuild:unit": "mocha", "prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore", diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 73acd3a..f6a8625 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -20,13 +20,26 @@ const PREVOUT_TYPES = new Set([ 'p2wpkh', 'p2ms', // P2SH wrapped + 'p2sh-p2pkh', + 'p2sh-p2pk', 'p2sh-p2wpkh', 'p2sh-p2ms', // P2WSH wrapped + 'p2wsh-p2pkh', + 'p2wsh-p2pk', 'p2wsh-p2ms', // P2SH-P2WSH wrapper + 'p2sh-p2wsh-p2pkh', + 'p2sh-p2wsh-p2pk', 'p2sh-p2wsh-p2ms', ]); +function tfMessage(type, value, message) { + try { + typeforce(type, value); + } catch (err) { + throw new Error(message); + } +} function txIsString(tx) { return typeof tx === 'string' || tx instanceof String; } @@ -159,66 +172,198 @@ class TransactionBuilder { ]); vin = signParams.vin; keyPair = signParams.keyPair; + hashType = signParams.hashType; const prevOutType = (this.__INPUTS[vin] || []).prevOutType; - switch (signParams.prevOutScriptType) { + const posType = signParams.prevOutScriptType; + switch (posType) { case 'p2pkh': - if (prevOutType !== 'pubkeyhash') { - throw new TypeError(`input #${vin} is not of type p2pkh`); + if (prevOutType && prevOutType !== 'pubkeyhash') { + throw new TypeError( + `input #${vin} is not of type p2pkh: ${prevOutType}`, + ); } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); break; case 'p2pk': - if (prevOutType !== 'pubkey') { - throw new TypeError(`input #${vin} is not of type p2pk`); + if (prevOutType && prevOutType !== 'pubkey') { + throw new TypeError( + `input #${vin} is not of type p2pk: ${prevOutType}`, + ); } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); break; case 'p2wpkh': - if (prevOutType !== 'witnesspubkeyhash') { - throw new TypeError(`input #${vin} is not of type p2wpkh`); + if (prevOutType && prevOutType !== 'witnesspubkeyhash') { + throw new TypeError( + `input #${vin} is not of type p2wpkh: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.witnessScript); - typeforce(typeforce.Satoshi, signParams.witnessValue); - witnessScript = signParams.witnessScript; + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); witnessValue = signParams.witnessValue; break; case 'p2ms': - if (prevOutType !== 'multisig') { - throw new TypeError(`input #${vin} is not of type p2ms`); + if (prevOutType && prevOutType !== 'multisig') { + throw new TypeError( + `input #${vin} is not of type p2ms: ${prevOutType}`, + ); } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); break; case 'p2sh-p2wpkh': - if (prevOutType !== 'scripthash') { - throw new TypeError(`input #${vin} is not of type p2sh-p2wpkh`); + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${vin} is not of type p2sh-p2wpkh: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.witnessScript); - typeforce(typeforce.Buffer, signParams.redeemScript); - typeforce(typeforce.Satoshi, signParams.witnessValue); + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); witnessScript = signParams.witnessScript; redeemScript = signParams.redeemScript; witnessValue = signParams.witnessValue; break; case 'p2sh-p2ms': - if (prevOutType !== 'scripthash') { - throw new TypeError(`input #${vin} is not of type p2sh-p2ms`); + case 'p2sh-p2pk': + case 'p2sh-p2pkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${vin} is not of type ${posType}: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.redeemScript); + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); redeemScript = signParams.redeemScript; break; case 'p2wsh-p2ms': - if (prevOutType !== 'witnessscripthash') { - throw new TypeError(`input #${vin} is not of type p2wsh-p2ms`); + case 'p2wsh-p2pk': + case 'p2wsh-p2pkh': + if (prevOutType && prevOutType !== 'witnessscripthash') { + throw new TypeError( + `input #${vin} is not of type ${posType}: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.witnessScript); - typeforce(typeforce.Satoshi, signParams.witnessValue); + tfMessage( + typeforce.Buffer, + signParams.witnessScript, + `${posType} requires witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); witnessScript = signParams.witnessScript; witnessValue = signParams.witnessValue; break; case 'p2sh-p2wsh-p2ms': - if (prevOutType !== 'scripthash') { - throw new TypeError(`input #${vin} is not of type p2sh-p2wsh-p2ms`); + case 'p2sh-p2wsh-p2pk': + case 'p2sh-p2wsh-p2pkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${vin} is not of type ${posType}: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.witnessScript); - typeforce(typeforce.Buffer, signParams.redeemScript); - typeforce(typeforce.Satoshi, signParams.witnessValue); + tfMessage( + typeforce.Buffer, + signParams.witnessScript, + `${posType} requires witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires witnessScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessScript`, + ); witnessScript = signParams.witnessScript; redeemScript = signParams.redeemScript; witnessValue = signParams.witnessValue; @@ -229,6 +374,9 @@ class TransactionBuilder { 'TransactionBuilder sign first arg must be TxbSignArg or number', ); } + if (keyPair === undefined) { + throw new Error('sign requires keypair'); + } // TODO: remove keyPair.network matching in 4.0.0 if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network'); diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 24be3ba..040104a 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -11,6 +11,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] @@ -34,6 +35,7 @@ "prevTxScript": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 OP_CHECKSIG", "signs": [ { + "prevOutScriptType": "p2pk", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] @@ -56,6 +58,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_DUP OP_HASH160 751e76e8199196d454941c45d1b3a323f1433bd6 OP_EQUALVERIFY OP_CHECKSIG" } @@ -80,11 +83,14 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG" }, { - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" + "prevOutScriptType": "p2sh-p2ms", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG" } ] } @@ -108,9 +114,11 @@ "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" }, { + "prevOutScriptType": "p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" } ] @@ -135,9 +143,11 @@ "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" }, { + "prevOutScriptType": "p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" } ] @@ -162,9 +172,11 @@ "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" }, { + "prevOutScriptType": "p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" } ] @@ -188,11 +200,14 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG" }, { - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe" + "prevOutScriptType": "p2sh-p2ms", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG" } ] } @@ -215,12 +230,15 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", "hashType": 1 }, { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", "hashType": 2 } ] @@ -244,6 +262,7 @@ "prevTxScript": "OP_HASH160 e89677d91455e541630d62c63718bef738b478b1 OP_EQUAL", "signs": [ { + "prevOutScriptType": "p2sh-p2pk", "keyPair": "KxLDMPtVM7sLSu2v5n1LybDibw6P9FFbL4pUwJ51UDm7rp5AmXWW", "redeemScript": "033e29aea1168a835d5e386c292082db7b7807172a10ec634ad34226f36d79e70f OP_CHECKSIG" } @@ -267,6 +286,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] @@ -291,6 +311,7 @@ "sequence": 2147001, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] @@ -314,6 +335,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] @@ -337,6 +359,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "cQ6483mDWwoG8o4tn6nU9Jg52RKMjPUWXSY1vycAyPRXQJ1Pn2Rq" } ] @@ -359,6 +382,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] @@ -381,6 +405,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] @@ -404,6 +429,7 @@ "prevTxScript": "OP_0 751e76e8199196d454941c45d1b3a323f1433bd6", "signs": [ { + "prevOutScriptType": "p2wpkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "value": 10000 } @@ -428,6 +454,7 @@ "prevTxScript": "OP_0 0f9ea7bae7166c980169059e39443ed13324495b0d6678ce716262e879591210", "signs": [ { + "prevOutScriptType": "p2wsh-p2pk", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "witnessScript": "038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_CHECKSIG", "value": 80000 @@ -452,9 +479,9 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 3, - "value": 30000000 + "hashType": 3 } ], "sequence": 4294967295, @@ -482,9 +509,9 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 1, - "value": 40000 + "hashType": 1 } ], "sequence": 4294967295, @@ -495,9 +522,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 1, - "value": 40000 + "hashType": 1 } ], "sequence": 4294967295, @@ -508,9 +535,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 1, - "value": 40000 + "hashType": 1 } ], "sequence": 4294967295, @@ -542,9 +569,9 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 129, - "value": 40000 + "hashType": 129 } ], "sequence": 4294967295, @@ -555,9 +582,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 129, - "value": 40000 + "hashType": 129 } ], "sequence": 4294967295, @@ -568,9 +595,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 129, - "value": 40000 + "hashType": 129 } ], "sequence": 4294967295, @@ -602,9 +629,9 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 3, - "value": 40000 + "hashType": 3 } ], "sequence": 4294967295, @@ -615,9 +642,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 3, - "value": 40000 + "hashType": 3 } ], "sequence": 4294967295, @@ -628,9 +655,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 3, - "value": 40000 + "hashType": 3 } ], "sequence": 4294967295, @@ -662,9 +689,9 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 131, - "value": 40000 + "hashType": 131 } ], "sequence": 4294967295, @@ -675,9 +702,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 131, - "value": 40000 + "hashType": 131 } ], "sequence": 4294967295, @@ -688,9 +715,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 131, - "value": 40000 + "hashType": 131 } ], "sequence": 4294967295, @@ -722,9 +749,9 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 2, - "value": 40000 + "hashType": 2 } ], "sequence": 4294967295, @@ -735,9 +762,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 2, - "value": 40000 + "hashType": 2 } ], "sequence": 4294967295, @@ -748,9 +775,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 2, - "value": 40000 + "hashType": 2 } ], "sequence": 4294967295, @@ -782,9 +809,9 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 130, - "value": 40000 + "hashType": 130 } ], "sequence": 4294967295, @@ -795,9 +822,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 130, - "value": 40000 + "hashType": 130 } ], "sequence": 4294967295, @@ -808,9 +835,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 130, - "value": 40000 + "hashType": 130 } ], "sequence": 4294967295, @@ -842,9 +869,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pk", "keyPair": "L3Wh2WPg21MWqzMFYsVC7PeBXcq1ow32KRccRihnTUnAhJaZUvg1", - "hashType": 1, - "value": 625000000 + "hashType": 1 } ], "sequence": 4294967278, @@ -855,6 +882,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2wpkh", "keyPair": "KzVTBhbMaKrAYagJ11VdTaBrb6yzLykLGyuMBkf9sCFPDxdT8shL", "hashType": 1, "value": 600000000 @@ -886,6 +914,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2sh-p2wpkh", "keyPair": "L57KYn5isHFThD4cohjJgLTZA2vaxnMMKWngnzbttF159yH9dARf", "hashType": 1, "redeemScript": "OP_0 79091972186c449eb1ded22b78e40d009bdf0089", @@ -909,7 +938,7 @@ "locktime": 1170 }, { - "description": "Sighash V1: P2WSH(P2SH(P2MS 6/6))", + "description": "Sighash V1: P2SH(P2WSH(P2MS 6/6))", "txHex": "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff02e6312761010000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac583e0f00000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac0800483045022100f902f491c4df15199e584790ae8c7202569a977accac0a09fa3f4f3b6ec3517602205961a951c4a12fa966da67b6fd75975b9de156b9895f8ab5f289ecaee12b9b3501473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502483045022100bd5294e145d729e9593f49079b74e6e4b8aeba63440408595ce0949d5c6450a702207f9c9fb45907fe0180d3f4bee499006007bb90894b5f824a26dfa5d3afec543303483045022100febf9409d7f3c091ddc4d296a483aae7b3d2a91d38f6ea2a153f7ff085fe7766022078d11972c74cd78f816152463a5e1a5d986dfb94b55cf5f7242e4f6d5df000ff81483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a088247304402201a0e125aed6a700e45d6c86017d5a9d2264c8079319d868f3f163f5d63cb5bfe02200887608f2322ca0d82df67316275371028b0b21750417d594117963fe23b67ec83cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000", "version": 1, "inputs": [ @@ -918,6 +947,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "L15NqbRvcqso8ZCqD8aFaZV3CTypw6svjk8oCWsAfMmNViahS2Mw", "hashType": 1, "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", @@ -925,6 +955,7 @@ "value": 987654321 }, { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "Kwpf3fycToLH1ymZUkezFrYwTjhKaucHD861Ft5A4Tih855LBxVx", "hashType": 2, "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", @@ -932,6 +963,7 @@ "value": 987654321 }, { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "L1EV111k2WzNTapY2etd1TaB2aWbjUgouko9YyipS2S8H8WdGkQi", "hashType": 3, "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", @@ -939,6 +971,7 @@ "value": 987654321 }, { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "KwuvEmpBtJaw8SQLnpi3CoEHZJvv33EnYBHn13VcDuwprJqmkfSH", "hashType": 129, "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", @@ -946,6 +979,7 @@ "value": 987654321 }, { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "L5kdM8eWyfj8pdRDWA8j5SmBwAQt2yyhqjb2ZZQxtRGJfCquC6TB", "hashType": 130, "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", @@ -953,6 +987,7 @@ "value": 987654321 }, { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "KyT4JbJVRy5FZ6ZEZhkaocP2JSBXiF7X3Cx6DBAGLrydR9fiXQUK", "hashType": 131, "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", @@ -986,9 +1021,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pk", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "value": 80000 + "hashType": 1 } ], "sequence": 4294967295, @@ -1013,10 +1048,10 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2pk", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, - "redeemScript": "038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_CHECKSIG", - "value": 80000 + "redeemScript": "038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_CHECKSIG" } ], "sequence": 4294967295, @@ -1041,6 +1076,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2wsh-p2pk", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, "witnessScript": "038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_CHECKSIG", @@ -1069,6 +1105,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2wsh-p2pk", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, "redeemScript": "OP_0 0f9ea7bae7166c980169059e39443ed13324495b0d6678ce716262e879591210", @@ -1098,9 +1135,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "value": 80000 + "hashType": 1 } ], "sequence": 4294967295, @@ -1125,10 +1162,10 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2pkh", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, - "redeemScript": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG", - "value": 80000 + "redeemScript": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" } ], "sequence": 4294967295, @@ -1153,6 +1190,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2wsh-p2pkh", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, "witnessScript": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG", @@ -1181,6 +1219,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2wsh-p2pkh", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, "witnessScript": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG", @@ -1210,9 +1249,9 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2ms", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "value": 80000 + "hashType": 1 } ], "sequence": 4294967295, @@ -1237,10 +1276,10 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, - "redeemScript": "OP_1 038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_1 OP_CHECKMULTISIG", - "value": 80000 + "redeemScript": "OP_1 038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_1 OP_CHECKMULTISIG" } ], "sequence": 4294967295, @@ -1265,6 +1304,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2wsh-p2ms", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, "witnessScript": "OP_1 038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_1 OP_CHECKMULTISIG", @@ -1293,6 +1333,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, "witnessScript": "OP_1 038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_1 OP_CHECKMULTISIG", @@ -1322,6 +1363,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2wpkh", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, "value": 80000 @@ -1349,6 +1391,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2wpkh", "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", "hashType": 1, "redeemScript": "OP_0 851a33a5ef0d4279bd5854949174e2c65b1d4500", @@ -1377,6 +1420,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] @@ -1402,6 +1446,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS", "witnessScript": "OP_2 02bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e2 02d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea19 OP_2 OP_CHECKMULTISIG", "redeemScript": "OP_0 24376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af", @@ -1430,12 +1475,14 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS", "witnessScript": "OP_2 02bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e2 02d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea19 OP_2 OP_CHECKMULTISIG", "redeemScript": "OP_0 24376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af", "value": 100000 }, { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "cTUFsNeVd8TKU4yREN8nMdViNnHyNvCCYVRmRUmkMLgomiMWTiii", "witnessScript": "OP_2 02bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e2 02d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea19 OP_2 OP_CHECKMULTISIG", "redeemScript": "OP_0 24376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af", @@ -1453,7 +1500,7 @@ ] }, { - "description": "P2WSH(P2MS 2/3) -> P2PKH", + "description": "P2SH(P2WSH(P2MS 2/3)) -> P2PKH", "network": "testnet", "txHex": "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01000000232200201b48bf145648b9492ecd6d76754ea3def4b90e22e4ef7aee9ca291b2de455701ffffffff01f07e0e00000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac0400473044022036c9ecb03cb04c09be1f52766725dcfe9a815973bd2f34ce19a345f2d925a45502207b90737852d2508db104ad17612de473687e67928c045555a1ed8d495c0570d901483045022100aec0e58e4e597b35ca5a727702a0da3d4f2ef4759914da7fc80aecb3c479a6d902201ec27ea8dcca4b73ee81e4b627f52f9e627c3497f61e4beeb98f86e02979640a0169522103c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc5787721020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b02102d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc914953ae00000000", "version": 1, @@ -1466,6 +1513,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "cUxccFVBdJRq6HnyxiFMd8Z15GLThXaNLcnPBgoXLEv9iX6wuV2b", "witnessScript": "OP_2 03c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc57877 020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b0 02d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc9149 OP_3 OP_CHECKMULTISIG", "redeemScript": "OP_0 1b48bf145648b9492ecd6d76754ea3def4b90e22e4ef7aee9ca291b2de455701", @@ -1473,6 +1521,7 @@ "stage": true }, { + "prevOutScriptType": "p2sh-p2wsh-p2ms", "keyPair": "cVSNe9ZdZRsRvEBL8YRR7YiZmH4cLsf5FthgERWkZezJVrGseaXy", "witnessScript": "OP_2 03c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc57877 020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b0 02d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc9149 OP_3 OP_CHECKMULTISIG", "redeemScript": "OP_0 1b48bf145648b9492ecd6d76754ea3def4b90e22e4ef7aee9ca291b2de455701", @@ -1561,6 +1610,7 @@ "scriptSigAfter": "OP_0 OP_0 OP_0 30440220793d87f2a8afeb856816efa38984418c692c15170e99ca371f547454079c0dd3022074ae95e438fee1f37619fabe0ce1083c3be0d65c3defb5337833d50fdc694b1301 52210258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a9121038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f2103b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d953ae", "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "cTkcnMZoFYH1UgumzCFHv2veLMNN1PaJyHHUxFT127zhNGBqqEZ2", "redeemScript": "OP_2 0258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a91 038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f 03b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d9 OP_3 OP_CHECKMULTISIG" } @@ -1585,11 +1635,13 @@ "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 0, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" }, { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 1, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" @@ -1616,11 +1668,13 @@ "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 1, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "scriptSig": "OP_0 OP_0 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" }, { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 0, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" @@ -1647,11 +1701,13 @@ "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 0, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" }, { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 1, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" @@ -1678,11 +1734,13 @@ "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 0, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 1, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100ae06d8269b79b5cfa0297d1d88261b0061e52fc2814948c3aa05fa78ee76894302206e0c79a5c90569d8c72a542ef9a06471cbbcd2c651b312339983dfba4f8ff46301 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" @@ -1709,11 +1767,13 @@ "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 0, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 2, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" @@ -1740,11 +1800,13 @@ "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 2, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", "scriptSig": "OP_0 OP_0 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 0, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" @@ -1771,11 +1833,13 @@ "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 0, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 2, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" @@ -1802,11 +1866,13 @@ "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 2, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", "scriptSig": "OP_0 OP_0 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { + "prevOutScriptType": "p2sh-p2ms", "pubKeyIndex": 0, "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSigBefore": "OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae", @@ -1863,6 +1929,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzBQVXYUGDAvqG7VeU3C7ZMRYiwtsxSVVFcYGzKU9E4aUVDUquZU" } ] @@ -1900,6 +1967,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] @@ -1927,6 +1995,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG" } @@ -1980,10 +2049,11 @@ "network": "testnet", "inputs": [ { - "txHex": "0100000001f7e6430096cd2790bac115aaab22c0a50fb0a1794305302e1a399e81d8d354f4020000006a47304402205793a862d193264afc32713e2e14541e1ff9ebb647dd7e7e6a0051d0faa87de302205216653741ecbbed573ea2fc053209dd6980616701c27be5b958a159fc97f45a012103e877e7deb32d19250dcfe534ea82c99ad739800295cd5429a7f69e2896c36fcdfeffffff0340420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac8bda2702000000001976a9145a0ef60784137d03e7868d063b05424f2f43799f88ac40420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac2fcc0e00", + "txHex": "01000000000101f7e6430096cd2790bac115aaab22c0a50fb0a1794305302e1a399e81d8d354f40200000000feffffff0340420f00000000001600145c7b8d623fba952d2387703d051d8e931a6aa0a18bda2702000000001976a9145a0ef60784137d03e7868d063b05424f2f43799f88ac40420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac0247304402205793a862d193264afc32713e2e14541e1ff9ebb647dd7e7e6a0051d0faa87de302205216653741ecbbed573ea2fc053209dd6980616701c27be5b958a159fc97f45a012103e877e7deb32d19250dcfe534ea82c99ad739800295cd5429a7f69e2896c36fcd2fcc0e00", "vout": 0, "signs": [ { + "prevOutScriptType": "p2wpkh", "keyPair": "cQ6483mDWwoG8o4tn6nU9Jg52RKMjPUWXSY1vycAyPRXQJ1Pn2Rq", "throws": true, "value": 22500000000 @@ -2007,9 +2077,11 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" }, { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "throws": true } @@ -2033,6 +2105,7 @@ "prevTxScript": "OP_0 15a71ffa7b5bb70cddefcf364494071022efe390", "signs": [ { + "prevOutScriptType": "p2wpkh", "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", "value": 10000, "throws": true @@ -2057,6 +2130,7 @@ "prevTxScript": "OP_0 5339df4de3854c4208376443ed075014ad996aa349ad6b5abf6c4d20f604d348", "signs": [ { + "prevOutScriptType": "p2wsh-p2pk", "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", "witnessScript": "04f56d09b32cefc818735150bf8560eefdaf30d2edb3fe557bf27682aedaed81bf9aaff7eeb496e088058ec548826c12b521dbb566a862d9b67677910c2b421e06 OP_CHECKSIG", "value": 80000, @@ -2082,6 +2156,7 @@ "prevTxScript": "OP_HASH160 5afe12b2827e3eac05fe3f17c59406ef262aa177 OP_EQUAL", "signs": [ { + "prevOutScriptType": "p2sh-p2wsh-p2pk", "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", "redeemScript": "OP_0 5339df4de3854c4208376443ed075014ad996aa349ad6b5abf6c4d20f604d348", "witnessScript": "04f56d09b32cefc818735150bf8560eefdaf30d2edb3fe557bf27682aedaed81bf9aaff7eeb496e088058ec548826c12b521dbb566a862d9b67677910c2b421e06 OP_CHECKSIG", @@ -2106,6 +2181,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2sh-p2pk", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", "throws": true @@ -2121,7 +2197,7 @@ ] }, { - "exception": "PrevOutScript is scripthash, requires redeemScript", + "exception": "p2sh-p2pkh requires redeemScript", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -2129,6 +2205,7 @@ "prevTxScript": "OP_HASH160 7f67f0521934a57d3039f77f9f32cf313f3ac74b OP_EQUAL", "signs": [ { + "prevOutScriptType": "p2sh-p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "throws": true } @@ -2143,7 +2220,7 @@ ] }, { - "exception": "PrevOutScript is witnessscripthash, requires witnessScript", + "exception": "p2wsh-p2pk requires witnessScript", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -2151,6 +2228,7 @@ "prevTxScript": "OP_0 0f9ea7bae7166c980169059e39443ed13324495b0d6678ce716262e879591210", "signs": [ { + "prevOutScriptType": "p2wsh-p2pk", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "throws": true } @@ -2173,10 +2251,12 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" }, { + "prevOutScriptType": "p2sh-p2ms", "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "throws": true @@ -2201,9 +2281,9 @@ "prevTxScript": "OP_HASH160 ffffffffffffffffffffffffffffffffffffffff OP_EQUAL", "signs": [ { + "prevOutScriptType": "p2sh-p2pkh", "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", "redeemScript": "OP_1", - "value": 10000, "throws": true } ] @@ -2226,6 +2306,7 @@ "prevTxScript": "OP_0 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "signs": [ { + "prevOutScriptType": "p2wsh-p2pkh", "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", "witnessScript": "OP_1", "value": 10000, @@ -2249,6 +2330,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2sh-p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_HASH160 7f67f0521934a57d3039f77f9f32cf313f3ac74b OP_EQUAL", "throws": true @@ -2264,7 +2346,7 @@ ] }, { - "exception": "PrevOutScript must be P2SH", + "exception": "input #0 is not of type p2sh-p2pkh: pubkeyhash", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -2272,6 +2354,7 @@ "prevTxScript": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "signs": [ { + "prevOutScriptType": "p2sh-p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG", "throws": true @@ -2295,11 +2378,14 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf", "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG" }, { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf", + "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG", "throws": true } ] @@ -2322,6 +2408,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "network": "testnet", "throws": true @@ -2345,6 +2432,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2sh-p2ms", "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG", "throws": true @@ -2360,7 +2448,7 @@ ] }, { - "exception": "nulldata not supported", + "exception": "input #0 is not of type p2pkh: nulldata", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -2368,6 +2456,7 @@ "prevTxScript": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", "throws": true } @@ -2390,6 +2479,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "hashType": 2 } @@ -2400,6 +2490,7 @@ "vout": 1, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "throws": true } @@ -2417,6 +2508,7 @@ "vout": 0, "signs": [ { + "prevOutScriptType": "p2pkh", "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "throws": true } diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 8be864a..30b868c 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -92,7 +92,11 @@ async function faucetComplex (output, value) { const txvb = new bitcoin.TransactionBuilder(NETWORK) txvb.addInput(unspent.txId, unspent.vout, null, p2pkh.output) txvb.addOutput(output, value) - txvb.sign(0, keyPair) + txvb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair + }) const txv = txvb.build() await broadcast(txv.toHex()) diff --git a/test/integration/payments.js b/test/integration/payments.js index 66b0a13..256bd00 100644 --- a/test/integration/payments.js +++ b/test/integration/payments.js @@ -15,12 +15,29 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { txb.addInput(unspent.txId, unspent.vout, null, prevOutput) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const posType = depends.prevOutScriptType + const needsValue = !!witnessScript || posType.slice(-6) === 'p2wpkh' + if (depends.signatures) { keyPairs.forEach(keyPair => { - txb.sign(0, keyPair, redeemScript, null, unspent.value, witnessScript) + txb.sign({ + prevOutScriptType: posType, + vin: 0, + keyPair, + redeemScript, + witnessValue: needsValue ? unspent.value : undefined, + witnessScript, + }) }) } else if (depends.signature) { - txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, witnessScript) + txb.sign({ + prevOutScriptType: posType, + vin: 0, + keyPair: keyPairs[0], + redeemScript, + witnessValue: needsValue ? unspent.value : undefined, + witnessScript, + }) } return regtestUtils.broadcast(txb.build().toHex()) @@ -41,12 +58,14 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { describe('bitcoinjs-lib (payments - ' + k + ')', () => { it('can broadcast as an output, and be spent as an input', async () => { - await buildAndSign(depends, output, null, null) + Object.assign(depends, { prevOutScriptType: k }) + await buildAndSign(depends, output, undefined, undefined) }) it('can (as P2SH(' + k + ')) broadcast as an output, and be spent as an input', async () => { const p2sh = bitcoin.payments.p2sh({ redeem: { output }, network: NETWORK }) - await buildAndSign(depends, p2sh.output, p2sh.redeem.output, null) + Object.assign(depends, { prevOutScriptType: 'p2sh-' + k }) + await buildAndSign(depends, p2sh.output, p2sh.redeem.output, undefined) }) // NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail @@ -54,13 +73,15 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', async () => { const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) - await buildAndSign(depends, p2wsh.output, null, p2wsh.redeem.output) + Object.assign(depends, { prevOutScriptType: 'p2wsh-' + k }) + await buildAndSign(depends, p2wsh.output, undefined, p2wsh.redeem.output) }) it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', async () => { const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) const p2sh = bitcoin.payments.p2sh({ redeem: { output: p2wsh.output }, network: NETWORK }) + Object.assign(depends, { prevOutScriptType: 'p2sh-p2wsh-' + k }) await buildAndSign(depends, p2sh.output, p2sh.redeem.output, p2wsh.redeem.output) }) }) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index c096b87..464460e 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -18,7 +18,11 @@ describe('bitcoinjs-lib (transactions)', () => { txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000) // (in)15000 - (out)12000 = (fee)3000, this is the miner fee - txb.sign(0, alice) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair: alice + }) // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below assert.strictEqual(txb.build().toHex(), '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000') @@ -36,8 +40,16 @@ describe('bitcoinjs-lib (transactions)', () => { txb.addOutput('1JtK9CQw1syfWj1WtFMWomrYdV3W2tWBF9', 170000) // (in)(200000 + 300000) - (out)(180000 + 170000) = (fee)150000, this is the miner fee - txb.sign(1, bob) // Bob signs his input, which was the second input (1th) - txb.sign(0, alice) // Alice signs her input, which was the first input (0th) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 1, + keyPair: bob + }) // Bob signs his input, which was the second input (1th) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair: alice + }) // Alice signs her input, which was the first input (0th) // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below assert.strictEqual(txb.build().toHex(), '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000') @@ -65,8 +77,16 @@ describe('bitcoinjs-lib (transactions)', () => { // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee // Alice signs each input with the respective private keys - txb.sign(0, alice1) - txb.sign(1, alice2) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair: alice1 + }) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 1, + keyPair: alice2 + }) // build and broadcast our RegTest network await regtestUtils.broadcast(txb.build().toHex()) @@ -85,7 +105,11 @@ describe('bitcoinjs-lib (transactions)', () => { txb.addInput(unspent.txId, unspent.vout) txb.addOutput(embed.output, 1000) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) - txb.sign(0, keyPair) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) // build and broadcast to the RegTest network await regtestUtils.broadcast(txb.build().toHex()) @@ -108,8 +132,18 @@ describe('bitcoinjs-lib (transactions)', () => { txb.addInput(unspent.txId, unspent.vout) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) - txb.sign(0, keyPairs[0], p2sh.redeem.output) - txb.sign(0, keyPairs[2], p2sh.redeem.output) + txb.sign({ + prevOutScriptType: 'p2sh-p2ms', + vin: 0, + keyPair: keyPairs[0], + redeemScript: p2sh.redeem.output, + }) + txb.sign({ + prevOutScriptType: 'p2sh-p2ms', + vin: 0, + keyPair: keyPairs[2], + redeemScript: p2sh.redeem.output, + }) const tx = txb.build() // build and broadcast to the Bitcoin RegTest network @@ -133,7 +167,13 @@ describe('bitcoinjs-lib (transactions)', () => { const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - txb.sign(0, keyPair, p2sh.redeem.output, null, unspent.value) + txb.sign({ + prevOutScriptType: 'p2sh-p2wpkh', + vin: 0, + keyPair: keyPair, + redeemScript: p2sh.redeem.output, + witnessValue: unspent.value, + }) const tx = txb.build() @@ -158,7 +198,12 @@ describe('bitcoinjs-lib (transactions)', () => { const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout, null, p2wpkh.output) // NOTE: provide the prevOutScript! txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - txb.sign(0, keyPair, null, null, unspent.value) // NOTE: no redeem script + txb.sign({ + prevOutScriptType: 'p2wpkh', + vin: 0, + keyPair: keyPair, + witnessValue: unspent.value, + }) // NOTE: no redeem script const tx = txb.build() // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network @@ -183,7 +228,13 @@ describe('bitcoinjs-lib (transactions)', () => { const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout, null, p2wsh.output) // NOTE: provide the prevOutScript! txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) - txb.sign(0, keyPair, null, null, 5e4, p2wsh.redeem.output) // NOTE: provide a witnessScript! + txb.sign({ + prevOutScriptType: 'p2wsh-p2pk', + vin: 0, + keyPair: keyPair, + witnessValue: 5e4, + witnessScript: p2wsh.redeem.output, + }) // NOTE: provide a witnessScript! const tx = txb.build() // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network @@ -215,9 +266,30 @@ describe('bitcoinjs-lib (transactions)', () => { const txb = new bitcoin.TransactionBuilder(regtest) txb.addInput(unspent.txId, unspent.vout, null, p2sh.output) txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4) - txb.sign(0, keyPairs[0], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) - txb.sign(0, keyPairs[2], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) - txb.sign(0, keyPairs[3], p2sh.redeem.output, null, unspent.value, p2wsh.redeem.output) + txb.sign({ + prevOutScriptType: 'p2sh-p2wsh-p2ms', + vin: 0, + keyPair: keyPairs[0], + redeemScript: p2sh.redeem.output, + witnessValue: unspent.value, + witnessScript: p2wsh.redeem.output, + }) + txb.sign({ + prevOutScriptType: 'p2sh-p2wsh-p2ms', + vin: 0, + keyPair: keyPairs[2], + redeemScript: p2sh.redeem.output, + witnessValue: unspent.value, + witnessScript: p2wsh.redeem.output, + }) + txb.sign({ + prevOutScriptType: 'p2sh-p2wsh-p2ms', + vin: 0, + keyPair: keyPairs[3], + redeemScript: p2sh.redeem.output, + witnessValue: unspent.value, + witnessScript: p2wsh.redeem.output, + }) const tx = txb.build() diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 1af8272..0bf2d88 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -21,21 +21,29 @@ function constructSign (f, txb) { const keyPair = ECPair.fromWIF(sign.keyPair, network) let redeemScript let witnessScript - let value + let witnessValue if (sign.redeemScript) { redeemScript = bscript.fromASM(sign.redeemScript) } if (sign.value) { - value = sign.value + witnessValue = sign.value } if (sign.witnessScript) { witnessScript = bscript.fromASM(sign.witnessScript) } - txb.sign(index, keyPair, redeemScript, sign.hashType, value, witnessScript) + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: index, + keyPair, + redeemScript, + hashType: sign.hashType, + witnessValue, + witnessScript, + }) if (sign.stage) { const tx = txb.buildIncomplete() @@ -232,7 +240,11 @@ describe('TransactionBuilder', () => { it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { txb.addInput(txHash, 0) txb.addOutput(scripts[0], 1000) - txb.sign(0, keyPair) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) assert.throws(() => { txb.addInput(txHash, 0) @@ -274,26 +286,46 @@ describe('TransactionBuilder', () => { it('add second output after signed first input with SIGHASH_NONE', () => { txb.addInput(txHash, 0) txb.addOutput(scripts[0], 2000) - txb.sign(0, keyPair, undefined, Transaction.SIGHASH_NONE) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + hashType: Transaction.SIGHASH_NONE, + }) assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) }) it('add first output after signed first input with SIGHASH_NONE', () => { txb.addInput(txHash, 0) - txb.sign(0, keyPair, undefined, Transaction.SIGHASH_NONE) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + hashType: Transaction.SIGHASH_NONE, + }) assert.strictEqual(txb.addOutput(scripts[0], 2000), 0) }) it('add second output after signed first input with SIGHASH_SINGLE', () => { txb.addInput(txHash, 0) txb.addOutput(scripts[0], 2000) - txb.sign(0, keyPair, undefined, Transaction.SIGHASH_SINGLE) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + hashType: Transaction.SIGHASH_SINGLE, + }) assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) }) it('add first output after signed first input with SIGHASH_SINGLE', () => { txb.addInput(txHash, 0) - txb.sign(0, keyPair, undefined, Transaction.SIGHASH_SINGLE) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + hashType: Transaction.SIGHASH_SINGLE, + }) assert.throws(() => { txb.addOutput(scripts[0], 2000) }, /No, this would invalidate signatures/) @@ -302,7 +334,11 @@ describe('TransactionBuilder', () => { it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { txb.addInput(txHash, 0) txb.addOutput(scripts[0], 2000) - txb.sign(0, keyPair) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) assert.throws(() => { txb.addOutput(scripts[1], 9000) @@ -315,7 +351,11 @@ describe('TransactionBuilder', () => { const txb = new TransactionBuilder() txb.addInput(txHash, 0) txb.addOutput(scripts[0], 100) - txb.sign(0, keyPair) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) assert.throws(() => { txb.setLockTime(65535) @@ -334,7 +374,11 @@ describe('TransactionBuilder', () => { txb.setVersion(1) txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) txb.addOutput('1111111111111111111114oLvT2', 100000) - txb.sign(0, keyPair) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) @@ -343,7 +387,11 @@ describe('TransactionBuilder', () => { txb.setVersion(1) txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) txb.addOutput('1111111111111111111114oLvT2', 100000) - txb.sign(0, keyPair) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) // high R assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') @@ -352,7 +400,11 @@ describe('TransactionBuilder', () => { txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) txb.addOutput('1111111111111111111114oLvT2', 100000) txb.setLowR() - txb.sign(0, keyPair) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) // low R assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) @@ -379,11 +431,27 @@ describe('TransactionBuilder', () => { if (sign.throws) { assert.throws(() => { - txb.sign(index, keyPair2, redeemScript, sign.hashType, sign.value, witnessScript) + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: index, + keyPair: keyPair2, + redeemScript, + hashType: sign.hashType, + witnessValue: sign.value, + witnessScript, + }) }, new RegExp(f.exception)) threw = true } else { - txb.sign(index, keyPair2, redeemScript, sign.hashType, sign.value, witnessScript) + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: index, + keyPair: keyPair2, + redeemScript, + hashType: sign.hashType, + witnessValue: sign.value, + witnessScript, + }) } }) }) @@ -516,7 +584,13 @@ describe('TransactionBuilder', () => { } const keyPair2 = ECPair.fromWIF(sign.keyPair, network) - txb.sign(i, keyPair2, redeemScript, sign.hashType) + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: i, + keyPair: keyPair2, + redeemScript, + hashType: sign.hashType, + }) // update the tx tx = txb.buildIncomplete() @@ -571,7 +645,14 @@ describe('TransactionBuilder', () => { txb.setVersion(1) txb.addInput('a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', 1, 0xffffffff, scriptPubKey) txb.addOutput(scriptPubKey, 99000) - txb.sign(0, keyPair, redeemScript, null, 100000, witnessScript) + txb.sign({ + prevOutScriptType: 'p2sh-p2wsh-p2ms', + vin: 0, + keyPair, + redeemScript, + witnessValue: 100000, + witnessScript, + }) // 2-of-2 signed only once const tx = txb.buildIncomplete() @@ -596,7 +677,12 @@ describe('TransactionBuilder', () => { const txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet) const keyPair2 = ECPair.fromWIF('91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', network) - txb.sign(0, keyPair2, redeemScript) + txb.sign({ + prevOutScriptType: 'p2sh-p2ms', + vin: 0, + keyPair: keyPair2, + redeemScript, + }) const tx2 = txb.build() assert.strictEqual(tx2.getId(), 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') @@ -613,14 +699,22 @@ describe('TransactionBuilder', () => { // sign, as expected txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) - txb.sign(0, keyPair) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) const txId = txb.build().getId() assert.strictEqual(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') // and, repeat txb = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) - txb.sign(0, keyPair) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) const txId2 = txb.build().getId() assert.strictEqual(txId, txId2) }) diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index b4d794d..33ea48b 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -23,11 +23,17 @@ const PREVOUT_TYPES: Set<string> = new Set([ 'p2wpkh', 'p2ms', // P2SH wrapped + 'p2sh-p2pkh', + 'p2sh-p2pk', 'p2sh-p2wpkh', 'p2sh-p2ms', // P2WSH wrapped + 'p2wsh-p2pkh', + 'p2wsh-p2pk', 'p2wsh-p2ms', // P2SH-P2WSH wrapper + 'p2sh-p2wsh-p2pkh', + 'p2sh-p2wsh-p2pk', 'p2sh-p2wsh-p2ms', ]); @@ -75,6 +81,14 @@ interface TxbSignArg { witnessScript?: Buffer; } +function tfMessage(type: any, value: any, message: string): void { + try { + typeforce(type, value); + } catch (err) { + throw new Error(message); + } +} + function txIsString(tx: Buffer | string | Transaction): tx is string { return typeof tx === 'string' || tx instanceof String; } @@ -223,7 +237,7 @@ export class TransactionBuilder { sign( signParams: number | TxbSignArg, - keyPair: ECPairInterface, + keyPair?: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, @@ -248,66 +262,198 @@ export class TransactionBuilder { ]); vin = signParams.vin; keyPair = signParams.keyPair; + hashType = signParams.hashType; const prevOutType = (this.__INPUTS[vin] || []).prevOutType; - switch (signParams.prevOutScriptType) { + const posType = signParams.prevOutScriptType; + switch (posType) { case 'p2pkh': - if (prevOutType !== 'pubkeyhash') { - throw new TypeError(`input #${vin} is not of type p2pkh`); + if (prevOutType && prevOutType !== 'pubkeyhash') { + throw new TypeError( + `input #${vin} is not of type p2pkh: ${prevOutType}`, + ); } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); break; case 'p2pk': - if (prevOutType !== 'pubkey') { - throw new TypeError(`input #${vin} is not of type p2pk`); + if (prevOutType && prevOutType !== 'pubkey') { + throw new TypeError( + `input #${vin} is not of type p2pk: ${prevOutType}`, + ); } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); break; case 'p2wpkh': - if (prevOutType !== 'witnesspubkeyhash') { - throw new TypeError(`input #${vin} is not of type p2wpkh`); + if (prevOutType && prevOutType !== 'witnesspubkeyhash') { + throw new TypeError( + `input #${vin} is not of type p2wpkh: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.witnessScript); - typeforce(typeforce.Satoshi, signParams.witnessValue); - witnessScript = signParams.witnessScript; + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); witnessValue = signParams.witnessValue; break; case 'p2ms': - if (prevOutType !== 'multisig') { - throw new TypeError(`input #${vin} is not of type p2ms`); + if (prevOutType && prevOutType !== 'multisig') { + throw new TypeError( + `input #${vin} is not of type p2ms: ${prevOutType}`, + ); } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); break; case 'p2sh-p2wpkh': - if (prevOutType !== 'scripthash') { - throw new TypeError(`input #${vin} is not of type p2sh-p2wpkh`); + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${vin} is not of type p2sh-p2wpkh: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.witnessScript); - typeforce(typeforce.Buffer, signParams.redeemScript); - typeforce(typeforce.Satoshi, signParams.witnessValue); + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); witnessScript = signParams.witnessScript; redeemScript = signParams.redeemScript; witnessValue = signParams.witnessValue; break; case 'p2sh-p2ms': - if (prevOutType !== 'scripthash') { - throw new TypeError(`input #${vin} is not of type p2sh-p2ms`); + case 'p2sh-p2pk': + case 'p2sh-p2pkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${vin} is not of type ${posType}: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.redeemScript); + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); redeemScript = signParams.redeemScript; break; case 'p2wsh-p2ms': - if (prevOutType !== 'witnessscripthash') { - throw new TypeError(`input #${vin} is not of type p2wsh-p2ms`); + case 'p2wsh-p2pk': + case 'p2wsh-p2pkh': + if (prevOutType && prevOutType !== 'witnessscripthash') { + throw new TypeError( + `input #${vin} is not of type ${posType}: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.witnessScript); - typeforce(typeforce.Satoshi, signParams.witnessValue); + tfMessage( + typeforce.Buffer, + signParams.witnessScript, + `${posType} requires witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); witnessScript = signParams.witnessScript; witnessValue = signParams.witnessValue; break; case 'p2sh-p2wsh-p2ms': - if (prevOutType !== 'scripthash') { - throw new TypeError(`input #${vin} is not of type p2sh-p2wsh-p2ms`); + case 'p2sh-p2wsh-p2pk': + case 'p2sh-p2wsh-p2pkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${vin} is not of type ${posType}: ${prevOutType}`, + ); } - typeforce(typeforce.Buffer, signParams.witnessScript); - typeforce(typeforce.Buffer, signParams.redeemScript); - typeforce(typeforce.Satoshi, signParams.witnessValue); + tfMessage( + typeforce.Buffer, + signParams.witnessScript, + `${posType} requires witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires witnessScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessScript`, + ); witnessScript = signParams.witnessScript; redeemScript = signParams.redeemScript; witnessValue = signParams.witnessValue; @@ -318,6 +464,9 @@ export class TransactionBuilder { 'TransactionBuilder sign first arg must be TxbSignArg or number', ); } + if (keyPair === undefined) { + throw new Error('sign requires keypair'); + } // TODO: remove keyPair.network matching in 4.0.0 if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network'); @@ -391,7 +540,7 @@ export class TransactionBuilder { ); } - const signature = keyPair.sign(signatureHash, this.__USE_LOW_R); + const signature = keyPair!.sign(signatureHash, this.__USE_LOW_R); input.signatures![i] = bscript.signature.encode(signature, hashType!); return true; }); diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts index 9441d6b..d57e4eb 100644 --- a/types/transaction_builder.d.ts +++ b/types/transaction_builder.d.ts @@ -27,7 +27,7 @@ export declare class TransactionBuilder { addOutput(scriptPubKey: string | Buffer, value: number): number; build(): Transaction; buildIncomplete(): Transaction; - sign(signParams: number | TxbSignArg, keyPair: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void; + sign(signParams: number | TxbSignArg, keyPair?: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void; private __addInputUnsafe; private __build; private __canModifyInputs; From 053d282e9d31c31c3590094dc3c44e123dd070f8 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 13 Jun 2019 13:32:21 +0900 Subject: [PATCH 302/568] Add stricter Signer interface check --- src/transaction_builder.js | 2 +- src/types.js | 8 ++++++++ ts_src/transaction_builder.ts | 2 +- ts_src/types.ts | 8 ++++++++ types/types.d.ts | 1 + 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index f6a8625..098e6c1 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -166,7 +166,7 @@ class TransactionBuilder { `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, ); } - typeforce(typeforce.tuple(typeforce.Number, typeforce.Object), [ + typeforce(typeforce.tuple(typeforce.Number, types.Signer), [ signParams.vin, signParams.keyPair, ]); diff --git a/src/types.js b/src/types.js index be95266..8bcee2c 100644 --- a/src/types.js +++ b/src/types.js @@ -13,6 +13,14 @@ exports.BIP32Path = BIP32Path; BIP32Path.toJSON = () => { return 'BIP32 derivation path'; }; +function Signer(obj) { + return ( + (typeforce.Buffer(obj.publicKey) || + typeof obj.getPublicKey === 'function') && + typeof obj.sign === 'function' + ); +} +exports.Signer = Signer; const SATOSHI_MAX = 21 * 1e14; function Satoshi(value) { return typeforce.UInt53(value) && value <= SATOSHI_MAX; diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 33ea48b..a2ccc8b 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -256,7 +256,7 @@ export class TransactionBuilder { `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, ); } - typeforce(typeforce.tuple(typeforce.Number, typeforce.Object), [ + typeforce(typeforce.tuple(typeforce.Number, types.Signer), [ signParams.vin, signParams.keyPair, ]); diff --git a/ts_src/types.ts b/ts_src/types.ts index 06c247d..2e41267 100644 --- a/ts_src/types.ts +++ b/ts_src/types.ts @@ -12,6 +12,14 @@ BIP32Path.toJSON = (): string => { return 'BIP32 derivation path'; }; +export function Signer(obj: any): boolean { + return ( + (typeforce.Buffer(obj.publicKey) || + typeof obj.getPublicKey === 'function') && + typeof obj.sign === 'function' + ); +} + const SATOSHI_MAX: number = 21 * 1e14; export function Satoshi(value: number): boolean { return typeforce.UInt53(value) && value <= SATOSHI_MAX; diff --git a/types/types.d.ts b/types/types.d.ts index 242bab8..e7c588d 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -3,6 +3,7 @@ export declare function BIP32Path(value: string): boolean; export declare namespace BIP32Path { var toJSON: () => string; } +export declare function Signer(obj: any): boolean; export declare function Satoshi(value: number): boolean; export declare const ECPoint: any; export declare const Network: any; From 2e4c9f6b7aba9f6bb27f59f2f765400729045655 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 13 Jun 2019 15:04:11 +0900 Subject: [PATCH 303/568] Remove checks to outer function --- src/transaction_builder.js | 411 ++++++++++++++++----------------- ts_src/transaction_builder.ts | 412 +++++++++++++++++----------------- 2 files changed, 413 insertions(+), 410 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 098e6c1..37768a0 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -161,214 +161,13 @@ class TransactionBuilder { ); vin = signParams; } else if (typeof signParams === 'object') { - if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { - throw new TypeError( - `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, - ); - } - typeforce(typeforce.tuple(typeforce.Number, types.Signer), [ - signParams.vin, - signParams.keyPair, - ]); + checkSignArgs(this, signParams); vin = signParams.vin; keyPair = signParams.keyPair; + redeemScript = signParams.redeemScript; hashType = signParams.hashType; - const prevOutType = (this.__INPUTS[vin] || []).prevOutType; - const posType = signParams.prevOutScriptType; - switch (posType) { - case 'p2pkh': - if (prevOutType && prevOutType !== 'pubkeyhash') { - throw new TypeError( - `input #${vin} is not of type p2pkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2pk': - if (prevOutType && prevOutType !== 'pubkey') { - throw new TypeError( - `input #${vin} is not of type p2pk: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2wpkh': - if (prevOutType && prevOutType !== 'witnesspubkeyhash') { - throw new TypeError( - `input #${vin} is not of type p2wpkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - witnessValue = signParams.witnessValue; - break; - case 'p2ms': - if (prevOutType && prevOutType !== 'multisig') { - throw new TypeError( - `input #${vin} is not of type p2ms: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2sh-p2wpkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${vin} is not of type p2sh-p2wpkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - witnessScript = signParams.witnessScript; - redeemScript = signParams.redeemScript; - witnessValue = signParams.witnessValue; - break; - case 'p2sh-p2ms': - case 'p2sh-p2pk': - case 'p2sh-p2pkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - redeemScript = signParams.redeemScript; - break; - case 'p2wsh-p2ms': - case 'p2wsh-p2pk': - case 'p2wsh-p2pkh': - if (prevOutType && prevOutType !== 'witnessscripthash') { - throw new TypeError( - `input #${vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.Buffer, - signParams.witnessScript, - `${posType} requires witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - witnessScript = signParams.witnessScript; - witnessValue = signParams.witnessValue; - break; - case 'p2sh-p2wsh-p2ms': - case 'p2sh-p2wsh-p2pk': - case 'p2sh-p2wsh-p2pkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.Buffer, - signParams.witnessScript, - `${posType} requires witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires witnessScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessScript`, - ); - witnessScript = signParams.witnessScript; - redeemScript = signParams.redeemScript; - witnessValue = signParams.witnessValue; - break; - } + witnessValue = signParams.witnessValue; + witnessScript = signParams.witnessScript; } else { throw new TypeError( 'TransactionBuilder sign first arg must be TxbSignArg or number', @@ -1004,3 +803,205 @@ function canSign(input) { function signatureHashType(buffer) { return buffer.readUInt8(buffer.length - 1); } +function checkSignArgs(txb, signParams) { + if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { + throw new TypeError( + `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, + ); + } + typeforce( + typeforce.tuple( + typeforce.Number, + typeforce.maybe(typeforce.Number), + types.Signer, + ), + [signParams.vin, signParams.hashType, signParams.keyPair], + ); + // @ts-ignore + const prevOutType = (txb.__INPUTS[signParams.vin] || []).prevOutType; + const posType = signParams.prevOutScriptType; + switch (posType) { + case 'p2pkh': + if (prevOutType && prevOutType !== 'pubkeyhash') { + throw new TypeError( + `input #${signParams.vin} is not of type p2pkh: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); + break; + case 'p2pk': + if (prevOutType && prevOutType !== 'pubkey') { + throw new TypeError( + `input #${signParams.vin} is not of type p2pk: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); + break; + case 'p2wpkh': + if (prevOutType && prevOutType !== 'witnesspubkeyhash') { + throw new TypeError( + `input #${signParams.vin} is not of type p2wpkh: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); + break; + case 'p2ms': + if (prevOutType && prevOutType !== 'multisig') { + throw new TypeError( + `input #${signParams.vin} is not of type p2ms: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); + break; + case 'p2sh-p2wpkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${signParams.vin} is not of type p2sh-p2wpkh: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); + break; + case 'p2sh-p2ms': + case 'p2sh-p2pk': + case 'p2sh-p2pkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); + break; + case 'p2wsh-p2ms': + case 'p2wsh-p2pk': + case 'p2wsh-p2pkh': + if (prevOutType && prevOutType !== 'witnessscripthash') { + throw new TypeError( + `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, + ); + } + tfMessage( + typeforce.Buffer, + signParams.witnessScript, + `${posType} requires witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); + break; + case 'p2sh-p2wsh-p2ms': + case 'p2sh-p2wsh-p2pk': + case 'p2sh-p2wsh-p2pkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, + ); + } + tfMessage( + typeforce.Buffer, + signParams.witnessScript, + `${posType} requires witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires witnessScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessScript`, + ); + break; + } +} diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index a2ccc8b..f4fd644 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -251,214 +251,13 @@ export class TransactionBuilder { ); vin = signParams; } else if (typeof signParams === 'object') { - if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { - throw new TypeError( - `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, - ); - } - typeforce(typeforce.tuple(typeforce.Number, types.Signer), [ - signParams.vin, - signParams.keyPair, - ]); + checkSignArgs(this, signParams); vin = signParams.vin; keyPair = signParams.keyPair; + redeemScript = signParams.redeemScript; hashType = signParams.hashType; - const prevOutType = (this.__INPUTS[vin] || []).prevOutType; - const posType = signParams.prevOutScriptType; - switch (posType) { - case 'p2pkh': - if (prevOutType && prevOutType !== 'pubkeyhash') { - throw new TypeError( - `input #${vin} is not of type p2pkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2pk': - if (prevOutType && prevOutType !== 'pubkey') { - throw new TypeError( - `input #${vin} is not of type p2pk: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2wpkh': - if (prevOutType && prevOutType !== 'witnesspubkeyhash') { - throw new TypeError( - `input #${vin} is not of type p2wpkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - witnessValue = signParams.witnessValue; - break; - case 'p2ms': - if (prevOutType && prevOutType !== 'multisig') { - throw new TypeError( - `input #${vin} is not of type p2ms: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2sh-p2wpkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${vin} is not of type p2sh-p2wpkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - witnessScript = signParams.witnessScript; - redeemScript = signParams.redeemScript; - witnessValue = signParams.witnessValue; - break; - case 'p2sh-p2ms': - case 'p2sh-p2pk': - case 'p2sh-p2pkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - redeemScript = signParams.redeemScript; - break; - case 'p2wsh-p2ms': - case 'p2wsh-p2pk': - case 'p2wsh-p2pkh': - if (prevOutType && prevOutType !== 'witnessscripthash') { - throw new TypeError( - `input #${vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.Buffer, - signParams.witnessScript, - `${posType} requires witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - witnessScript = signParams.witnessScript; - witnessValue = signParams.witnessValue; - break; - case 'p2sh-p2wsh-p2ms': - case 'p2sh-p2wsh-p2pk': - case 'p2sh-p2wsh-p2pkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.Buffer, - signParams.witnessScript, - `${posType} requires witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires witnessScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessScript`, - ); - witnessScript = signParams.witnessScript; - redeemScript = signParams.redeemScript; - witnessValue = signParams.witnessValue; - break; - } + witnessValue = signParams.witnessValue; + witnessScript = signParams.witnessScript; } else { throw new TypeError( 'TransactionBuilder sign first arg must be TxbSignArg or number', @@ -1239,3 +1038,206 @@ function canSign(input: TxbInput): boolean { function signatureHashType(buffer: Buffer): number { return buffer.readUInt8(buffer.length - 1); } + +function checkSignArgs(txb: TransactionBuilder, signParams: TxbSignArg): void { + if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { + throw new TypeError( + `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, + ); + } + typeforce( + typeforce.tuple( + typeforce.Number, + typeforce.maybe(typeforce.Number), + types.Signer, + ), + [signParams.vin, signParams.hashType, signParams.keyPair], + ); + // @ts-ignore + const prevOutType = (txb.__INPUTS[signParams.vin] || []).prevOutType; + const posType = signParams.prevOutScriptType; + switch (posType) { + case 'p2pkh': + if (prevOutType && prevOutType !== 'pubkeyhash') { + throw new TypeError( + `input #${signParams.vin} is not of type p2pkh: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); + break; + case 'p2pk': + if (prevOutType && prevOutType !== 'pubkey') { + throw new TypeError( + `input #${signParams.vin} is not of type p2pk: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); + break; + case 'p2wpkh': + if (prevOutType && prevOutType !== 'witnesspubkeyhash') { + throw new TypeError( + `input #${signParams.vin} is not of type p2wpkh: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); + break; + case 'p2ms': + if (prevOutType && prevOutType !== 'multisig') { + throw new TypeError( + `input #${signParams.vin} is not of type p2ms: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); + break; + case 'p2sh-p2wpkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${signParams.vin} is not of type p2sh-p2wpkh: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); + break; + case 'p2sh-p2ms': + case 'p2sh-p2pk': + case 'p2sh-p2pkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, + ); + } + tfMessage( + typeforce.value(undefined), + signParams.witnessScript, + `${posType} requires NO witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires redeemScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.witnessValue, + `${posType} requires NO witnessValue`, + ); + break; + case 'p2wsh-p2ms': + case 'p2wsh-p2pk': + case 'p2wsh-p2pkh': + if (prevOutType && prevOutType !== 'witnessscripthash') { + throw new TypeError( + `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, + ); + } + tfMessage( + typeforce.Buffer, + signParams.witnessScript, + `${posType} requires witnessScript`, + ); + tfMessage( + typeforce.value(undefined), + signParams.redeemScript, + `${posType} requires NO redeemScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessValue`, + ); + break; + case 'p2sh-p2wsh-p2ms': + case 'p2sh-p2wsh-p2pk': + case 'p2sh-p2wsh-p2pkh': + if (prevOutType && prevOutType !== 'scripthash') { + throw new TypeError( + `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, + ); + } + tfMessage( + typeforce.Buffer, + signParams.witnessScript, + `${posType} requires witnessScript`, + ); + tfMessage( + typeforce.Buffer, + signParams.redeemScript, + `${posType} requires witnessScript`, + ); + tfMessage( + types.Satoshi, + signParams.witnessValue, + `${posType} requires witnessScript`, + ); + break; + } +} From 7c454e5f4495973267187164ee952fdc35691cee Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 13 Jun 2019 17:08:43 +0900 Subject: [PATCH 304/568] TIL: destructuring objects without let or const is a thing --- src/transaction_builder.js | 14 ++++++++------ ts_src/transaction_builder.ts | 14 ++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 37768a0..06cb6b8 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -162,12 +162,14 @@ class TransactionBuilder { vin = signParams; } else if (typeof signParams === 'object') { checkSignArgs(this, signParams); - vin = signParams.vin; - keyPair = signParams.keyPair; - redeemScript = signParams.redeemScript; - hashType = signParams.hashType; - witnessValue = signParams.witnessValue; - witnessScript = signParams.witnessScript; + ({ + vin, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, + } = signParams); } else { throw new TypeError( 'TransactionBuilder sign first arg must be TxbSignArg or number', diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index f4fd644..ccf91cf 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -252,12 +252,14 @@ export class TransactionBuilder { vin = signParams; } else if (typeof signParams === 'object') { checkSignArgs(this, signParams); - vin = signParams.vin; - keyPair = signParams.keyPair; - redeemScript = signParams.redeemScript; - hashType = signParams.hashType; - witnessValue = signParams.witnessValue; - witnessScript = signParams.witnessScript; + ({ + vin, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, + } = signParams); } else { throw new TypeError( 'TransactionBuilder sign first arg must be TxbSignArg or number', From ee3150d7c719ebffd020907980d171c67d9e0cf2 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 14 Jun 2019 11:47:40 +0900 Subject: [PATCH 305/568] Refactor sign for clarity --- src/transaction_builder.js | 223 ++++++++++++++++------------ ts_src/transaction_builder.ts | 264 +++++++++++++++++++++------------- 2 files changed, 293 insertions(+), 194 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 06cb6b8..4f16b41 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -153,98 +153,25 @@ class TransactionBuilder { witnessValue, witnessScript, ) { - let vin; - if (typeof signParams === 'number') { - console.warn( - 'DEPRECATED: TransactionBuilder sign method arguments ' + - 'will change in v6, please use the TxbSignArg interface', - ); - vin = signParams; - } else if (typeof signParams === 'object') { - checkSignArgs(this, signParams); - ({ - vin, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - } = signParams); - } else { - throw new TypeError( - 'TransactionBuilder sign first arg must be TxbSignArg or number', - ); - } - if (keyPair === undefined) { - throw new Error('sign requires keypair'); - } - // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== this.network) - throw new TypeError('Inconsistent network'); - if (!this.__INPUTS[vin]) throw new Error('No input at index: ' + vin); - hashType = hashType || transaction_1.Transaction.SIGHASH_ALL; - if (this.__needsOutputs(hashType)) - throw new Error('Transaction needs outputs'); - const input = this.__INPUTS[vin]; - // if redeemScript was previously provided, enforce consistency - if ( - input.redeemScript !== undefined && - redeemScript && - !input.redeemScript.equals(redeemScript) - ) { - throw new Error('Inconsistent redeemScript'); - } - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey(); - if (!canSign(input)) { - if (witnessValue !== undefined) { - if (input.value !== undefined && input.value !== witnessValue) - throw new Error('Input did not match witnessValue'); - typeforce(types.Satoshi, witnessValue); - input.value = witnessValue; - } - if (!canSign(input)) { - const prepared = prepareInput( - input, - ourPubKey, - redeemScript, - witnessScript, - ); - // updates inline - Object.assign(input, prepared); - } - if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); - } - // ready to sign - let signatureHash; - if (input.hasWitness) { - signatureHash = this.__TX.hashForWitnessV0( - vin, - input.signScript, - input.value, - hashType, - ); - } else { - signatureHash = this.__TX.hashForSignature( - vin, - input.signScript, - hashType, - ); - } - // enforce in order signing of public keys - const signed = input.pubkeys.some((pubKey, i) => { - if (!ourPubKey.equals(pubKey)) return false; - if (input.signatures[i]) throw new Error('Signature already exists'); - // TODO: add tests - if (ourPubKey.length !== 33 && input.hasWitness) { - throw new Error( - 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', - ); - } - const signature = keyPair.sign(signatureHash, this.__USE_LOW_R); - input.signatures[i] = bscript.signature.encode(signature, hashType); - return true; - }); - if (!signed) throw new Error('Key pair cannot sign for this input'); + const data = getSigningData( + this, + signParams, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, + ); + const { input, ourPubKey, signatureHash } = data; + ({ keyPair, hashType } = data); + trySign( + input, + ourPubKey, + keyPair, + signatureHash, + hashType, + this.__USE_LOW_R, + ); } __addInputUnsafe(txHash, vout, options) { if (transaction_1.Transaction.isCoinbaseHash(txHash)) { @@ -1007,3 +934,115 @@ function checkSignArgs(txb, signParams) { break; } } +function trySign(input, ourPubKey, keyPair, signatureHash, hashType, useLowR) { + // enforce in order signing of public keys + const signed = input.pubkeys.some((pubKey, i) => { + if (!ourPubKey.equals(pubKey)) return false; + if (input.signatures[i]) throw new Error('Signature already exists'); + // TODO: add tests + if (ourPubKey.length !== 33 && input.hasWitness) { + throw new Error( + 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', + ); + } + const signature = keyPair.sign(signatureHash, useLowR); + input.signatures[i] = bscript.signature.encode(signature, hashType); + return true; + }); + if (!signed) throw new Error('Key pair cannot sign for this input'); +} +function getSigningData( + txb, + signParams, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, +) { + let vin; + if (typeof signParams === 'number') { + console.warn( + 'DEPRECATED: TransactionBuilder sign method arguments ' + + 'will change in v6, please use the TxbSignArg interface', + ); + vin = signParams; + } else if (typeof signParams === 'object') { + checkSignArgs(txb, signParams); + ({ + vin, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, + } = signParams); + } else { + throw new TypeError( + 'TransactionBuilder sign first arg must be TxbSignArg or number', + ); + } + if (keyPair === undefined) { + throw new Error('sign requires keypair'); + } + // TODO: remove keyPair.network matching in 4.0.0 + if (keyPair.network && keyPair.network !== txb.network) + throw new TypeError('Inconsistent network'); + // @ts-ignore + if (!txb.__INPUTS[vin]) throw new Error('No input at index: ' + vin); + hashType = hashType || transaction_1.Transaction.SIGHASH_ALL; + // @ts-ignore + if (txb.__needsOutputs(hashType)) + throw new Error('Transaction needs outputs'); + // @ts-ignore + const input = txb.__INPUTS[vin]; + // if redeemScript was previously provided, enforce consistency + if ( + input.redeemScript !== undefined && + redeemScript && + !input.redeemScript.equals(redeemScript) + ) { + throw new Error('Inconsistent redeemScript'); + } + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey(); + if (!canSign(input)) { + if (witnessValue !== undefined) { + if (input.value !== undefined && input.value !== witnessValue) + throw new Error('Input did not match witnessValue'); + typeforce(types.Satoshi, witnessValue); + input.value = witnessValue; + } + if (!canSign(input)) { + const prepared = prepareInput( + input, + ourPubKey, + redeemScript, + witnessScript, + ); + // updates inline + Object.assign(input, prepared); + } + if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); + } + // ready to sign + let signatureHash; + if (input.hasWitness) { + // @ts-ignore + signatureHash = txb.__TX.hashForWitnessV0( + vin, + input.signScript, + input.value, + hashType, + ); + } else { + // @ts-ignore + signatureHash = txb.__TX.hashForSignature(vin, input.signScript, hashType); + } + return { + input, + ourPubKey, + keyPair, + signatureHash, + hashType, + }; +} diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index ccf91cf..7af207b 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -243,110 +243,27 @@ export class TransactionBuilder { witnessValue?: number, witnessScript?: Buffer, ): void { - let vin: number; - if (typeof signParams === 'number') { - console.warn( - 'DEPRECATED: TransactionBuilder sign method arguments ' + - 'will change in v6, please use the TxbSignArg interface', - ); - vin = signParams; - } else if (typeof signParams === 'object') { - checkSignArgs(this, signParams); - ({ - vin, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - } = signParams); - } else { - throw new TypeError( - 'TransactionBuilder sign first arg must be TxbSignArg or number', - ); - } - if (keyPair === undefined) { - throw new Error('sign requires keypair'); - } - // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== this.network) - throw new TypeError('Inconsistent network'); - if (!this.__INPUTS[vin]) throw new Error('No input at index: ' + vin); + const data = getSigningData( + this, + signParams, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, + ); - hashType = hashType || Transaction.SIGHASH_ALL; - if (this.__needsOutputs(hashType)) - throw new Error('Transaction needs outputs'); + const { input, ourPubKey, signatureHash } = data; + ({ keyPair, hashType } = data); - const input = this.__INPUTS[vin]; - - // if redeemScript was previously provided, enforce consistency - if ( - input.redeemScript !== undefined && - redeemScript && - !input.redeemScript.equals(redeemScript) - ) { - throw new Error('Inconsistent redeemScript'); - } - - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!(); - if (!canSign(input)) { - if (witnessValue !== undefined) { - if (input.value !== undefined && input.value !== witnessValue) - throw new Error('Input did not match witnessValue'); - typeforce(types.Satoshi, witnessValue); - input.value = witnessValue; - } - - if (!canSign(input)) { - const prepared = prepareInput( - input, - ourPubKey, - redeemScript, - witnessScript, - ); - - // updates inline - Object.assign(input, prepared); - } - - if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); - } - - // ready to sign - let signatureHash: Buffer; - if (input.hasWitness) { - signatureHash = this.__TX.hashForWitnessV0( - vin, - input.signScript as Buffer, - input.value as number, - hashType, - ); - } else { - signatureHash = this.__TX.hashForSignature( - vin, - input.signScript as Buffer, - hashType, - ); - } - - // enforce in order signing of public keys - const signed = input.pubkeys!.some((pubKey, i) => { - if (!ourPubKey.equals(pubKey!)) return false; - if (input.signatures![i]) throw new Error('Signature already exists'); - - // TODO: add tests - if (ourPubKey.length !== 33 && input.hasWitness) { - throw new Error( - 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', - ); - } - - const signature = keyPair!.sign(signatureHash, this.__USE_LOW_R); - input.signatures![i] = bscript.signature.encode(signature, hashType!); - return true; - }); - - if (!signed) throw new Error('Key pair cannot sign for this input'); + trySign( + input, + ourPubKey, + keyPair, + signatureHash, + hashType, + this.__USE_LOW_R, + ); } private __addInputUnsafe( @@ -1243,3 +1160,146 @@ function checkSignArgs(txb: TransactionBuilder, signParams: TxbSignArg): void { break; } } + +function trySign( + input: TxbInput, + ourPubKey: Buffer, + keyPair: ECPairInterface, + signatureHash: Buffer, + hashType: number, + useLowR: boolean, +): void { + // enforce in order signing of public keys + const signed = input.pubkeys!.some((pubKey, i) => { + if (!ourPubKey.equals(pubKey!)) return false; + if (input.signatures![i]) throw new Error('Signature already exists'); + + // TODO: add tests + if (ourPubKey.length !== 33 && input.hasWitness) { + throw new Error( + 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', + ); + } + + const signature = keyPair.sign(signatureHash, useLowR); + input.signatures![i] = bscript.signature.encode(signature, hashType); + return true; + }); + + if (!signed) throw new Error('Key pair cannot sign for this input'); +} + +function getSigningData( + txb: TransactionBuilder, + signParams: number | TxbSignArg, + keyPair?: ECPairInterface, + redeemScript?: Buffer, + hashType?: number, + witnessValue?: number, + witnessScript?: Buffer, +): { + input: TxbInput; + ourPubKey: Buffer; + keyPair: ECPairInterface; + signatureHash: Buffer; + hashType: number; +} { + let vin: number; + if (typeof signParams === 'number') { + console.warn( + 'DEPRECATED: TransactionBuilder sign method arguments ' + + 'will change in v6, please use the TxbSignArg interface', + ); + vin = signParams; + } else if (typeof signParams === 'object') { + checkSignArgs(txb, signParams); + ({ + vin, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, + } = signParams); + } else { + throw new TypeError( + 'TransactionBuilder sign first arg must be TxbSignArg or number', + ); + } + if (keyPair === undefined) { + throw new Error('sign requires keypair'); + } + // TODO: remove keyPair.network matching in 4.0.0 + if (keyPair.network && keyPair.network !== txb.network) + throw new TypeError('Inconsistent network'); + // @ts-ignore + if (!txb.__INPUTS[vin]) throw new Error('No input at index: ' + vin); + + hashType = hashType || Transaction.SIGHASH_ALL; + // @ts-ignore + if (txb.__needsOutputs(hashType)) + throw new Error('Transaction needs outputs'); + + // @ts-ignore + const input = txb.__INPUTS[vin]; + + // if redeemScript was previously provided, enforce consistency + if ( + input.redeemScript !== undefined && + redeemScript && + !input.redeemScript.equals(redeemScript) + ) { + throw new Error('Inconsistent redeemScript'); + } + + const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!(); + if (!canSign(input)) { + if (witnessValue !== undefined) { + if (input.value !== undefined && input.value !== witnessValue) + throw new Error('Input did not match witnessValue'); + typeforce(types.Satoshi, witnessValue); + input.value = witnessValue; + } + + if (!canSign(input)) { + const prepared = prepareInput( + input, + ourPubKey, + redeemScript, + witnessScript, + ); + + // updates inline + Object.assign(input, prepared); + } + + if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); + } + + // ready to sign + let signatureHash: Buffer; + if (input.hasWitness) { + // @ts-ignore + signatureHash = txb.__TX.hashForWitnessV0( + vin, + input.signScript as Buffer, + input.value as number, + hashType, + ); + } else { + // @ts-ignore + signatureHash = txb.__TX.hashForSignature( + vin, + input.signScript as Buffer, + hashType, + ); + } + + return { + input, + ourPubKey, + keyPair, + signatureHash, + hashType, + }; +} From 4bed585f6a33c39620961b2a5c0535145a6286e9 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 14 Jun 2019 11:55:34 +0900 Subject: [PATCH 306/568] Test old args as well --- test/transaction_builder.js | 1107 ++++++++++++++++++----------------- 1 file changed, 558 insertions(+), 549 deletions(-) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 0bf2d88..3615d28 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -11,7 +11,7 @@ const NETWORKS = require('../src/networks') const fixtures = require('./fixtures/transaction_builder') -function constructSign (f, txb) { +function constructSign (f, txb, useOldSignArgs) { const network = NETWORKS[f.network] const stages = f.stages && f.stages.concat() @@ -35,15 +35,22 @@ function constructSign (f, txb) { witnessScript = bscript.fromASM(sign.witnessScript) } - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: index, - keyPair, - redeemScript, - hashType: sign.hashType, - witnessValue, - witnessScript, - }) + if (useOldSignArgs) { + // DEPRECATED: v6 will remove this interface + txb.sign(index, keyPair, redeemScript, sign.hashType, witnessValue, witnessScript) + } else { + // prevOutScriptType is required, see /ts_src/transaction_builder.ts + // The PREVOUT_TYPES constant is a Set with all possible values. + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: index, + keyPair, + redeemScript, + hashType: sign.hashType, + witnessValue, + witnessScript, + }) + } if (sign.stage) { const tx = txb.buildIncomplete() @@ -56,7 +63,7 @@ function constructSign (f, txb) { return txb } -function construct (f, dontSign) { +function construct (f, dontSign, useOldSignArgs) { const network = NETWORKS[f.network] const txb = new TransactionBuilder(network) @@ -92,345 +99,358 @@ function construct (f, dontSign) { }) if (dontSign) return txb - return constructSign(f, txb) + return constructSign(f, txb, useOldSignArgs) } -describe('TransactionBuilder', () => { - // constants - const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) - const scripts = [ - '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', - '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP' - ].map(x => { - return baddress.toOutputScript(x) - }) - const txHash = Buffer.from('0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', 'hex') - - describe('fromTransaction', () => { - fixtures.valid.build.forEach(f => { - it('returns TransactionBuilder, with ' + f.description, () => { - const network = NETWORKS[f.network || 'bitcoin'] - - const tx = Transaction.fromHex(f.txHex) - const txb = TransactionBuilder.fromTransaction(tx, network) - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() - - assert.strictEqual(txAfter.toHex(), f.txHex) - assert.strictEqual(txb.network, network) - }) +for (const useOldSignArgs of [ false, true ]) { + describe(`TransactionBuilder: useOldSignArgs === ${useOldSignArgs}`, () => { + // constants + const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) + const scripts = [ + '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', + '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP' + ].map(x => { + return baddress.toOutputScript(x) }) + const txHash = Buffer.from('0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', 'hex') - fixtures.valid.fromTransaction.forEach(f => { - it('returns TransactionBuilder, with ' + f.description, () => { - const tx = new Transaction() + describe('fromTransaction', () => { + fixtures.valid.build.forEach(f => { + it('returns TransactionBuilder, with ' + f.description, () => { + const network = NETWORKS[f.network || 'bitcoin'] - f.inputs.forEach(input => { - const txHash2 = Buffer.from(input.txId, 'hex').reverse() + const tx = Transaction.fromHex(f.txHex) + const txb = TransactionBuilder.fromTransaction(tx, network) + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() - tx.addInput(txHash2, input.vout, undefined, bscript.fromASM(input.scriptSig)) + assert.strictEqual(txAfter.toHex(), f.txHex) + assert.strictEqual(txb.network, network) }) + }) - f.outputs.forEach(output => { - tx.addOutput(bscript.fromASM(output.script), output.value) + fixtures.valid.fromTransaction.forEach(f => { + it('returns TransactionBuilder, with ' + f.description, () => { + const tx = new Transaction() + + f.inputs.forEach(input => { + const txHash2 = Buffer.from(input.txId, 'hex').reverse() + + tx.addInput(txHash2, input.vout, undefined, bscript.fromASM(input.scriptSig)) + }) + + f.outputs.forEach(output => { + tx.addOutput(bscript.fromASM(output.script), output.value) + }) + + const txb = TransactionBuilder.fromTransaction(tx) + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + + txAfter.ins.forEach((input, i) => { + assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) + }) + + txAfter.outs.forEach((output, i) => { + assert.strictEqual(bscript.toASM(output.script), f.outputs[i].script) + }) }) + }) + fixtures.valid.fromTransactionSequential.forEach(f => { + it('with ' + f.description, () => { + const network = NETWORKS[f.network] + const tx = Transaction.fromHex(f.txHex) + const txb = TransactionBuilder.fromTransaction(tx, network) + + tx.ins.forEach((input, i) => { + assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSig) + }) + + constructSign(f, txb, useOldSignArgs) + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + + txAfter.ins.forEach((input, i) => { + assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) + }) + + assert.strictEqual(txAfter.toHex(), f.txHexAfter) + }) + }) + + it('classifies transaction inputs', () => { + const tx = Transaction.fromHex(fixtures.valid.classification.hex) const txb = TransactionBuilder.fromTransaction(tx) - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() - txAfter.ins.forEach((input, i) => { - assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) + txb.__INPUTS.forEach(i => { + assert.strictEqual(i.prevOutType, 'scripthash') + assert.strictEqual(i.redeemScriptType, 'multisig') }) + }) - txAfter.outs.forEach((output, i) => { - assert.strictEqual(bscript.toASM(output.script), f.outputs[i].script) + fixtures.invalid.fromTransaction.forEach(f => { + it('throws ' + f.exception, () => { + const tx = Transaction.fromHex(f.txHex) + + assert.throws(() => { + TransactionBuilder.fromTransaction(tx) + }, new RegExp(f.exception)) }) }) }) - fixtures.valid.fromTransactionSequential.forEach(f => { - it('with ' + f.description, () => { - const network = NETWORKS[f.network] - const tx = Transaction.fromHex(f.txHex) - const txb = TransactionBuilder.fromTransaction(tx, network) - - tx.ins.forEach((input, i) => { - assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSig) - }) - - constructSign(f, txb) - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() - - txAfter.ins.forEach((input, i) => { - assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) - }) - - assert.strictEqual(txAfter.toHex(), f.txHexAfter) + describe('addInput', () => { + let txb + beforeEach(() => { + txb = new TransactionBuilder() }) - }) - it('classifies transaction inputs', () => { - const tx = Transaction.fromHex(fixtures.valid.classification.hex) - const txb = TransactionBuilder.fromTransaction(tx) + it('accepts a txHash, index [and sequence number]', () => { + const vin = txb.addInput(txHash, 1, 54) + assert.strictEqual(vin, 0) - txb.__INPUTS.forEach(i => { - assert.strictEqual(i.prevOutType, 'scripthash') - assert.strictEqual(i.redeemScriptType, 'multisig') + const txIn = txb.__TX.ins[0] + assert.strictEqual(txIn.hash, txHash) + assert.strictEqual(txIn.index, 1) + assert.strictEqual(txIn.sequence, 54) + assert.strictEqual(txb.__INPUTS[0].prevOutScript, undefined) }) - }) - fixtures.invalid.fromTransaction.forEach(f => { - it('throws ' + f.exception, () => { - const tx = Transaction.fromHex(f.txHex) + it('accepts a txHash, index [, sequence number and scriptPubKey]', () => { + const vin = txb.addInput(txHash, 1, 54, scripts[1]) + assert.strictEqual(vin, 0) + + const txIn = txb.__TX.ins[0] + assert.strictEqual(txIn.hash, txHash) + assert.strictEqual(txIn.index, 1) + assert.strictEqual(txIn.sequence, 54) + assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) + }) + + it('accepts a prevTx, index [and sequence number]', () => { + const prevTx = new Transaction() + prevTx.addOutput(scripts[0], 0) + prevTx.addOutput(scripts[1], 1) + + const vin = txb.addInput(prevTx, 1, 54) + assert.strictEqual(vin, 0) + + const txIn = txb.__TX.ins[0] + assert.deepStrictEqual(txIn.hash, prevTx.getHash()) + assert.strictEqual(txIn.index, 1) + assert.strictEqual(txIn.sequence, 54) + assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) + }) + + it('returns the input index', () => { + assert.strictEqual(txb.addInput(txHash, 0), 0) + assert.strictEqual(txb.addInput(txHash, 1), 1) + }) + + it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { + txb.addInput(txHash, 0) + txb.addOutput(scripts[0], 1000) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) assert.throws(() => { - TransactionBuilder.fromTransaction(tx) - }, new RegExp(f.exception)) + txb.addInput(txHash, 0) + }, /No, this would invalidate signatures/) }) }) - }) - describe('addInput', () => { - let txb - beforeEach(() => { - txb = new TransactionBuilder() - }) - - it('accepts a txHash, index [and sequence number]', () => { - const vin = txb.addInput(txHash, 1, 54) - assert.strictEqual(vin, 0) - - const txIn = txb.__TX.ins[0] - assert.strictEqual(txIn.hash, txHash) - assert.strictEqual(txIn.index, 1) - assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__INPUTS[0].prevOutScript, undefined) - }) - - it('accepts a txHash, index [, sequence number and scriptPubKey]', () => { - const vin = txb.addInput(txHash, 1, 54, scripts[1]) - assert.strictEqual(vin, 0) - - const txIn = txb.__TX.ins[0] - assert.strictEqual(txIn.hash, txHash) - assert.strictEqual(txIn.index, 1) - assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) - }) - - it('accepts a prevTx, index [and sequence number]', () => { - const prevTx = new Transaction() - prevTx.addOutput(scripts[0], 0) - prevTx.addOutput(scripts[1], 1) - - const vin = txb.addInput(prevTx, 1, 54) - assert.strictEqual(vin, 0) - - const txIn = txb.__TX.ins[0] - assert.deepStrictEqual(txIn.hash, prevTx.getHash()) - assert.strictEqual(txIn.index, 1) - assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) - }) - - it('returns the input index', () => { - assert.strictEqual(txb.addInput(txHash, 0), 0) - assert.strictEqual(txb.addInput(txHash, 1), 1) - }) - - it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 1000) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, + describe('addOutput', () => { + let txb + beforeEach(() => { + txb = new TransactionBuilder() }) - assert.throws(() => { + it('accepts an address string and value', () => { + const { address } = payments.p2pkh({ pubkey: keyPair.publicKey }) + const vout = txb.addOutput(address, 1000) + assert.strictEqual(vout, 0) + + const txout = txb.__TX.outs[0] + assert.deepStrictEqual(txout.script, scripts[0]) + assert.strictEqual(txout.value, 1000) + }) + + it('accepts a ScriptPubKey and value', () => { + const vout = txb.addOutput(scripts[0], 1000) + assert.strictEqual(vout, 0) + + const txout = txb.__TX.outs[0] + assert.deepStrictEqual(txout.script, scripts[0]) + assert.strictEqual(txout.value, 1000) + }) + + it('throws if address is of the wrong network', () => { + assert.throws(() => { + txb.addOutput('2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9', 1000) + }, /2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9 has no matching Script/) + }) + + it('add second output after signed first input with SIGHASH_NONE', () => { txb.addInput(txHash, 0) - }, /No, this would invalidate signatures/) - }) - }) - - describe('addOutput', () => { - let txb - beforeEach(() => { - txb = new TransactionBuilder() - }) - - it('accepts an address string and value', () => { - const { address } = payments.p2pkh({ pubkey: keyPair.publicKey }) - const vout = txb.addOutput(address, 1000) - assert.strictEqual(vout, 0) - - const txout = txb.__TX.outs[0] - assert.deepStrictEqual(txout.script, scripts[0]) - assert.strictEqual(txout.value, 1000) - }) - - it('accepts a ScriptPubKey and value', () => { - const vout = txb.addOutput(scripts[0], 1000) - assert.strictEqual(vout, 0) - - const txout = txb.__TX.outs[0] - assert.deepStrictEqual(txout.script, scripts[0]) - assert.strictEqual(txout.value, 1000) - }) - - it('throws if address is of the wrong network', () => { - assert.throws(() => { - txb.addOutput('2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9', 1000) - }, /2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9 has no matching Script/) - }) - - it('add second output after signed first input with SIGHASH_NONE', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 2000) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - hashType: Transaction.SIGHASH_NONE, - }) - assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) - }) - - it('add first output after signed first input with SIGHASH_NONE', () => { - txb.addInput(txHash, 0) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - hashType: Transaction.SIGHASH_NONE, - }) - assert.strictEqual(txb.addOutput(scripts[0], 2000), 0) - }) - - it('add second output after signed first input with SIGHASH_SINGLE', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 2000) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - hashType: Transaction.SIGHASH_SINGLE, - }) - assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) - }) - - it('add first output after signed first input with SIGHASH_SINGLE', () => { - txb.addInput(txHash, 0) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - hashType: Transaction.SIGHASH_SINGLE, - }) - assert.throws(() => { txb.addOutput(scripts[0], 2000) - }, /No, this would invalidate signatures/) - }) - - it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 2000) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + hashType: Transaction.SIGHASH_NONE, + }) + assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) }) - assert.throws(() => { - txb.addOutput(scripts[1], 9000) - }, /No, this would invalidate signatures/) - }) - }) - - describe('setLockTime', () => { - it('throws if if there exist any scriptSigs', () => { - const txb = new TransactionBuilder() - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 100) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, + it('add first output after signed first input with SIGHASH_NONE', () => { + txb.addInput(txHash, 0) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + hashType: Transaction.SIGHASH_NONE, + }) + assert.strictEqual(txb.addOutput(scripts[0], 2000), 0) }) - assert.throws(() => { - txb.setLockTime(65535) - }, /No, this would invalidate signatures/) - }) - }) - - describe('sign', () => { - it('supports the alternative abstract interface { publicKey, sign }', () => { - const keyPair = { - publicKey: ECPair.makeRandom({ rng: () => { return Buffer.alloc(32, 1) } }).publicKey, - sign: hash => { return Buffer.alloc(64, 0x5f) } - } - - const txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, + it('add second output after signed first input with SIGHASH_SINGLE', () => { + txb.addInput(txHash, 0) + txb.addOutput(scripts[0], 2000) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + hashType: Transaction.SIGHASH_SINGLE, + }) + assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) + }) + + it('add first output after signed first input with SIGHASH_SINGLE', () => { + txb.addInput(txHash, 0) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + hashType: Transaction.SIGHASH_SINGLE, + }) + assert.throws(() => { + txb.addOutput(scripts[0], 2000) + }, /No, this would invalidate signatures/) + }) + + it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { + txb.addInput(txHash, 0) + txb.addOutput(scripts[0], 2000) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) + + assert.throws(() => { + txb.addOutput(scripts[1], 9000) + }, /No, this would invalidate signatures/) }) - assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) - it('supports low R signature signing', () => { - let txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - }) - // high R - assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + describe('setLockTime', () => { + it('throws if if there exist any scriptSigs', () => { + const txb = new TransactionBuilder() + txb.addInput(txHash, 0) + txb.addOutput(scripts[0], 100) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) - txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) - txb.setLowR() - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, + assert.throws(() => { + txb.setLockTime(65535) + }, /No, this would invalidate signatures/) }) - // low R - assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) - fixtures.invalid.sign.forEach(f => { - it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), () => { - const txb = construct(f, true) + describe('sign', () => { + it('supports the alternative abstract interface { publicKey, sign }', () => { + const keyPair = { + publicKey: ECPair.makeRandom({ rng: () => { return Buffer.alloc(32, 1) } }).publicKey, + sign: hash => { return Buffer.alloc(64, 0x5f) } + } - let threw = false - f.inputs.forEach((input, index) => { - input.signs.forEach(sign => { - const keyPairNetwork = NETWORKS[sign.network || f.network] - const keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork) - let redeemScript - let witnessScript + const txb = new TransactionBuilder() + txb.setVersion(1) + txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) + txb.addOutput('1111111111111111111114oLvT2', 100000) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) + assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + }) - if (sign.redeemScript) { - redeemScript = bscript.fromASM(sign.redeemScript) - } + it('supports low R signature signing', () => { + let txb = new TransactionBuilder() + txb.setVersion(1) + txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) + txb.addOutput('1111111111111111111114oLvT2', 100000) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) + // high R + assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') - if (sign.witnessScript) { - witnessScript = bscript.fromASM(sign.witnessScript) - } + txb = new TransactionBuilder() + txb.setVersion(1) + txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) + txb.addOutput('1111111111111111111114oLvT2', 100000) + txb.setLowR() + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) + // low R + assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + }) - if (sign.throws) { - assert.throws(() => { + fixtures.invalid.sign.forEach(f => { + it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), () => { + const txb = construct(f, true) + + let threw = false + f.inputs.forEach((input, index) => { + input.signs.forEach(sign => { + const keyPairNetwork = NETWORKS[sign.network || f.network] + const keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork) + let redeemScript + let witnessScript + + if (sign.redeemScript) { + redeemScript = bscript.fromASM(sign.redeemScript) + } + + if (sign.witnessScript) { + witnessScript = bscript.fromASM(sign.witnessScript) + } + + if (sign.throws) { + assert.throws(() => { + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: index, + keyPair: keyPair2, + redeemScript, + hashType: sign.hashType, + witnessValue: sign.value, + witnessScript, + }) + }, new RegExp(f.exception)) + threw = true + } else { txb.sign({ prevOutScriptType: sign.prevOutScriptType, vin: index, @@ -440,283 +460,272 @@ describe('TransactionBuilder', () => { witnessValue: sign.value, witnessScript, }) - }, new RegExp(f.exception)) - threw = true - } else { - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: index, - keyPair: keyPair2, - redeemScript, - hashType: sign.hashType, - witnessValue: sign.value, - witnessScript, - }) - } + } + }) }) + + assert.strictEqual(threw, true) }) - - assert.strictEqual(threw, true) - }) - }) - }) - - describe('build', () => { - fixtures.valid.build.forEach(f => { - it('builds "' + f.description + '"', () => { - const txb = construct(f) - const tx = f.incomplete ? txb.buildIncomplete() : txb.build() - - assert.strictEqual(tx.toHex(), f.txHex) }) }) - // TODO: remove duplicate test code - fixtures.invalid.build.forEach(f => { - describe('for ' + (f.description || f.exception), () => { - it('throws ' + f.exception, () => { - assert.throws(() => { - let txb - if (f.txHex) { - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) - } else { - txb = construct(f) - } + describe('build', () => { + fixtures.valid.build.forEach(f => { + it('builds "' + f.description + '"', () => { + const txb = construct(f, undefined, useOldSignArgs) + const tx = f.incomplete ? txb.buildIncomplete() : txb.build() - txb.build() - }, new RegExp(f.exception)) + assert.strictEqual(tx.toHex(), f.txHex) }) + }) - // if throws on incomplete too, enforce that - if (f.incomplete) { + // TODO: remove duplicate test code + fixtures.invalid.build.forEach(f => { + describe('for ' + (f.description || f.exception), () => { it('throws ' + f.exception, () => { assert.throws(() => { let txb if (f.txHex) { txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) } else { - txb = construct(f) + txb = construct(f, undefined, useOldSignArgs) + } + + txb.build() + }, new RegExp(f.exception)) + }) + + // if throws on incomplete too, enforce that + if (f.incomplete) { + it('throws ' + f.exception, () => { + assert.throws(() => { + let txb + if (f.txHex) { + txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) + } else { + txb = construct(f, undefined, useOldSignArgs) + } + + txb.buildIncomplete() + }, new RegExp(f.exception)) + }) + } else { + it('does not throw if buildIncomplete', () => { + let txb + if (f.txHex) { + txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) + } else { + txb = construct(f, undefined, useOldSignArgs) } txb.buildIncomplete() - }, new RegExp(f.exception)) - }) - } else { - it('does not throw if buildIncomplete', () => { - let txb - if (f.txHex) { - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) - } else { - txb = construct(f) - } + }) + } + }) + }) - txb.buildIncomplete() - }) - } + it('for incomplete with 0 signatures', () => { + const randomTxData = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000' + const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH' + + const randomTx = Transaction.fromHex(randomTxData) + let tx = new TransactionBuilder() + tx.addInput(randomTx, 0) + tx.addOutput(randomAddress, 1000) + tx = tx.buildIncomplete() + assert(tx) + }) + + it('for incomplete P2SH with 0 signatures', () => { + const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', 'hex') // arbitrary P2SH input + const inpTx = Transaction.fromBuffer(inp) + + const txb = new TransactionBuilder(NETWORKS.testnet) + txb.addInput(inpTx, 0) + txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output + + txb.buildIncomplete() + }) + + it('for incomplete P2WPKH with 0 signatures', () => { + const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', 'hex') + const inpTx = Transaction.fromBuffer(inp) + + const txb = new TransactionBuilder(NETWORKS.testnet) + txb.addInput(inpTx, 0) + txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output + + txb.buildIncomplete() + }) + + it('for incomplete P2WSH with 0 signatures', () => { + const inpTx = Transaction.fromBuffer(Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', 'hex')) + + const txb = new TransactionBuilder(NETWORKS.testnet) + txb.addInput(inpTx, 0) + txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output + + txb.buildIncomplete() }) }) - it('for incomplete with 0 signatures', () => { - const randomTxData = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000' - const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH' + describe('multisig', () => { + fixtures.valid.multisig.forEach(f => { + it(f.description, () => { + const network = NETWORKS[f.network] + let txb = construct(f, true) + let tx - const randomTx = Transaction.fromHex(randomTxData) - let tx = new TransactionBuilder() - tx.addInput(randomTx, 0) - tx.addOutput(randomAddress, 1000) - tx = tx.buildIncomplete() - assert(tx) - }) + f.inputs.forEach((input, i) => { + const redeemScript = bscript.fromASM(input.redeemScript) - it('for incomplete P2SH with 0 signatures', () => { - const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', 'hex') // arbitrary P2SH input - const inpTx = Transaction.fromBuffer(inp) + input.signs.forEach(sign => { + // rebuild the transaction each-time after the first + if (tx) { + // manually override the scriptSig? + if (sign.scriptSigBefore) { + tx.ins[i].script = bscript.fromASM(sign.scriptSigBefore) + } - const txb = new TransactionBuilder(NETWORKS.testnet) - txb.addInput(inpTx, 0) - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output - - txb.buildIncomplete() - }) - - it('for incomplete P2WPKH with 0 signatures', () => { - const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', 'hex') - const inpTx = Transaction.fromBuffer(inp) - - const txb = new TransactionBuilder(NETWORKS.testnet) - txb.addInput(inpTx, 0) - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output - - txb.buildIncomplete() - }) - - it('for incomplete P2WSH with 0 signatures', () => { - const inpTx = Transaction.fromBuffer(Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', 'hex')) - - const txb = new TransactionBuilder(NETWORKS.testnet) - txb.addInput(inpTx, 0) - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output - - txb.buildIncomplete() - }) - }) - - describe('multisig', () => { - fixtures.valid.multisig.forEach(f => { - it(f.description, () => { - const network = NETWORKS[f.network] - let txb = construct(f, true) - let tx - - f.inputs.forEach((input, i) => { - const redeemScript = bscript.fromASM(input.redeemScript) - - input.signs.forEach(sign => { - // rebuild the transaction each-time after the first - if (tx) { - // manually override the scriptSig? - if (sign.scriptSigBefore) { - tx.ins[i].script = bscript.fromASM(sign.scriptSigBefore) + // rebuild + txb = TransactionBuilder.fromTransaction(tx, network) } - // rebuild - txb = TransactionBuilder.fromTransaction(tx, network) - } + const keyPair2 = ECPair.fromWIF(sign.keyPair, network) + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: i, + keyPair: keyPair2, + redeemScript, + hashType: sign.hashType, + }) - const keyPair2 = ECPair.fromWIF(sign.keyPair, network) - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: i, - keyPair: keyPair2, - redeemScript, - hashType: sign.hashType, + // update the tx + tx = txb.buildIncomplete() + + // now verify the serialized scriptSig is as expected + assert.strictEqual(bscript.toASM(tx.ins[i].script), sign.scriptSig) }) - - // update the tx - tx = txb.buildIncomplete() - - // now verify the serialized scriptSig is as expected - assert.strictEqual(bscript.toASM(tx.ins[i].script), sign.scriptSig) }) + + tx = txb.build() + assert.strictEqual(tx.toHex(), f.txHex) + }) + }) + }) + + describe('various edge case', () => { + const network = NETWORKS.testnet + + it('should warn of high fee for segwit transaction based on VSize, not Size', () => { + const rawtx = '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + + '1df90000000000fffffffffdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a1df9' + + '0100000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca40000' + + '000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca401000000' + + '00ffffffff0100040000000000001976a914cf307285359ab7ef6a2daa0522c7908ddf5fe7a988ac024730' + + '440220113324438816338406841775e079b04c50d04f241da652a4035b1017ea1ecf5502205802191eb49c' + + '54bf2a5667aea72e51c3ca92085efc60f12d1ebda3a64aff343201210283409659355b6d1cc3c32decd5d5' + + '61abaac86c37a353b52895a5e6c196d6f44802483045022100dc2892874e6d8708e3f5a058c5c9263cdf03' + + '969492270f89ee4933caf6daf8bb0220391dfe61a002709b63b9d64422d3db09b727839d1287e10a128a5d' + + 'b52a82309301210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f448024830' + + '450221009e3ed3a6ae93a018f443257b43e47b55cf7f7f3547d8807178072234686b22160220576121cfe6' + + '77c7eddf5575ea0a7c926247df6eca723c4f85df306e8bc08ea2df01210283409659355b6d1cc3c32decd5' + + 'd561abaac86c37a353b52895a5e6c196d6f44802473044022007be81ffd4297441ab10e740fc9bab9545a2' + + '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + + '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000' + const txb = TransactionBuilder.fromTransaction(Transaction.fromHex(rawtx)) + txb.__INPUTS[0].value = 241530 + txb.__INPUTS[1].value = 241530 + txb.__INPUTS[2].value = 248920 + txb.__INPUTS[3].value = 248920 + + assert.throws(() => { + txb.build() + }, new RegExp('Transaction has absurd fees')) + }) + + it('should classify witness inputs with witness = true during multisigning', () => { + const keyPair = ECPair.fromWIF('cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', network) + const witnessScript = Buffer.from('522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', 'hex') + const redeemScript = Buffer.from('002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', 'hex') + const scriptPubKey = Buffer.from('a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', 'hex') + const txb = new TransactionBuilder(network) + txb.setVersion(1) + txb.addInput('a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', 1, 0xffffffff, scriptPubKey) + txb.addOutput(scriptPubKey, 99000) + txb.sign({ + prevOutScriptType: 'p2sh-p2wsh-p2ms', + vin: 0, + keyPair, + redeemScript, + witnessValue: 100000, + witnessScript, }) - tx = txb.build() - assert.strictEqual(tx.toHex(), f.txHex) + // 2-of-2 signed only once + const tx = txb.buildIncomplete() + + // Only input is segwit, so txid should be accurate with the final tx + assert.strictEqual(tx.getId(), 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') + + const txHex = tx.toHex() + TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) + }) + + it('should handle badly pre-filled OP_0s', () => { + // OP_0 is used where a signature is missing + const redeemScripSig = bscript.fromASM('OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') + const redeemScript = bscript.fromASM('OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG') + + const tx = new Transaction() + tx.addInput(Buffer.from('cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f07149', 'hex'), 0, undefined, redeemScripSig) + tx.addOutput(Buffer.from('76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac', 'hex'), 1000) + + // now import the Transaction + const txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet) + + const keyPair2 = ECPair.fromWIF('91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', network) + txb.sign({ + prevOutScriptType: 'p2sh-p2ms', + vin: 0, + keyPair: keyPair2, + redeemScript, + }) + + const tx2 = txb.build() + assert.strictEqual(tx2.getId(), 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') + assert.strictEqual(bscript.toASM(tx2.ins[0].script), 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') + }) + + it('should not classify blank scripts as nonstandard', () => { + let txb = new TransactionBuilder() + txb.setVersion(1) + txb.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) + + const incomplete = txb.buildIncomplete().toHex() + const keyPair = ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') + + // sign, as expected + txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) + const txId = txb.build().getId() + assert.strictEqual(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') + + // and, repeat + txb = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) + txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + }) + const txId2 = txb.build().getId() + assert.strictEqual(txId, txId2) }) }) }) - - describe('various edge case', () => { - const network = NETWORKS.testnet - - it('should warn of high fee for segwit transaction based on VSize, not Size', () => { - const rawtx = '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + - '1df90000000000fffffffffdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a1df9' + - '0100000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca40000' + - '000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca401000000' + - '00ffffffff0100040000000000001976a914cf307285359ab7ef6a2daa0522c7908ddf5fe7a988ac024730' + - '440220113324438816338406841775e079b04c50d04f241da652a4035b1017ea1ecf5502205802191eb49c' + - '54bf2a5667aea72e51c3ca92085efc60f12d1ebda3a64aff343201210283409659355b6d1cc3c32decd5d5' + - '61abaac86c37a353b52895a5e6c196d6f44802483045022100dc2892874e6d8708e3f5a058c5c9263cdf03' + - '969492270f89ee4933caf6daf8bb0220391dfe61a002709b63b9d64422d3db09b727839d1287e10a128a5d' + - 'b52a82309301210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f448024830' + - '450221009e3ed3a6ae93a018f443257b43e47b55cf7f7f3547d8807178072234686b22160220576121cfe6' + - '77c7eddf5575ea0a7c926247df6eca723c4f85df306e8bc08ea2df01210283409659355b6d1cc3c32decd5' + - 'd561abaac86c37a353b52895a5e6c196d6f44802473044022007be81ffd4297441ab10e740fc9bab9545a2' + - '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + - '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000' - const txb = TransactionBuilder.fromTransaction(Transaction.fromHex(rawtx)) - txb.__INPUTS[0].value = 241530 - txb.__INPUTS[1].value = 241530 - txb.__INPUTS[2].value = 248920 - txb.__INPUTS[3].value = 248920 - - assert.throws(() => { - txb.build() - }, new RegExp('Transaction has absurd fees')) - }) - - it('should classify witness inputs with witness = true during multisigning', () => { - const keyPair = ECPair.fromWIF('cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', network) - const witnessScript = Buffer.from('522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', 'hex') - const redeemScript = Buffer.from('002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', 'hex') - const scriptPubKey = Buffer.from('a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', 'hex') - const txb = new TransactionBuilder(network) - txb.setVersion(1) - txb.addInput('a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', 1, 0xffffffff, scriptPubKey) - txb.addOutput(scriptPubKey, 99000) - txb.sign({ - prevOutScriptType: 'p2sh-p2wsh-p2ms', - vin: 0, - keyPair, - redeemScript, - witnessValue: 100000, - witnessScript, - }) - - // 2-of-2 signed only once - const tx = txb.buildIncomplete() - - // Only input is segwit, so txid should be accurate with the final tx - assert.strictEqual(tx.getId(), 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') - - const txHex = tx.toHex() - TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) - }) - - it('should handle badly pre-filled OP_0s', () => { - // OP_0 is used where a signature is missing - const redeemScripSig = bscript.fromASM('OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') - const redeemScript = bscript.fromASM('OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG') - - const tx = new Transaction() - tx.addInput(Buffer.from('cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f07149', 'hex'), 0, undefined, redeemScripSig) - tx.addOutput(Buffer.from('76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac', 'hex'), 1000) - - // now import the Transaction - const txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet) - - const keyPair2 = ECPair.fromWIF('91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', network) - txb.sign({ - prevOutScriptType: 'p2sh-p2ms', - vin: 0, - keyPair: keyPair2, - redeemScript, - }) - - const tx2 = txb.build() - assert.strictEqual(tx2.getId(), 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') - assert.strictEqual(bscript.toASM(tx2.ins[0].script), 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') - }) - - it('should not classify blank scripts as nonstandard', () => { - let txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) - - const incomplete = txb.buildIncomplete().toHex() - const keyPair = ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') - - // sign, as expected - txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - }) - const txId = txb.build().getId() - assert.strictEqual(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') - - // and, repeat - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) - txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - }) - const txId2 = txb.build().getId() - assert.strictEqual(txId, txId2) - }) - }) -}) +} From 84d5e67e38573b5496ca45e170946107fee9270f Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 14 Jun 2019 12:20:12 +0900 Subject: [PATCH 307/568] Use for loop instead of some to allow for future await usage --- src/transaction_builder.js | 9 +++++---- ts_src/transaction_builder.ts | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 4f16b41..40aa30f 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -936,8 +936,9 @@ function checkSignArgs(txb, signParams) { } function trySign(input, ourPubKey, keyPair, signatureHash, hashType, useLowR) { // enforce in order signing of public keys - const signed = input.pubkeys.some((pubKey, i) => { - if (!ourPubKey.equals(pubKey)) return false; + let signed = false; + for (const [i, pubKey] of input.pubkeys.entries()) { + if (!ourPubKey.equals(pubKey)) continue; if (input.signatures[i]) throw new Error('Signature already exists'); // TODO: add tests if (ourPubKey.length !== 33 && input.hasWitness) { @@ -947,8 +948,8 @@ function trySign(input, ourPubKey, keyPair, signatureHash, hashType, useLowR) { } const signature = keyPair.sign(signatureHash, useLowR); input.signatures[i] = bscript.signature.encode(signature, hashType); - return true; - }); + signed = true; + } if (!signed) throw new Error('Key pair cannot sign for this input'); } function getSigningData( diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 7af207b..880a662 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -1170,8 +1170,9 @@ function trySign( useLowR: boolean, ): void { // enforce in order signing of public keys - const signed = input.pubkeys!.some((pubKey, i) => { - if (!ourPubKey.equals(pubKey!)) return false; + let signed = false; + for (const [i, pubKey] of input.pubkeys!.entries()) { + if (!ourPubKey.equals(pubKey!)) continue; if (input.signatures![i]) throw new Error('Signature already exists'); // TODO: add tests @@ -1183,8 +1184,8 @@ function trySign( const signature = keyPair.sign(signatureHash, useLowR); input.signatures![i] = bscript.signature.encode(signature, hashType); - return true; - }); + signed = true; + } if (!signed) throw new Error('Key pair cannot sign for this input'); } From d814c214663d28a771c587fc9210e69d1f5b6417 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 14 Jun 2019 14:23:51 +0900 Subject: [PATCH 308/568] Condense sign parts work directly --- src/transaction_builder.js | 38 +++++++++++---------- ts_src/transaction_builder.ts | 64 ++++++++++++++++------------------- 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 40aa30f..91b24f7 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -153,24 +153,17 @@ class TransactionBuilder { witnessValue, witnessScript, ) { - const data = getSigningData( - this, - signParams, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - ); - const { input, ourPubKey, signatureHash } = data; - ({ keyPair, hashType } = data); trySign( - input, - ourPubKey, - keyPair, - signatureHash, - hashType, - this.__USE_LOW_R, + getSigningData( + this, + signParams, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, + this.__USE_LOW_R, + ), ); } __addInputUnsafe(txHash, vout, options) { @@ -934,7 +927,14 @@ function checkSignArgs(txb, signParams) { break; } } -function trySign(input, ourPubKey, keyPair, signatureHash, hashType, useLowR) { +function trySign({ + input, + ourPubKey, + keyPair, + signatureHash, + hashType, + useLowR, +}) { // enforce in order signing of public keys let signed = false; for (const [i, pubKey] of input.pubkeys.entries()) { @@ -960,6 +960,7 @@ function getSigningData( hashType, witnessValue, witnessScript, + useLowR, ) { let vin; if (typeof signParams === 'number') { @@ -1045,5 +1046,6 @@ function getSigningData( keyPair, signatureHash, hashType, + useLowR: !!useLowR, }; } diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 880a662..9cb3c8f 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -243,26 +243,17 @@ export class TransactionBuilder { witnessValue?: number, witnessScript?: Buffer, ): void { - const data = getSigningData( - this, - signParams, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - ); - - const { input, ourPubKey, signatureHash } = data; - ({ keyPair, hashType } = data); - trySign( - input, - ourPubKey, - keyPair, - signatureHash, - hashType, - this.__USE_LOW_R, + getSigningData( + this, + signParams, + keyPair, + redeemScript, + hashType, + witnessValue, + witnessScript, + this.__USE_LOW_R, + ), ); } @@ -1161,14 +1152,14 @@ function checkSignArgs(txb: TransactionBuilder, signParams: TxbSignArg): void { } } -function trySign( - input: TxbInput, - ourPubKey: Buffer, - keyPair: ECPairInterface, - signatureHash: Buffer, - hashType: number, - useLowR: boolean, -): void { +function trySign({ + input, + ourPubKey, + keyPair, + signatureHash, + hashType, + useLowR, +}: SigningData): void { // enforce in order signing of public keys let signed = false; for (const [i, pubKey] of input.pubkeys!.entries()) { @@ -1190,6 +1181,15 @@ function trySign( if (!signed) throw new Error('Key pair cannot sign for this input'); } +interface SigningData { + input: TxbInput; + ourPubKey: Buffer; + keyPair: ECPairInterface; + signatureHash: Buffer; + hashType: number; + useLowR: boolean; +} + function getSigningData( txb: TransactionBuilder, signParams: number | TxbSignArg, @@ -1198,13 +1198,8 @@ function getSigningData( hashType?: number, witnessValue?: number, witnessScript?: Buffer, -): { - input: TxbInput; - ourPubKey: Buffer; - keyPair: ECPairInterface; - signatureHash: Buffer; - hashType: number; -} { + useLowR?: boolean, +): SigningData { let vin: number; if (typeof signParams === 'number') { console.warn( @@ -1302,5 +1297,6 @@ function getSigningData( keyPair, signatureHash, hashType, + useLowR: !!useLowR, }; } From 1cc7205e9ce07e8dfe11a79a83182f4384e0edbc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 14 Jun 2019 14:48:22 +0900 Subject: [PATCH 309/568] Remove @ts-ignore --- src/transaction_builder.js | 35 ++++++++++++++++----------------- ts_src/transaction_builder.ts | 37 ++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 91b24f7..252e7c1 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -155,7 +155,10 @@ class TransactionBuilder { ) { trySign( getSigningData( - this, + this.network, + this.__INPUTS, + this.__needsOutputs.bind(this), + this.__TX, signParams, keyPair, redeemScript, @@ -725,7 +728,7 @@ function canSign(input) { function signatureHashType(buffer) { return buffer.readUInt8(buffer.length - 1); } -function checkSignArgs(txb, signParams) { +function checkSignArgs(inputs, signParams) { if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { throw new TypeError( `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, @@ -739,8 +742,7 @@ function checkSignArgs(txb, signParams) { ), [signParams.vin, signParams.hashType, signParams.keyPair], ); - // @ts-ignore - const prevOutType = (txb.__INPUTS[signParams.vin] || []).prevOutType; + const prevOutType = (inputs[signParams.vin] || []).prevOutType; const posType = signParams.prevOutScriptType; switch (posType) { case 'p2pkh': @@ -953,7 +955,10 @@ function trySign({ if (!signed) throw new Error('Key pair cannot sign for this input'); } function getSigningData( - txb, + network, + inputs, + needsOutputs, + tx, signParams, keyPair, redeemScript, @@ -970,7 +975,7 @@ function getSigningData( ); vin = signParams; } else if (typeof signParams === 'object') { - checkSignArgs(txb, signParams); + checkSignArgs(inputs, signParams); ({ vin, keyPair, @@ -988,16 +993,12 @@ function getSigningData( throw new Error('sign requires keypair'); } // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== txb.network) + if (keyPair.network && keyPair.network !== network) throw new TypeError('Inconsistent network'); - // @ts-ignore - if (!txb.__INPUTS[vin]) throw new Error('No input at index: ' + vin); + if (!inputs[vin]) throw new Error('No input at index: ' + vin); hashType = hashType || transaction_1.Transaction.SIGHASH_ALL; - // @ts-ignore - if (txb.__needsOutputs(hashType)) - throw new Error('Transaction needs outputs'); - // @ts-ignore - const input = txb.__INPUTS[vin]; + if (needsOutputs(hashType)) throw new Error('Transaction needs outputs'); + const input = inputs[vin]; // if redeemScript was previously provided, enforce consistency if ( input.redeemScript !== undefined && @@ -1029,16 +1030,14 @@ function getSigningData( // ready to sign let signatureHash; if (input.hasWitness) { - // @ts-ignore - signatureHash = txb.__TX.hashForWitnessV0( + signatureHash = tx.hashForWitnessV0( vin, input.signScript, input.value, hashType, ); } else { - // @ts-ignore - signatureHash = txb.__TX.hashForSignature(vin, input.signScript, hashType); + signatureHash = tx.hashForSignature(vin, input.signScript, hashType); } return { input, diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 9cb3c8f..fede01b 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -245,7 +245,10 @@ export class TransactionBuilder { ): void { trySign( getSigningData( - this, + this.network, + this.__INPUTS, + this.__needsOutputs.bind(this), + this.__TX, signParams, keyPair, redeemScript, @@ -949,7 +952,7 @@ function signatureHashType(buffer: Buffer): number { return buffer.readUInt8(buffer.length - 1); } -function checkSignArgs(txb: TransactionBuilder, signParams: TxbSignArg): void { +function checkSignArgs(inputs: TxbInput[], signParams: TxbSignArg): void { if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { throw new TypeError( `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, @@ -963,8 +966,7 @@ function checkSignArgs(txb: TransactionBuilder, signParams: TxbSignArg): void { ), [signParams.vin, signParams.hashType, signParams.keyPair], ); - // @ts-ignore - const prevOutType = (txb.__INPUTS[signParams.vin] || []).prevOutType; + const prevOutType = (inputs[signParams.vin] || []).prevOutType; const posType = signParams.prevOutScriptType; switch (posType) { case 'p2pkh': @@ -1190,8 +1192,13 @@ interface SigningData { useLowR: boolean; } +type HashTypeCheck = (hashType: number) => boolean; + function getSigningData( - txb: TransactionBuilder, + network: Network, + inputs: TxbInput[], + needsOutputs: HashTypeCheck, + tx: Transaction, signParams: number | TxbSignArg, keyPair?: ECPairInterface, redeemScript?: Buffer, @@ -1208,7 +1215,7 @@ function getSigningData( ); vin = signParams; } else if (typeof signParams === 'object') { - checkSignArgs(txb, signParams); + checkSignArgs(inputs, signParams); ({ vin, keyPair, @@ -1226,18 +1233,14 @@ function getSigningData( throw new Error('sign requires keypair'); } // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== txb.network) + if (keyPair.network && keyPair.network !== network) throw new TypeError('Inconsistent network'); - // @ts-ignore - if (!txb.__INPUTS[vin]) throw new Error('No input at index: ' + vin); + if (!inputs[vin]) throw new Error('No input at index: ' + vin); hashType = hashType || Transaction.SIGHASH_ALL; - // @ts-ignore - if (txb.__needsOutputs(hashType)) - throw new Error('Transaction needs outputs'); + if (needsOutputs(hashType)) throw new Error('Transaction needs outputs'); - // @ts-ignore - const input = txb.__INPUTS[vin]; + const input = inputs[vin]; // if redeemScript was previously provided, enforce consistency if ( @@ -1275,16 +1278,14 @@ function getSigningData( // ready to sign let signatureHash: Buffer; if (input.hasWitness) { - // @ts-ignore - signatureHash = txb.__TX.hashForWitnessV0( + signatureHash = tx.hashForWitnessV0( vin, input.signScript as Buffer, input.value as number, hashType, ); } else { - // @ts-ignore - signatureHash = txb.__TX.hashForSignature( + signatureHash = tx.hashForSignature( vin, input.signScript as Buffer, hashType, From 691e4d15d14353017aa8ed73bd01c332223afa06 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 14 Jun 2019 15:25:07 +0900 Subject: [PATCH 310/568] Create Signer interface and use it --- src/transaction_builder.js | 3 ++- ts_src/ecpair.ts | 19 +++++++++++++++---- ts_src/index.ts | 2 +- ts_src/transaction_builder.ts | 13 +++++++------ types/ecpair.d.ts | 17 +++++++++++++---- types/index.d.ts | 2 +- types/transaction_builder.d.ts | 6 +++--- 7 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 252e7c1..c299e29 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -1007,7 +1007,8 @@ function getSigningData( ) { throw new Error('Inconsistent redeemScript'); } - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey(); + const ourPubKey = + keyPair.publicKey || (keyPair.getPublicKey && keyPair.getPublicKey()); if (!canSign(input)) { if (witnessValue !== undefined) { if (input.value !== undefined && input.value !== witnessValue) diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index 3d74433..c951f76 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -19,15 +19,26 @@ interface ECPairOptions { rng?(arg0: number): Buffer; } -export interface ECPairInterface { +export interface Signer { + publicKey: Buffer; + network?: Network; + sign(hash: Buffer, lowR?: boolean): Buffer; + getPublicKey?(): Buffer; +} + +export interface SignerAsync { + publicKey: Buffer; + network?: Network; + sign(hash: Buffer, lowR?: boolean): Promise<Buffer>; + getPublicKey?(): Buffer; +} + +export interface ECPairInterface extends Signer { compressed: boolean; network: Network; - publicKey: Buffer; privateKey?: Buffer; toWIF(): string; - sign(hash: Buffer, lowR?: boolean): Buffer; verify(hash: Buffer, signature: Buffer): boolean; - getPublicKey?(): Buffer; } class ECPair implements ECPairInterface { diff --git a/ts_src/index.ts b/ts_src/index.ts index 1068839..4f2d498 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -14,7 +14,7 @@ export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; -export { ECPairInterface } from './ecpair'; +export { ECPairInterface, Signer, SignerAsync } from './ecpair'; export { Network } from './networks'; export { Payment, PaymentOpts, Stack, StackElement } from './payments'; export { OpCode } from './script'; diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index fede01b..7d47ad3 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -2,7 +2,7 @@ import * as baddress from './address'; import { reverseBuffer } from './bufferutils'; import * as classify from './classify'; import * as bcrypto from './crypto'; -import { ECPairInterface } from './ecpair'; +import { Signer } from './ecpair'; import * as ECPair from './ecpair'; import { Network } from './networks'; import * as networks from './networks'; @@ -74,7 +74,7 @@ interface TxbOutput { interface TxbSignArg { prevOutScriptType: string; vin: number; - keyPair: ECPairInterface; + keyPair: Signer; redeemScript?: Buffer; hashType?: number; witnessValue?: number; @@ -237,7 +237,7 @@ export class TransactionBuilder { sign( signParams: number | TxbSignArg, - keyPair?: ECPairInterface, + keyPair?: Signer, redeemScript?: Buffer, hashType?: number, witnessValue?: number, @@ -1186,7 +1186,7 @@ function trySign({ interface SigningData { input: TxbInput; ourPubKey: Buffer; - keyPair: ECPairInterface; + keyPair: Signer; signatureHash: Buffer; hashType: number; useLowR: boolean; @@ -1200,7 +1200,7 @@ function getSigningData( needsOutputs: HashTypeCheck, tx: Transaction, signParams: number | TxbSignArg, - keyPair?: ECPairInterface, + keyPair?: Signer, redeemScript?: Buffer, hashType?: number, witnessValue?: number, @@ -1251,7 +1251,8 @@ function getSigningData( throw new Error('Inconsistent redeemScript'); } - const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!(); + const ourPubKey = + keyPair.publicKey || (keyPair.getPublicKey && keyPair.getPublicKey()); if (!canSign(input)) { if (witnessValue !== undefined) { if (input.value !== undefined && input.value !== witnessValue) diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index 5f301b2..8b7d193 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -5,15 +5,24 @@ interface ECPairOptions { network?: Network; rng?(arg0: number): Buffer; } -export interface ECPairInterface { +export interface Signer { + publicKey: Buffer; + network?: Network; + sign(hash: Buffer, lowR?: boolean): Buffer; + getPublicKey?(): Buffer; +} +export interface SignerAsync { + publicKey: Buffer; + network?: Network; + sign(hash: Buffer, lowR?: boolean): Promise<Buffer>; + getPublicKey?(): Buffer; +} +export interface ECPairInterface extends Signer { compressed: boolean; network: Network; - publicKey: Buffer; privateKey?: Buffer; toWIF(): string; - sign(hash: Buffer, lowR?: boolean): Buffer; verify(hash: Buffer, signature: Buffer): boolean; - getPublicKey?(): Buffer; } declare class ECPair implements ECPairInterface { private __D?; diff --git a/types/index.d.ts b/types/index.d.ts index 28046df..93d72e4 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -11,7 +11,7 @@ export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; -export { ECPairInterface } from './ecpair'; +export { ECPairInterface, Signer, SignerAsync } from './ecpair'; export { Network } from './networks'; export { Payment, PaymentOpts, Stack, StackElement } from './payments'; export { OpCode } from './script'; diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts index d57e4eb..2799464 100644 --- a/types/transaction_builder.d.ts +++ b/types/transaction_builder.d.ts @@ -1,11 +1,11 @@ /// <reference types="node" /> -import { ECPairInterface } from './ecpair'; +import { Signer } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; interface TxbSignArg { prevOutScriptType: string; vin: number; - keyPair: ECPairInterface; + keyPair: Signer; redeemScript?: Buffer; hashType?: number; witnessValue?: number; @@ -27,7 +27,7 @@ export declare class TransactionBuilder { addOutput(scriptPubKey: string | Buffer, value: number): number; build(): Transaction; buildIncomplete(): Transaction; - sign(signParams: number | TxbSignArg, keyPair?: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void; + sign(signParams: number | TxbSignArg, keyPair?: Signer, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void; private __addInputUnsafe; private __build; private __canModifyInputs; From 071e201a752c56233368328cb300f44fbc503a01 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 17 Jun 2019 13:02:09 +0900 Subject: [PATCH 311/568] Silence console.warn during old arg tests --- test/transaction_builder.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 3615d28..fd6efa1 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -102,7 +102,16 @@ function construct (f, dontSign, useOldSignArgs) { return constructSign(f, txb, useOldSignArgs) } +// TODO: Remove loop in v6 for (const useOldSignArgs of [ false, true ]) { + // Search for "// TODO: Remove me in v6" + // to find the second part of this console.warn replace + let consoleWarn; + if (useOldSignArgs) { + consoleWarn = console.warn; + // Silence console.warn during these tests + console.warn = () => undefined; + } describe(`TransactionBuilder: useOldSignArgs === ${useOldSignArgs}`, () => { // constants const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) @@ -725,6 +734,10 @@ for (const useOldSignArgs of [ false, true ]) { }) const txId2 = txb.build().getId() assert.strictEqual(txId, txId2) + // TODO: Remove me in v6 + if (useOldSignArgs) { + console.warn = consoleWarn; + } }) }) }) From 1b39d9caf431b3534a1b58ca526eb006b9ad19a0 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 17 Jun 2019 13:34:20 +0900 Subject: [PATCH 312/568] Make errors clearer and increase coverage --- src/transaction_builder.js | 21 ++- test/fixtures/transaction_builder.json | 184 +++++++++++++++++++++++++ test/transaction_builder.js | 45 +++++- ts_src/transaction_builder.ts | 21 ++- 4 files changed, 256 insertions(+), 15 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index c299e29..e63bdd0 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -734,13 +734,20 @@ function checkSignArgs(inputs, signParams) { `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, ); } - typeforce( - typeforce.tuple( - typeforce.Number, - typeforce.maybe(typeforce.Number), - types.Signer, - ), - [signParams.vin, signParams.hashType, signParams.keyPair], + tfMessage( + typeforce.Number, + signParams.vin, + `sign must include vin parameter as Number (input index)`, + ); + tfMessage( + types.Signer, + signParams.keyPair, + `sign must include keyPair parameter as Signer interface`, + ); + tfMessage( + typeforce.maybe(typeforce.Number), + signParams.hashType, + `sign hashType parameter must be a number`, ); const prevOutType = (inputs[signParams.vin] || []).prevOutType; const posType = signParams.prevOutScriptType; diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 040104a..07226b0 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -2470,6 +2470,190 @@ } ] }, + { + "exception": "input #0 is not of type p2pk: nulldata", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "prevTxScript": "OP_RETURN deadbeef", + "signs": [ + { + "prevOutScriptType": "p2pk", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "throws": true + } + ] + } + ], + "outputs": [ + { + "script": "OP_RETURN deadbeef", + "value": 1000 + } + ] + }, + { + "exception": "input #0 is not of type p2wpkh: nulldata", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "prevTxScript": "OP_RETURN deadbeef", + "signs": [ + { + "prevOutScriptType": "p2wpkh", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "throws": true + } + ] + } + ], + "outputs": [ + { + "script": "OP_RETURN deadbeef", + "value": 1000 + } + ] + }, + { + "exception": "input #0 is not of type p2ms: nulldata", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "prevTxScript": "OP_RETURN deadbeef", + "signs": [ + { + "prevOutScriptType": "p2ms", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "throws": true + } + ] + } + ], + "outputs": [ + { + "script": "OP_RETURN deadbeef", + "value": 1000 + } + ] + }, + { + "exception": "input #0 is not of type p2sh-p2wpkh: nulldata", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "prevTxScript": "OP_RETURN deadbeef", + "signs": [ + { + "prevOutScriptType": "p2sh-p2wpkh", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "throws": true + } + ] + } + ], + "outputs": [ + { + "script": "OP_RETURN deadbeef", + "value": 1000 + } + ] + }, + { + "exception": "input #0 is not of type p2sh-p2pk: nulldata", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "prevTxScript": "OP_RETURN deadbeef", + "signs": [ + { + "prevOutScriptType": "p2sh-p2pk", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "throws": true + } + ] + } + ], + "outputs": [ + { + "script": "OP_RETURN deadbeef", + "value": 1000 + } + ] + }, + { + "exception": "input #0 is not of type p2wsh-p2pk: nulldata", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "prevTxScript": "OP_RETURN deadbeef", + "signs": [ + { + "prevOutScriptType": "p2wsh-p2pk", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "throws": true + } + ] + } + ], + "outputs": [ + { + "script": "OP_RETURN deadbeef", + "value": 1000 + } + ] + }, + { + "exception": "input #0 is not of type p2sh-p2wsh-p2pk: nulldata", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "prevTxScript": "OP_RETURN deadbeef", + "signs": [ + { + "prevOutScriptType": "p2sh-p2wsh-p2pk", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "throws": true + } + ] + } + ], + "outputs": [ + { + "script": "OP_RETURN deadbeef", + "value": 1000 + } + ] + }, + { + "exception": "Unknown prevOutScriptType \"notvalidtype\"", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "prevTxScript": "OP_RETURN deadbeef", + "signs": [ + { + "prevOutScriptType": "notvalidtype", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "throws": true + } + ] + } + ], + "outputs": [ + { + "script": "OP_RETURN deadbeef", + "value": 1000 + } + ] + }, { "description": "Transaction w/ no outputs (but 1 SIGHASH_NONE)", "exception": "Transaction needs outputs", diff --git a/test/transaction_builder.js b/test/transaction_builder.js index fd6efa1..a135cca 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -104,7 +104,7 @@ function construct (f, dontSign, useOldSignArgs) { // TODO: Remove loop in v6 for (const useOldSignArgs of [ false, true ]) { - // Search for "// TODO: Remove me in v6" + // Search for "useOldSignArgs" // to find the second part of this console.warn replace let consoleWarn; if (useOldSignArgs) { @@ -426,6 +426,49 @@ for (const useOldSignArgs of [ false, true ]) { assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) + it('fails when missing required arguments', () => { + let txb = new TransactionBuilder() + txb.setVersion(1) + txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) + txb.addOutput('1111111111111111111114oLvT2', 100000) + assert.throws(() => { + txb.sign() + }, /TransactionBuilder sign first arg must be TxbSignArg or number/) + assert.throws(() => { + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 1, + keyPair, + }) + }, /No input at index: 1/) + assert.throws(() => { + txb.sign({ + prevOutScriptType: 'p2pkh', + keyPair, + }) + }, /sign must include vin parameter as Number \(input index\)/) + assert.throws(() => { + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair: {}, + }) + }, /sign must include keyPair parameter as Signer interface/) + assert.throws(() => { + txb.sign({ + prevOutScriptType: 'p2pkh', + vin: 0, + keyPair, + hashType: 'string', + }) + }, /sign hashType parameter must be a number/) + if (useOldSignArgs) { + assert.throws(() => { + txb.sign(0) + }, /sign requires keypair/) + } + }) + fixtures.invalid.sign.forEach(f => { it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), () => { const txb = construct(f, true) diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index 7d47ad3..c486285 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -958,13 +958,20 @@ function checkSignArgs(inputs: TxbInput[], signParams: TxbSignArg): void { `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, ); } - typeforce( - typeforce.tuple( - typeforce.Number, - typeforce.maybe(typeforce.Number), - types.Signer, - ), - [signParams.vin, signParams.hashType, signParams.keyPair], + tfMessage( + typeforce.Number, + signParams.vin, + `sign must include vin parameter as Number (input index)`, + ); + tfMessage( + types.Signer, + signParams.keyPair, + `sign must include keyPair parameter as Signer interface`, + ); + tfMessage( + typeforce.maybe(typeforce.Number), + signParams.hashType, + `sign hashType parameter must be a number`, ); const prevOutType = (inputs[signParams.vin] || []).prevOutType; const posType = signParams.prevOutScriptType; From e4c5128fd8124ba74dbf897ad22d897139ecd78b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 15 Jun 2019 19:36:44 +0900 Subject: [PATCH 313/568] Use regtest-client --- package-lock.json | 9 ++ package.json | 1 + test/integration/_regtest.js | 155 +---------------------------------- 3 files changed, 14 insertions(+), 151 deletions(-) diff --git a/package-lock.json b/package-lock.json index d769ef5..fc2d9cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1493,6 +1493,15 @@ "read-pkg": "^3.0.0" } }, + "regtest-client": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/regtest-client/-/regtest-client-0.1.0.tgz", + "integrity": "sha512-qTy+VvEKx8NRxSCr1jr+l9d+DeF06lxxWU9kmS8+kRVtgWHBTZYgQwRN6KkVqBGYP1Vls6dlG9X874WWTEurSQ==", + "dev": true, + "requires": { + "dhttp": "^3.0.3" + } + }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", diff --git a/package.json b/package.json index b8ec5f3..7c18d24 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "nyc": "^14.1.1", "prettier": "1.16.4", "proxyquire": "^2.0.1", + "regtest-client": "0.1.0", "rimraf": "^2.6.3", "tslint": "^5.16.0", "typescript": "3.2.2" diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 30b868c..02913a8 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -1,156 +1,9 @@ -const assert = require('assert') -const bitcoin = require('../../') -const dhttpCallback = require('dhttp/200') -// use Promises -const dhttp = options => new Promise((resolve, reject) => { - return dhttpCallback(options, (err, data) => { - if (err) return reject(err) - else return resolve(data) - }) -}) +const { RegtestUtils } = require('regtest-client') +const bitcoin = require('../..') const APIPASS = process.env.APIPASS || 'satoshi' const APIURL = process.env.APIURL || 'https://regtest.bitbank.cc/1' -const NETWORK = bitcoin.networks.testnet -function broadcast (txHex) { - return dhttp({ - method: 'POST', - url: APIURL + '/t/push', - body: txHex - }) -} +const regtestUtils = new RegtestUtils(bitcoin, { APIPASS, APIURL }) -function mine (count) { - return dhttp({ - method: 'POST', - url: APIURL + '/r/generate?count=' + count + '&key=' + APIPASS - }) -} - -function height () { - return dhttp({ - method: 'GET', - url: APIURL + '/b/best/height' - }) -} - -function _faucetRequest (address, value) { - return dhttp({ - method: 'POST', - url: APIURL + '/r/faucet?address=' + address + '&value=' + value + '&key=' + APIPASS - }) -} - -async function faucet (address, value) { - let count = 0 - let _unspents = [] - const sleep = ms => new Promise((resolve, reject) => setTimeout(resolve, ms)) - const randInt = (min, max) => min + Math.floor((max - min + 1) * Math.random()) - while (_unspents.length === 0) { - if (count > 0) { - if (count >= 5) throw new Error('Missing Inputs') - console.log('Missing Inputs, retry #' + count) - await sleep(randInt(150, 250)) - } - - const txId = await _faucetRequest(address, value) - .then( - v => v, // Pass success value as is - async err => { - // Bad Request error is fixed by making sure height is >= 432 - const currentHeight = await height() - if (err.message === 'Bad Request' && currentHeight < 432) { - await mine(432 - currentHeight) - return _faucetRequest(address, value) - } else if (err.message === 'Bad Request' && currentHeight >= 432) { - return _faucetRequest(address, value) - } else { - throw err - } - } - ) - - await sleep(randInt(10, 40)) - - const results = await unspents(address) - - _unspents = results.filter(x => x.txId === txId) - - count++ - } - - return _unspents.pop() -} - -async function faucetComplex (output, value) { - const keyPair = bitcoin.ECPair.makeRandom({ network: NETWORK }) - const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: NETWORK }) - - const unspent = await faucet(p2pkh.address, value * 2) - - const txvb = new bitcoin.TransactionBuilder(NETWORK) - txvb.addInput(unspent.txId, unspent.vout, null, p2pkh.output) - txvb.addOutput(output, value) - txvb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair - }) - const txv = txvb.build() - - await broadcast(txv.toHex()) - - return { - txId: txv.getId(), - vout: 0, - value - } -} - -function fetch (txId) { - return dhttp({ - method: 'GET', - url: APIURL + '/t/' + txId + '/json' - }) -} - -function unspents (address) { - return dhttp({ - method: 'GET', - url: APIURL + '/a/' + address + '/unspents' - }) -} - -async function verify (txo) { - const tx = await fetch(txo.txId) - - const txoActual = tx.outs[txo.vout] - if (txo.address) assert.strictEqual(txoActual.address, txo.address) - if (txo.value) assert.strictEqual(txoActual.value, txo.value) -} - -function getAddress (node, network) { - return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address -} - -function randomAddress () { - return getAddress(bitcoin.ECPair.makeRandom({ - network: bitcoin.networks.testnet - }), bitcoin.networks.testnet) -} - -module.exports = { - broadcast, - dhttp, - faucet, - faucetComplex, - fetch, - height, - mine, - network: NETWORK, - unspents, - verify, - randomAddress, - RANDOM_ADDRESS: randomAddress() -} +module.exports = regtestUtils; From 62f174902127ad9ccf30414b52e2dc23c588526f Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Mon, 24 Jun 2019 17:09:48 +0700 Subject: [PATCH 314/568] Add BIP174 dependency --- package-lock.json | 5 +++++ package.json | 1 + 2 files changed, 6 insertions(+) diff --git a/package-lock.json b/package-lock.json index d769ef5..30a6172 100644 --- a/package-lock.json +++ b/package-lock.json @@ -199,6 +199,11 @@ "file-uri-to-path": "1.0.0" } }, + "bip174": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.4.tgz", + "integrity": "sha512-KirRr35qCNih51thdf6klTevFTtm/cIw2iqumRgxtqFjNREFG78fFylfCkMM9aQdlYfSoj3sczxgXham0jZVnw==" + }, "bip32": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.3.tgz", diff --git a/package.json b/package.json index b8ec5f3..fd5f5cf 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", + "bip174": "0.0.4", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", From 6a5e395ebd27eb3ae08953cae4c78ee4ba76c41f Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Mon, 24 Jun 2019 17:20:15 +0700 Subject: [PATCH 315/568] Extend BIP174 PSBT base class --- src/index.js | 2 ++ src/psbt.js | 9 +++++++++ ts_src/index.ts | 1 + ts_src/psbt.ts | 7 +++++++ types/index.d.ts | 1 + types/psbt.d.ts | 4 ++++ 6 files changed, 24 insertions(+) create mode 100644 src/psbt.js create mode 100644 ts_src/psbt.ts create mode 100644 types/psbt.d.ts diff --git a/src/index.js b/src/index.js index 499380e..bc71f02 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,8 @@ const script = require('./script'); exports.script = script; var block_1 = require('./block'); exports.Block = block_1.Block; +var psbt_1 = require('./psbt'); +exports.Psbt = psbt_1.Psbt; var script_1 = require('./script'); exports.opcodes = script_1.OPS; var transaction_1 = require('./transaction'); diff --git a/src/psbt.js b/src/psbt.js new file mode 100644 index 0000000..70b60e2 --- /dev/null +++ b/src/psbt.js @@ -0,0 +1,9 @@ +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const bip174_1 = require('bip174'); +class Psbt extends bip174_1.Psbt { + constructor() { + super(); + } +} +exports.Psbt = Psbt; diff --git a/ts_src/index.ts b/ts_src/index.ts index 4f2d498..58c39c7 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -9,6 +9,7 @@ import * as script from './script'; export { ECPair, address, bip32, crypto, networks, payments, script }; export { Block } from './block'; +export { Psbt } from './psbt'; export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts new file mode 100644 index 0000000..08d2670 --- /dev/null +++ b/ts_src/psbt.ts @@ -0,0 +1,7 @@ +import { Psbt as PsbtBase } from 'bip174'; + +export class Psbt extends PsbtBase { + constructor() { + super(); + } +} diff --git a/types/index.d.ts b/types/index.d.ts index 93d72e4..fc5a932 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -7,6 +7,7 @@ import * as payments from './payments'; import * as script from './script'; export { ECPair, address, bip32, crypto, networks, payments, script }; export { Block } from './block'; +export { Psbt } from './psbt'; export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; diff --git a/types/psbt.d.ts b/types/psbt.d.ts new file mode 100644 index 0000000..880ebd2 --- /dev/null +++ b/types/psbt.d.ts @@ -0,0 +1,4 @@ +import { Psbt as PsbtBase } from 'bip174'; +export declare class Psbt extends PsbtBase { + constructor(); +} From 2ed89cdc68b83b286c5d216b4257a8a31f56281f Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 25 Jun 2019 17:47:26 +0700 Subject: [PATCH 316/568] Update BIP174 package --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30a6172..ac0507b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.4.tgz", - "integrity": "sha512-KirRr35qCNih51thdf6klTevFTtm/cIw2iqumRgxtqFjNREFG78fFylfCkMM9aQdlYfSoj3sczxgXham0jZVnw==" + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.5.tgz", + "integrity": "sha512-NNt0e9pz7h8EhC+pNAcB8G0Ca/Lei42YnAtPMewpcuLzRJGgaJO4vgtBpeQHH/f3fWlabZwSh/3tyEHwFNXlRw==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index fd5f5cf..6527baf 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.4", + "bip174": "0.0.5", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", From 6ed635d7b41b976f2d88dc7492e857c8adda0543 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 25 Jun 2019 18:22:00 +0700 Subject: [PATCH 317/568] Flesh out signInput interface --- src/psbt.js | 12 ++++++++++++ ts_src/psbt.ts | 18 ++++++++++++++++++ types/psbt.d.ts | 2 ++ 3 files changed, 32 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index 70b60e2..887c814 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -5,5 +5,17 @@ class Psbt extends bip174_1.Psbt { constructor() { super(); } + signInput(inputIndex, keyPair) { + // TODO: Implement BIP174 pre-sign checks: + // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer + // TODO: Get hash to sign + const hash = Buffer.alloc(32); + const partialSig = { + pubkey: keyPair.publicKey, + signature: keyPair.sign(hash), + }; + this.addPartialSigToInput(inputIndex, partialSig); + return this; + } } exports.Psbt = Psbt; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 08d2670..8fec780 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,7 +1,25 @@ import { Psbt as PsbtBase } from 'bip174'; +import { Signer } from './ecpair'; export class Psbt extends PsbtBase { constructor() { super(); } + + signInput(inputIndex: number, keyPair: Signer): Psbt { + // TODO: Implement BIP174 pre-sign checks: + // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer + + // TODO: Get hash to sign + const hash = Buffer.alloc(32); + + const partialSig = { + pubkey: keyPair.publicKey, + signature: keyPair.sign(hash), + }; + + this.addPartialSigToInput(inputIndex, partialSig); + + return this; + } } diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 880ebd2..a58b982 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,4 +1,6 @@ import { Psbt as PsbtBase } from 'bip174'; +import { Signer } from './ecpair'; export declare class Psbt extends PsbtBase { constructor(); + signInput(inputIndex: number, keyPair: Signer): Psbt; } From ff3caa02fe23a949bad78b23c29a25d5da04d2e1 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 26 Jun 2019 16:29:00 +0700 Subject: [PATCH 318/568] Add BIP174 pseudo code for signing checks --- src/psbt.js | 21 +++++++++++++++++++++ ts_src/psbt.ts | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index 887c814..5bdb176 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -8,6 +8,27 @@ class Psbt extends bip174_1.Psbt { signInput(inputIndex, keyPair) { // TODO: Implement BIP174 pre-sign checks: // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer + // + // if non_witness_utxo.exists: + // assert(sha256d(non_witness_utxo) == psbt.tx.innput[i].prevout.hash) + // if redeemScript.exists: + // assert(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey == P2SH(redeemScript)) + // sign_non_witness(redeemScript) + // else: + // sign_non_witness(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey) + // else if witness_utxo.exists: + // if redeemScript.exists: + // assert(witness_utxo.scriptPubKey == P2SH(redeemScript)) + // script = redeemScript + // else: + // script = witness_utxo.scriptPubKey + // if IsP2WPKH(script): + // sign_witness(P2PKH(script[2:22])) + // else if IsP2WSH(script): + // assert(script == P2WSH(witnessScript)) + // sign_witness(witnessScript) + // else: + // assert False // TODO: Get hash to sign const hash = Buffer.alloc(32); const partialSig = { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 8fec780..73308a2 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -9,6 +9,27 @@ export class Psbt extends PsbtBase { signInput(inputIndex: number, keyPair: Signer): Psbt { // TODO: Implement BIP174 pre-sign checks: // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer + // + // if non_witness_utxo.exists: + // assert(sha256d(non_witness_utxo) == psbt.tx.innput[i].prevout.hash) + // if redeemScript.exists: + // assert(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey == P2SH(redeemScript)) + // sign_non_witness(redeemScript) + // else: + // sign_non_witness(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey) + // else if witness_utxo.exists: + // if redeemScript.exists: + // assert(witness_utxo.scriptPubKey == P2SH(redeemScript)) + // script = redeemScript + // else: + // script = witness_utxo.scriptPubKey + // if IsP2WPKH(script): + // sign_witness(P2PKH(script[2:22])) + // else if IsP2WSH(script): + // assert(script == P2WSH(witnessScript)) + // sign_witness(witnessScript) + // else: + // assert False // TODO: Get hash to sign const hash = Buffer.alloc(32); From 98dff9a47e810292b17284a3797cfee437e994d1 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 26 Jun 2019 17:55:02 +0700 Subject: [PATCH 319/568] Check Non-witness UTXO hash when signing PSBT input --- src/psbt.js | 19 +++++++++++++++++++ ts_src/psbt.ts | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index 5bdb176..5d40ab4 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -1,6 +1,7 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); +const transaction_1 = require('./transaction'); class Psbt extends bip174_1.Psbt { constructor() { super(); @@ -29,6 +30,24 @@ class Psbt extends bip174_1.Psbt { // sign_witness(witnessScript) // else: // assert False + const input = this.inputs[inputIndex]; + if (input === undefined) throw new Error(`No input #${inputIndex}`); + // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout + if (input.nonWitnessUtxo) { + const unsignedTx = transaction_1.Transaction.fromBuffer( + this.globalMap.unsignedTx, + ); + const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( + input.nonWitnessUtxo, + ); + const inputHash = unsignedTx.ins[inputIndex].hash; + const utxoHash = nonWitnessUtxoTx.getHash(); + if (Buffer.compare(inputHash, utxoHash) !== 0) { + throw new Error( + `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, + ); + } + } // TODO: Get hash to sign const hash = Buffer.alloc(32); const partialSig = { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 73308a2..9b0e9ce 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,5 +1,6 @@ import { Psbt as PsbtBase } from 'bip174'; import { Signer } from './ecpair'; +import { Transaction } from './transaction'; export class Psbt extends PsbtBase { constructor() { @@ -31,6 +32,24 @@ export class Psbt extends PsbtBase { // else: // assert False + const input = this.inputs[inputIndex]; + if (input === undefined) throw new Error(`No input #${inputIndex}`); + + // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout + if (input.nonWitnessUtxo) { + const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!); + const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); + + const inputHash = unsignedTx.ins[inputIndex].hash; + const utxoHash = nonWitnessUtxoTx.getHash(); + + if (Buffer.compare(inputHash, utxoHash) !== 0) { + throw new Error( + `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, + ); + } + } + // TODO: Get hash to sign const hash = Buffer.alloc(32); From 2dcac556015e736e215c293e7af71a17a1a0a88a Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 26 Jun 2019 18:20:49 +0700 Subject: [PATCH 320/568] Add simple tests for non-witness UTXO check --- test/psbt.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 test/psbt.js diff --git a/test/psbt.js b/test/psbt.js new file mode 100644 index 0000000..9422d6e --- /dev/null +++ b/test/psbt.js @@ -0,0 +1,43 @@ +const { describe, it, beforeEach } = require('mocha') +const assert = require('assert') + +const ECPair = require('../src/ecpair') +const Psbt = require('..').Psbt + +// For now, just hardcoding some test values is fine +// const fixtures = require('./fixtures/psbt') + +describe(`Psbt`, () => { + // constants + const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) + + describe('signInput', () => { + it('throws if non-witness UTXO hash doesn\'t match the hash specified in the prevout', () => { + const inputUtxo = '0200000001c1602ba68c8c241450a78b61dbfde272989181d07537b1e70d31b7db939557f2000000006a473044022029872b97579850c87658e431bb9df4a3f3e41590777529a55e25eb11eccafef50220511700aa1ea2c2cd499251f99014f22c5af63a00c76fd24da650014a0a8199e901210264187d9ee773aa333ac223678478b1df3ea268178fc9447e0a60c443eddaa749fdffffff01995d0000000000001976a914759d6677091e973b9e9d99f19c68fbf43e3f05f988acc8d30800' + const inputHash = 'd2d00ff71b1d1920d3a7717274b720f3d8230d9f38ec8f3e6867a207f2a40092' + const inputIndex = 0 + + const psbt = new Psbt() + psbt.addInput({hash: inputHash, index: inputIndex}) + psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex')) + + assert.throws(() => { + psbt.signInput(inputIndex, keyPair) + }) + }) + + it('does not throw if non-witness UTXO hash matches the hash specified in the prevout', () => { + const inputUtxo = '01000000027dddcf79a2e541030bc753871d1c9d4dc163e4d6bd5aefae4bd84de64e16a652000000008b483045022100d3a2c3b58ae0f0b551711aa8949f478724428efa03f3179c3a50dc2c9ace46aa02201b2da84a21429a10af187731c882fc1f727e7b89573e07f0192e9e3de79fabf00141040e3a759c33b03e1af8e5d86fb447a40eff244c847a4f8274276db490054e8be076f8801ddc9c5246ee86b6f33cfe38e8b7e57ab9db390eb3ec1ec6ae9eeea113fdffffff4fef6d7f3c1e5d0bea733b2fd644fa456cdf73f21eb7e8866a2721d79266e9e8010000008a4730440220284a2989d45c48a6c8a556b30b3467eaf6abf866ec75c4d9f0e3872074f62c070220686ad82869c1669e6be162c4a34e4c617a971766f2b9688789eb2f498fe5eb6b0141040e3a759c33b03e1af8e5d86fb447a40eff244c847a4f8274276db490054e8be076f8801ddc9c5246ee86b6f33cfe38e8b7e57ab9db390eb3ec1ec6ae9eeea113fdffffff0224c70d00000000001976a914da6473ed373e08f46dd8003fca7ba72fbe9c555e88ac9cb00e00000000001976a91449707992598f85a31aa6715af70fe507610b6f8b88ac11c00800' + const inputHash = 'd2d00ff71b1d1920d3a7717274b720f3d8230d9f38ec8f3e6867a207f2a40092' + const inputIndex = 0 + + const psbt = new Psbt() + psbt.addInput({hash: inputHash, index: inputIndex}) + psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex')) + + assert.doesNotThrow(() => { + psbt.signInput(inputIndex, keyPair) + }) + }) + }) +}) From 5fd18d806f8dbce87b176cd2a3f869a82dc07990 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 27 Jun 2019 18:19:15 +0700 Subject: [PATCH 321/568] Check redeem script matches when signing PSBT input --- src/psbt.js | 20 +++++++++++++++++--- ts_src/psbt.ts | 23 ++++++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 5d40ab4..6e72daa 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -1,6 +1,7 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); +const payments = require('./payments'); const transaction_1 = require('./transaction'); class Psbt extends bip174_1.Psbt { constructor() { @@ -32,7 +33,6 @@ class Psbt extends bip174_1.Psbt { // assert False const input = this.inputs[inputIndex]; if (input === undefined) throw new Error(`No input #${inputIndex}`); - // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout if (input.nonWitnessUtxo) { const unsignedTx = transaction_1.Transaction.fromBuffer( this.globalMap.unsignedTx, @@ -40,13 +40,27 @@ class Psbt extends bip174_1.Psbt { const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( input.nonWitnessUtxo, ); - const inputHash = unsignedTx.ins[inputIndex].hash; + const prevoutHash = unsignedTx.ins[inputIndex].hash; const utxoHash = nonWitnessUtxoTx.getHash(); - if (Buffer.compare(inputHash, utxoHash) !== 0) { + // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout + if (Buffer.compare(prevoutHash, utxoHash) !== 0) { throw new Error( `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, ); } + if (input.redeemScript) { + const prevoutIndex = unsignedTx.ins[inputIndex].index; + const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; + const redeemScriptOutput = payments.p2sh({ + redeem: { output: input.redeemScript }, + }).output; + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + if (Buffer.compare(prevout.script, redeemScriptOutput) !== 0) { + throw new Error( + `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } + } } // TODO: Get hash to sign const hash = Buffer.alloc(32); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 9b0e9ce..8f40881 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,5 +1,6 @@ import { Psbt as PsbtBase } from 'bip174'; import { Signer } from './ecpair'; +import * as payments from './payments'; import { Transaction } from './transaction'; export class Psbt extends PsbtBase { @@ -35,19 +36,35 @@ export class Psbt extends PsbtBase { const input = this.inputs[inputIndex]; if (input === undefined) throw new Error(`No input #${inputIndex}`); - // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout if (input.nonWitnessUtxo) { const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!); const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); - const inputHash = unsignedTx.ins[inputIndex].hash; + const prevoutHash = unsignedTx.ins[inputIndex].hash; const utxoHash = nonWitnessUtxoTx.getHash(); - if (Buffer.compare(inputHash, utxoHash) !== 0) { + // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout + if (Buffer.compare(prevoutHash, utxoHash) !== 0) { throw new Error( `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, ); } + + if (input.redeemScript) { + const prevoutIndex = unsignedTx.ins[inputIndex].index; + const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; + + const redeemScriptOutput = payments.p2sh({ + redeem: { output: input.redeemScript }, + }).output as Buffer; + + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + if (Buffer.compare(prevout.script, redeemScriptOutput) !== 0) { + throw new Error( + `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } + } } // TODO: Get hash to sign From 64dc6543be759f36c0c14e9191213f04c6940b70 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 27 Jun 2019 18:20:19 +0700 Subject: [PATCH 322/568] Add simple tests for redeem script check --- test/psbt.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/psbt.js b/test/psbt.js index 9422d6e..f814921 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -39,5 +39,37 @@ describe(`Psbt`, () => { psbt.signInput(inputIndex, keyPair) }) }) + + it('throws if redeem script does not match the scriptPubKey in the prevout', () => { + const inputUtxo = '0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000' + const inputRedeemScript = '00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903' + const inputHash = '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858' + const inputIndex = 0 + + const psbt = new Psbt() + psbt.addInput({hash: inputHash, index: inputIndex}) + psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex')) + psbt.addRedeemScriptToInput(inputIndex, Buffer.from(inputRedeemScript, 'hex')) + + assert.throws(() => { + psbt.signInput(inputIndex, keyPair) + }) + }) + + it('does not throw if redeem script matches the scriptPubKey in the prevout', () => { + const inputUtxo = '0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000' + const inputRedeemScript = '5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae' + const inputHash = '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858' + const inputIndex = 0 + + const psbt = new Psbt() + psbt.addInput({hash: inputHash, index: inputIndex}) + psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex')) + psbt.addRedeemScriptToInput(inputIndex, Buffer.from(inputRedeemScript, 'hex')) + + assert.doesNotThrow(() => { + psbt.signInput(inputIndex, keyPair) + }) + }) }) }) From 1afac399b14a1d1e40974c5bee597c06687cfff6 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Fri, 28 Jun 2019 16:31:29 +0700 Subject: [PATCH 323/568] Update BIP174 package to fix inheritance issues --- package-lock.json | 6 +++--- package.json | 2 +- src/psbt.js | 10 ++++++++-- ts_src/psbt.ts | 10 ++++++++-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index ac0507b..d409322 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.5.tgz", - "integrity": "sha512-NNt0e9pz7h8EhC+pNAcB8G0Ca/Lei42YnAtPMewpcuLzRJGgaJO4vgtBpeQHH/f3fWlabZwSh/3tyEHwFNXlRw==" + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.8.tgz", + "integrity": "sha512-xWPzmlCvLoOWTlXk1wG7+TyOfaN8xX07IieuG4ug5su3igC9s4Lsdq+IEEMo+YHDQ4hPPAX9LYio6aEIAA+Zrg==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index 6527baf..b964697 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.5", + "bip174": "0.0.8", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", diff --git a/src/psbt.js b/src/psbt.js index 6e72daa..9f29525 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -68,8 +68,14 @@ class Psbt extends bip174_1.Psbt { pubkey: keyPair.publicKey, signature: keyPair.sign(hash), }; - this.addPartialSigToInput(inputIndex, partialSig); - return this; + // Just hardcode this for now to satisfy the stricter sig type checks + partialSig.signature = Buffer.from( + '304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6' + + '771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa9' + + '4b99bdf86151db9a9a01', + 'hex', + ); + return this.addPartialSigToInput(inputIndex, partialSig); } } exports.Psbt = Psbt; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 8f40881..75a90d7 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -75,8 +75,14 @@ export class Psbt extends PsbtBase { signature: keyPair.sign(hash), }; - this.addPartialSigToInput(inputIndex, partialSig); + // Just hardcode this for now to satisfy the stricter sig type checks + partialSig.signature = Buffer.from( + '304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6' + + '771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa9' + + '4b99bdf86151db9a9a01', + 'hex', + ); - return this; + return this.addPartialSigToInput(inputIndex, partialSig); } } From 3a82486fb5667ee14bc6d830e745a1151163d202 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Fri, 28 Jun 2019 16:55:00 +0700 Subject: [PATCH 324/568] Loop over PSBT tests from fixtures --- test/fixtures/psbt.json | 30 +++++++++++++++++ test/psbt.js | 71 +++++++---------------------------------- 2 files changed, 42 insertions(+), 59 deletions(-) create mode 100644 test/fixtures/psbt.json diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json new file mode 100644 index 0000000..9268ed9 --- /dev/null +++ b/test/fixtures/psbt.json @@ -0,0 +1,30 @@ +{ + "signInput": { + "checks": [ + { + "description": "checks non-witness UTXO matches the hash specified in the prevout", + "shouldSign": { + "psbt": "cHNidP8BADMBAAAAAZIApPIHomdoPo/sOJ8NI9jzILd0cnGn0yAZHRv3D9DSAAAAAAD/////AAAAAAAAAQD9tQEBAAAAAn3dz3mi5UEDC8dThx0cnU3BY+TWvVrvrkvYTeZOFqZSAAAAAItIMEUCIQDTosO1iuDwtVFxGqiUn0eHJEKO+gPzF5w6UNwsms5GqgIgGy2oSiFCmhCvGHcxyIL8H3J+e4lXPgfwGS6ePeefq/ABQQQOOnWcM7A+Gvjl2G+0R6QO/yRMhHpPgnQnbbSQBU6L4Hb4gB3cnFJG7oa28zz+OOi35Xq52zkOs+wexq6e7qET/f///0/vbX88Hl0L6nM7L9ZE+kVs33PyHrfohmonIdeSZunoAQAAAIpHMEQCIChKKYnUXEimyKVWsws0Z+r2q/hm7HXE2fDjhyB09iwHAiBoatgoacFmnmvhYsSjTkxhepcXZvK5aIeJ6y9Jj+XrawFBBA46dZwzsD4a+OXYb7RHpA7/JEyEek+CdCdttJAFTovgdviAHdycUkbuhrbzPP446LflernbOQ6z7B7Grp7uoRP9////AiTHDQAAAAAAGXapFNpkc+03Pgj0bdgAP8p7py++nFVeiKycsA4AAAAAABl2qRRJcHmSWY+FoxqmcVr3D+UHYQtvi4isEcAIAAAA", + "inputToCheck": 0 + }, + "shouldThrow": { + "errorMessage": "Non-witness UTXO hash for input #0 doesn't match the hash specified in the prevout", + "psbt": "cHNidP8BADMBAAAAAZIApPIHomdoPo/sOJ8NI9jzILd0cnGn0yAZHRv3D9DSAAAAAAD/////AAAAAAAAAQC/AgAAAAHBYCumjIwkFFCni2Hb/eJymJGB0HU3secNMbfbk5VX8gAAAABqRzBEAiAphyuXV5hQyHZY5DG7nfSj8+QVkHd1KaVeJesR7Mr+9QIgURcAqh6iws1JklH5kBTyLFr2OgDHb9JNplABSgqBmekBIQJkGH2e53OqMzrCI2eEeLHfPqJoF4/JRH4KYMRD7dqnSf3///8BmV0AAAAAAAAZdqkUdZ1mdwkelzuenZnxnGj79D4/BfmIrMjTCAAAAA==", + "inputToCheck": 0 + } + }, + { + "description": "checks redeem script matches the scriptPubKey in the prevout", + "shouldSign": { + "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuAAA=", + "inputToCheck": 0 + }, + "shouldThrow": { + "errorMessage": "Redeem script for input #0 doesn't match the scriptPubKey in the prevout", + "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMAAA==", + "inputToCheck": 0 + } + } + ] + } +} diff --git a/test/psbt.js b/test/psbt.js index f814921..1d8ee90 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -4,71 +4,24 @@ const assert = require('assert') const ECPair = require('../src/ecpair') const Psbt = require('..').Psbt -// For now, just hardcoding some test values is fine -// const fixtures = require('./fixtures/psbt') +const fixtures = require('./fixtures/psbt') describe(`Psbt`, () => { // constants const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) describe('signInput', () => { - it('throws if non-witness UTXO hash doesn\'t match the hash specified in the prevout', () => { - const inputUtxo = '0200000001c1602ba68c8c241450a78b61dbfde272989181d07537b1e70d31b7db939557f2000000006a473044022029872b97579850c87658e431bb9df4a3f3e41590777529a55e25eb11eccafef50220511700aa1ea2c2cd499251f99014f22c5af63a00c76fd24da650014a0a8199e901210264187d9ee773aa333ac223678478b1df3ea268178fc9447e0a60c443eddaa749fdffffff01995d0000000000001976a914759d6677091e973b9e9d99f19c68fbf43e3f05f988acc8d30800' - const inputHash = 'd2d00ff71b1d1920d3a7717274b720f3d8230d9f38ec8f3e6867a207f2a40092' - const inputIndex = 0 - - const psbt = new Psbt() - psbt.addInput({hash: inputHash, index: inputIndex}) - psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex')) - - assert.throws(() => { - psbt.signInput(inputIndex, keyPair) - }) - }) - - it('does not throw if non-witness UTXO hash matches the hash specified in the prevout', () => { - const inputUtxo = '01000000027dddcf79a2e541030bc753871d1c9d4dc163e4d6bd5aefae4bd84de64e16a652000000008b483045022100d3a2c3b58ae0f0b551711aa8949f478724428efa03f3179c3a50dc2c9ace46aa02201b2da84a21429a10af187731c882fc1f727e7b89573e07f0192e9e3de79fabf00141040e3a759c33b03e1af8e5d86fb447a40eff244c847a4f8274276db490054e8be076f8801ddc9c5246ee86b6f33cfe38e8b7e57ab9db390eb3ec1ec6ae9eeea113fdffffff4fef6d7f3c1e5d0bea733b2fd644fa456cdf73f21eb7e8866a2721d79266e9e8010000008a4730440220284a2989d45c48a6c8a556b30b3467eaf6abf866ec75c4d9f0e3872074f62c070220686ad82869c1669e6be162c4a34e4c617a971766f2b9688789eb2f498fe5eb6b0141040e3a759c33b03e1af8e5d86fb447a40eff244c847a4f8274276db490054e8be076f8801ddc9c5246ee86b6f33cfe38e8b7e57ab9db390eb3ec1ec6ae9eeea113fdffffff0224c70d00000000001976a914da6473ed373e08f46dd8003fca7ba72fbe9c555e88ac9cb00e00000000001976a91449707992598f85a31aa6715af70fe507610b6f8b88ac11c00800' - const inputHash = 'd2d00ff71b1d1920d3a7717274b720f3d8230d9f38ec8f3e6867a207f2a40092' - const inputIndex = 0 - - const psbt = new Psbt() - psbt.addInput({hash: inputHash, index: inputIndex}) - psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex')) - - assert.doesNotThrow(() => { - psbt.signInput(inputIndex, keyPair) - }) - }) - - it('throws if redeem script does not match the scriptPubKey in the prevout', () => { - const inputUtxo = '0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000' - const inputRedeemScript = '00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903' - const inputHash = '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858' - const inputIndex = 0 - - const psbt = new Psbt() - psbt.addInput({hash: inputHash, index: inputIndex}) - psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex')) - psbt.addRedeemScriptToInput(inputIndex, Buffer.from(inputRedeemScript, 'hex')) - - assert.throws(() => { - psbt.signInput(inputIndex, keyPair) - }) - }) - - it('does not throw if redeem script matches the scriptPubKey in the prevout', () => { - const inputUtxo = '0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000' - const inputRedeemScript = '5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae' - const inputHash = '75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858' - const inputIndex = 0 - - const psbt = new Psbt() - psbt.addInput({hash: inputHash, index: inputIndex}) - psbt.addNonWitnessUtxoToInput(inputIndex, Buffer.from(inputUtxo, 'hex')) - psbt.addRedeemScriptToInput(inputIndex, Buffer.from(inputRedeemScript, 'hex')) - - assert.doesNotThrow(() => { - psbt.signInput(inputIndex, keyPair) + fixtures.signInput.checks.forEach(f => { + it(f.description, () => { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotThrow(() => { + psbtThatShouldsign.signInput(f.shouldSign.inputToCheck, keyPair) + }) + + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.throws(() => { + psbtThatShouldThrow.signInput(f.shouldThrow.inputToCheck, keyPair) + }, {message: f.shouldThrow.errorMessage}) }) }) }) From 6562ee96a48f49d80f4b66ade10b6b17c982f8c7 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Fri, 28 Jun 2019 17:17:18 +0700 Subject: [PATCH 325/568] Remove redundant import from test --- test/psbt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/psbt.js b/test/psbt.js index 1d8ee90..3bf873d 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -1,4 +1,4 @@ -const { describe, it, beforeEach } = require('mocha') +const { describe, it } = require('mocha') const assert = require('assert') const ECPair = require('../src/ecpair') From 08627e65a3faeade0b81ab2487379de773bdb6d0 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Fri, 28 Jun 2019 18:14:37 +0700 Subject: [PATCH 326/568] Check redeem script matches witness utxo when signing PSBT input --- src/psbt.js | 14 ++++++++++++++ ts_src/psbt.ts | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index 9f29525..4dc72ff 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -61,6 +61,20 @@ class Psbt extends bip174_1.Psbt { ); } } + } else if (input.witnessUtxo) { + if (input.redeemScript) { + const redeemScriptOutput = payments.p2sh({ + redeem: { output: input.redeemScript }, + }).output; + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + if ( + Buffer.compare(input.witnessUtxo.script, redeemScriptOutput) !== 0 + ) { + throw new Error( + `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } + } } // TODO: Get hash to sign const hash = Buffer.alloc(32); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 75a90d7..ab1fd7b 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -65,6 +65,21 @@ export class Psbt extends PsbtBase { ); } } + } else if (input.witnessUtxo) { + if (input.redeemScript) { + const redeemScriptOutput = payments.p2sh({ + redeem: { output: input.redeemScript }, + }).output as Buffer; + + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + if ( + Buffer.compare(input.witnessUtxo.script, redeemScriptOutput) !== 0 + ) { + throw new Error( + `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } + } } // TODO: Get hash to sign From 10b3aff4fde3ecd74bdad6a04e88dabdc151beac Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Fri, 28 Jun 2019 18:15:01 +0700 Subject: [PATCH 327/568] Test redeem script witness utxo check --- test/fixtures/psbt.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 9268ed9..1b72874 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -14,7 +14,7 @@ } }, { - "description": "checks redeem script matches the scriptPubKey in the prevout", + "description": "checks redeem script matches the scriptPubKey in a non-witness prevout", "shouldSign": { "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuAAA=", "inputToCheck": 0 @@ -24,6 +24,18 @@ "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMAAA==", "inputToCheck": 0 } + }, + { + "description": "checks redeem script matches the scriptPubKey in a witness prevout", + "shouldSign": { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", + "inputToCheck": 1 + }, + "shouldThrow": { + "errorMessage": "Redeem script for input #1 doesn't match the scriptPubKey in the prevout", + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", + "inputToCheck": 1 + } } ] } From 95b4a2806d6d433bd0d766d0be7c0183d28541cd Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Fri, 28 Jun 2019 18:21:32 +0700 Subject: [PATCH 328/568] Improve code re-use for redeem script checks --- src/psbt.js | 37 +++++++++++++++++-------------------- ts_src/psbt.ts | 46 +++++++++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 4dc72ff..c68afb0 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -3,6 +3,17 @@ Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); const payments = require('./payments'); const transaction_1 = require('./transaction'); +const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => { + const redeemScriptOutput = payments.p2sh({ + redeem: { output: redeemScript }, + }).output; + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + if (Buffer.compare(scriptPubKey, redeemScriptOutput) !== 0) { + throw new Error( + `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } +}; class Psbt extends bip174_1.Psbt { constructor() { super(); @@ -51,29 +62,15 @@ class Psbt extends bip174_1.Psbt { if (input.redeemScript) { const prevoutIndex = unsignedTx.ins[inputIndex].index; const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; - const redeemScriptOutput = payments.p2sh({ - redeem: { output: input.redeemScript }, - }).output; - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - if (Buffer.compare(prevout.script, redeemScriptOutput) !== 0) { - throw new Error( - `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, - ); - } + checkRedeemScript(inputIndex, prevout.script, input.redeemScript); } } else if (input.witnessUtxo) { if (input.redeemScript) { - const redeemScriptOutput = payments.p2sh({ - redeem: { output: input.redeemScript }, - }).output; - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - if ( - Buffer.compare(input.witnessUtxo.script, redeemScriptOutput) !== 0 - ) { - throw new Error( - `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, - ); - } + checkRedeemScript( + inputIndex, + input.witnessUtxo.script, + input.redeemScript, + ); } } // TODO: Get hash to sign diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index ab1fd7b..70e04c9 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -3,6 +3,23 @@ import { Signer } from './ecpair'; import * as payments from './payments'; import { Transaction } from './transaction'; +const checkRedeemScript = ( + inputIndex: number, + scriptPubKey: Buffer, + redeemScript: Buffer, +): void => { + const redeemScriptOutput = payments.p2sh({ + redeem: { output: redeemScript }, + }).output as Buffer; + + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + if (Buffer.compare(scriptPubKey, redeemScriptOutput) !== 0) { + throw new Error( + `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } +}; + export class Psbt extends PsbtBase { constructor() { super(); @@ -53,32 +70,15 @@ export class Psbt extends PsbtBase { if (input.redeemScript) { const prevoutIndex = unsignedTx.ins[inputIndex].index; const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; - - const redeemScriptOutput = payments.p2sh({ - redeem: { output: input.redeemScript }, - }).output as Buffer; - - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - if (Buffer.compare(prevout.script, redeemScriptOutput) !== 0) { - throw new Error( - `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, - ); - } + checkRedeemScript(inputIndex, prevout.script, input.redeemScript); } } else if (input.witnessUtxo) { if (input.redeemScript) { - const redeemScriptOutput = payments.p2sh({ - redeem: { output: input.redeemScript }, - }).output as Buffer; - - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - if ( - Buffer.compare(input.witnessUtxo.script, redeemScriptOutput) !== 0 - ) { - throw new Error( - `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, - ); - } + checkRedeemScript( + inputIndex, + input.witnessUtxo.script, + input.redeemScript, + ); } } From f961724c73b97e1d503d5311a64d8d228e1476bd Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Fri, 28 Jun 2019 18:26:42 +0700 Subject: [PATCH 329/568] Prefer buf1.equals(buf2) over Buffer.compare(buf1, buf2) !== 0 --- src/psbt.js | 4 ++-- ts_src/psbt.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index c68afb0..55e6ac9 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -8,7 +8,7 @@ const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => { redeem: { output: redeemScript }, }).output; // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - if (Buffer.compare(scriptPubKey, redeemScriptOutput) !== 0) { + if (!scriptPubKey.equals(redeemScriptOutput)) { throw new Error( `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, ); @@ -54,7 +54,7 @@ class Psbt extends bip174_1.Psbt { const prevoutHash = unsignedTx.ins[inputIndex].hash; const utxoHash = nonWitnessUtxoTx.getHash(); // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout - if (Buffer.compare(prevoutHash, utxoHash) !== 0) { + if (!prevoutHash.equals(utxoHash)) { throw new Error( `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, ); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 70e04c9..7e5d13c 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -13,7 +13,7 @@ const checkRedeemScript = ( }).output as Buffer; // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - if (Buffer.compare(scriptPubKey, redeemScriptOutput) !== 0) { + if (!scriptPubKey.equals(redeemScriptOutput)) { throw new Error( `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, ); @@ -61,7 +61,7 @@ export class Psbt extends PsbtBase { const utxoHash = nonWitnessUtxoTx.getHash(); // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout - if (Buffer.compare(prevoutHash, utxoHash) !== 0) { + if (!prevoutHash.equals(utxoHash)) { throw new Error( `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, ); From 18e7c9de80db6f5d335574f33ef39dedab82d0e3 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Fri, 28 Jun 2019 18:28:28 +0700 Subject: [PATCH 330/568] Move comments to main check logic --- src/psbt.js | 3 ++- ts_src/psbt.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 55e6ac9..2513fb9 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -7,7 +7,6 @@ const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => { const redeemScriptOutput = payments.p2sh({ redeem: { output: redeemScript }, }).output; - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript if (!scriptPubKey.equals(redeemScriptOutput)) { throw new Error( `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, @@ -62,10 +61,12 @@ class Psbt extends bip174_1.Psbt { if (input.redeemScript) { const prevoutIndex = unsignedTx.ins[inputIndex].index; const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript(inputIndex, prevout.script, input.redeemScript); } } else if (input.witnessUtxo) { if (input.redeemScript) { + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript( inputIndex, input.witnessUtxo.script, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 7e5d13c..167f15f 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -12,7 +12,6 @@ const checkRedeemScript = ( redeem: { output: redeemScript }, }).output as Buffer; - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript if (!scriptPubKey.equals(redeemScriptOutput)) { throw new Error( `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, @@ -70,10 +69,13 @@ export class Psbt extends PsbtBase { if (input.redeemScript) { const prevoutIndex = unsignedTx.ins[inputIndex].index; const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; + + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript(inputIndex, prevout.script, input.redeemScript); } } else if (input.witnessUtxo) { if (input.redeemScript) { + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript( inputIndex, input.witnessUtxo.script, From 667ffb58eb14aafef64b1816f9bf3e2adf610c50 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 1 Jul 2019 18:01:46 +0900 Subject: [PATCH 331/568] Use signature encode --- src/psbt.js | 13 +++++-------- ts_src/psbt.ts | 14 +++++--------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 2513fb9..7bfa439 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -2,6 +2,7 @@ Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); const payments = require('./payments'); +const script = require('./script'); const transaction_1 = require('./transaction'); const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => { const redeemScriptOutput = payments.p2sh({ @@ -78,15 +79,11 @@ class Psbt extends bip174_1.Psbt { const hash = Buffer.alloc(32); const partialSig = { pubkey: keyPair.publicKey, - signature: keyPair.sign(hash), + signature: script.signature.encode( + keyPair.sign(hash), + input.sighashType || 0x01, + ), }; - // Just hardcode this for now to satisfy the stricter sig type checks - partialSig.signature = Buffer.from( - '304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6' + - '771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa9' + - '4b99bdf86151db9a9a01', - 'hex', - ); return this.addPartialSigToInput(inputIndex, partialSig); } } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 167f15f..63bc057 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,6 +1,7 @@ import { Psbt as PsbtBase } from 'bip174'; import { Signer } from './ecpair'; import * as payments from './payments'; +import * as script from './script'; import { Transaction } from './transaction'; const checkRedeemScript = ( @@ -89,17 +90,12 @@ export class Psbt extends PsbtBase { const partialSig = { pubkey: keyPair.publicKey, - signature: keyPair.sign(hash), + signature: script.signature.encode( + keyPair.sign(hash), + input.sighashType || 0x01, + ), }; - // Just hardcode this for now to satisfy the stricter sig type checks - partialSig.signature = Buffer.from( - '304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6' + - '771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa9' + - '4b99bdf86151db9a9a01', - 'hex', - ); - return this.addPartialSigToInput(inputIndex, partialSig); } } From f87b66eb24850fbca2a8626e8473d4ece0b22ae1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 1 Jul 2019 18:55:18 +0900 Subject: [PATCH 332/568] Finish sign --- src/psbt.js | 81 +++++++++++++++++++++++++++++++++++++++--------- ts_src/psbt.ts | 84 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 135 insertions(+), 30 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 7bfa439..9a9ad7e 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -2,18 +2,35 @@ Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); const payments = require('./payments'); -const script = require('./script'); +const bscript = require('./script'); const transaction_1 = require('./transaction'); -const checkRedeemScript = (inputIndex, scriptPubKey, redeemScript) => { - const redeemScriptOutput = payments.p2sh({ +const scriptCheckerFactory = (payment, paymentScriptName) => ( + inputIndex, + scriptPubKey, + redeemScript, +) => { + const redeemScriptOutput = payment({ redeem: { output: redeemScript }, }).output; if (!scriptPubKey.equals(redeemScriptOutput)) { throw new Error( - `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, ); } }; +const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script'); +const checkWitnessScript = scriptCheckerFactory( + payments.p2wsh, + 'Witness script', +); +const isP2WPKH = script => { + try { + payments.p2wpkh({ output: script }); + return true; + } catch (err) { + return false; + } +}; class Psbt extends bip174_1.Psbt { constructor() { super(); @@ -44,10 +61,12 @@ class Psbt extends bip174_1.Psbt { // assert False const input = this.inputs[inputIndex]; if (input === undefined) throw new Error(`No input #${inputIndex}`); + const unsignedTx = transaction_1.Transaction.fromBuffer( + this.globalMap.unsignedTx, + ); + const sighashType = input.sighashType || 0x01; + let hash; if (input.nonWitnessUtxo) { - const unsignedTx = transaction_1.Transaction.fromBuffer( - this.globalMap.unsignedTx, - ); const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( input.nonWitnessUtxo, ); @@ -59,13 +78,25 @@ class Psbt extends bip174_1.Psbt { `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, ); } + const prevoutIndex = unsignedTx.ins[inputIndex].index; + const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; if (input.redeemScript) { - const prevoutIndex = unsignedTx.ins[inputIndex].index; - const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript(inputIndex, prevout.script, input.redeemScript); + hash = unsignedTx.hashForSignature( + inputIndex, + input.redeemScript, + sighashType, + ); + } else { + hash = unsignedTx.hashForSignature( + inputIndex, + prevout.script, + sighashType, + ); } } else if (input.witnessUtxo) { + let script; if (input.redeemScript) { // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript( @@ -73,16 +104,36 @@ class Psbt extends bip174_1.Psbt { input.witnessUtxo.script, input.redeemScript, ); + script = input.redeemScript; + } else { + script = input.witnessUtxo.script; } + if (isP2WPKH(script)) { + // P2WPKH uses the P2PKH template for prevoutScript when signing + const signingScript = payments.p2pkh({ hash: script.slice(2) }).output; + hash = unsignedTx.hashForWitnessV0( + inputIndex, + signingScript, + input.witnessUtxo.value, + sighashType, + ); + } else { + if (!input.witnessScript) + throw new Error('Segwit input needs witnessScript if not P2WPKH'); + checkWitnessScript(inputIndex, script, input.witnessScript); + hash = unsignedTx.hashForWitnessV0( + inputIndex, + script, + input.witnessUtxo.value, + sighashType, + ); + } + } else { + throw new Error('Need a Utxo input item for signing'); } - // TODO: Get hash to sign - const hash = Buffer.alloc(32); const partialSig = { pubkey: keyPair.publicKey, - signature: script.signature.encode( - keyPair.sign(hash), - input.sighashType || 0x01, - ), + signature: bscript.signature.encode(keyPair.sign(hash), sighashType), }; return this.addPartialSigToInput(inputIndex, partialSig); } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 63bc057..5ec9ef1 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,25 +1,45 @@ import { Psbt as PsbtBase } from 'bip174'; import { Signer } from './ecpair'; import * as payments from './payments'; -import * as script from './script'; +import * as bscript from './script'; import { Transaction } from './transaction'; -const checkRedeemScript = ( +type ScriptCheckerFunction = (idx: number, spk: Buffer, rs: Buffer) => void; + +const scriptCheckerFactory = ( + payment: any, + paymentScriptName: string, +): ScriptCheckerFunction => ( inputIndex: number, scriptPubKey: Buffer, redeemScript: Buffer, ): void => { - const redeemScriptOutput = payments.p2sh({ + const redeemScriptOutput = payment({ redeem: { output: redeemScript }, }).output as Buffer; if (!scriptPubKey.equals(redeemScriptOutput)) { throw new Error( - `Redeem script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, ); } }; +const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script'); +const checkWitnessScript = scriptCheckerFactory( + payments.p2wsh, + 'Witness script', +); + +const isP2WPKH = (script: Buffer): boolean => { + try { + payments.p2wpkh({ output: script }); + return true; + } catch (err) { + return false; + } +}; + export class Psbt extends PsbtBase { constructor() { super(); @@ -53,8 +73,11 @@ export class Psbt extends PsbtBase { const input = this.inputs[inputIndex]; if (input === undefined) throw new Error(`No input #${inputIndex}`); + const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!); + const sighashType = input.sighashType || 0x01; + let hash: Buffer; + if (input.nonWitnessUtxo) { - const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!); const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); const prevoutHash = unsignedTx.ins[inputIndex].hash; @@ -67,14 +90,26 @@ export class Psbt extends PsbtBase { ); } - if (input.redeemScript) { - const prevoutIndex = unsignedTx.ins[inputIndex].index; - const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; + const prevoutIndex = unsignedTx.ins[inputIndex].index; + const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; + if (input.redeemScript) { // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript(inputIndex, prevout.script, input.redeemScript); + hash = unsignedTx.hashForSignature( + inputIndex, + input.redeemScript, + sighashType, + ); + } else { + hash = unsignedTx.hashForSignature( + inputIndex, + prevout.script, + sighashType, + ); } } else if (input.witnessUtxo) { + let script: Buffer; if (input.redeemScript) { // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript( @@ -82,18 +117,37 @@ export class Psbt extends PsbtBase { input.witnessUtxo.script, input.redeemScript, ); + script = input.redeemScript; + } else { + script = input.witnessUtxo.script; } + if (isP2WPKH(script)) { + // P2WPKH uses the P2PKH template for prevoutScript when signing + const signingScript = payments.p2pkh({ hash: script.slice(2) }).output!; + hash = unsignedTx.hashForWitnessV0( + inputIndex, + signingScript, + input.witnessUtxo.value, + sighashType, + ); + } else { + if (!input.witnessScript) + throw new Error('Segwit input needs witnessScript if not P2WPKH'); + checkWitnessScript(inputIndex, script, input.witnessScript); + hash = unsignedTx.hashForWitnessV0( + inputIndex, + script, + input.witnessUtxo.value, + sighashType, + ); + } + } else { + throw new Error('Need a Utxo input item for signing'); } - // TODO: Get hash to sign - const hash = Buffer.alloc(32); - const partialSig = { pubkey: keyPair.publicKey, - signature: script.signature.encode( - keyPair.sign(hash), - input.sighashType || 0x01, - ), + signature: bscript.signature.encode(keyPair.sign(hash), sighashType), }; return this.addPartialSigToInput(inputIndex, partialSig); From f72c915ff150188575c0766ee4ab6dbc622bfed0 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 1 Jul 2019 19:57:35 +0900 Subject: [PATCH 333/568] Start towards finalizing inputs --- package-lock.json | 6 ++--- package.json | 2 +- src/psbt.js | 60 ++++++++++++++++++++++++++++++++++++++++--- ts_src/psbt.ts | 65 ++++++++++++++++++++++++++++++++++++++++++++--- types/psbt.d.ts | 5 +++- 5 files changed, 125 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index d409322..4195bf2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.8.tgz", - "integrity": "sha512-xWPzmlCvLoOWTlXk1wG7+TyOfaN8xX07IieuG4ug5su3igC9s4Lsdq+IEEMo+YHDQ4hPPAX9LYio6aEIAA+Zrg==" + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.10.tgz", + "integrity": "sha512-gFtSEMayg7HPKGnIQcEx5CqD/qHWuMlxLJ/+VV4k4Q2mcA0rY040JbNpFuCGVI6rJYv211f0NA7nkU4xkPX4nQ==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index b964697..1346928 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.8", + "bip174": "0.0.10", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", diff --git a/src/psbt.js b/src/psbt.js index 9a9ad7e..9d00e41 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -1,6 +1,8 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); +const utils_1 = require('bip174/src/lib/utils'); +const classify = require('./classify'); const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); @@ -23,17 +25,67 @@ const checkWitnessScript = scriptCheckerFactory( payments.p2wsh, 'Witness script', ); -const isP2WPKH = script => { +const isPayment = (script, payment) => { try { - payments.p2wpkh({ output: script }); + payment({ output: script }); return true; } catch (err) { return false; } }; +function getScriptFromInput(inputIndex, input, _unsignedTx) { + let script; + if (input.nonWitnessUtxo) { + if (input.redeemScript) { + script = input.redeemScript; + } else { + const unsignedTx = transaction_1.Transaction.fromBuffer(_unsignedTx); + const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( + input.nonWitnessUtxo, + ); + const prevoutIndex = unsignedTx.ins[inputIndex].index; + script = nonWitnessUtxoTx.outs[prevoutIndex].script; + } + } else if (input.witnessUtxo) { + if (input.witnessScript) { + script = input.witnessScript; + } else if (input.redeemScript) { + script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output; + } else { + script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) }) + .output; + } + } else { + return; + } + return script; +} class Psbt extends bip174_1.Psbt { - constructor() { + constructor(network) { super(); + this.network = network; + } + canFinalize(inputIndex) { + const input = utils_1.checkForInput(this.inputs, inputIndex); + const script = getScriptFromInput( + inputIndex, + input, + this.globalMap.unsignedTx, + ); + if (!script) return false; + const scriptType = classify.output(script); + switch (scriptType) { + case 'pubkey': + return false; + case 'pubkeyhash': + return false; + case 'multisig': + return false; + case 'witnesspubkeyhash': + return false; + default: + return false; + } } signInput(inputIndex, keyPair) { // TODO: Implement BIP174 pre-sign checks: @@ -108,7 +160,7 @@ class Psbt extends bip174_1.Psbt { } else { script = input.witnessUtxo.script; } - if (isP2WPKH(script)) { + if (isPayment(script, payments.p2wpkh)) { // P2WPKH uses the P2PKH template for prevoutScript when signing const signingScript = payments.p2pkh({ hash: script.slice(2) }).output; hash = unsignedTx.hashForWitnessV0( diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 5ec9ef1..2f0b6ea 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,5 +1,9 @@ import { Psbt as PsbtBase } from 'bip174'; +import { PsbtInput } from 'bip174/src/lib/interfaces'; +import { checkForInput } from 'bip174/src/lib/utils'; +import * as classify from './classify'; import { Signer } from './ecpair'; +import { Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; import { Transaction } from './transaction'; @@ -31,20 +35,73 @@ const checkWitnessScript = scriptCheckerFactory( 'Witness script', ); -const isP2WPKH = (script: Buffer): boolean => { +const isPayment = (script: Buffer, payment: any): boolean => { try { - payments.p2wpkh({ output: script }); + payment({ output: script }); return true; } catch (err) { return false; } }; +function getScriptFromInput( + inputIndex: number, + input: PsbtInput, + _unsignedTx: Buffer, +): Buffer | undefined { + let script: Buffer; + if (input.nonWitnessUtxo) { + if (input.redeemScript) { + script = input.redeemScript; + } else { + const unsignedTx = Transaction.fromBuffer(_unsignedTx); + const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); + const prevoutIndex = unsignedTx.ins[inputIndex].index; + script = nonWitnessUtxoTx.outs[prevoutIndex].script; + } + } else if (input.witnessUtxo) { + if (input.witnessScript) { + script = input.witnessScript; + } else if (input.redeemScript) { + script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output!; + } else { + script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) }) + .output!; + } + } else { + return; + } + return script; +} + export class Psbt extends PsbtBase { - constructor() { + constructor(public network?: Network) { super(); } + canFinalize(inputIndex: number): boolean { + const input = checkForInput(this.inputs, inputIndex); + const script = getScriptFromInput( + inputIndex, + input, + this.globalMap.unsignedTx!, + ); + if (!script) return false; + const scriptType = classify.output(script); + switch (scriptType) { + case 'pubkey': + return false; + case 'pubkeyhash': + return false; + case 'multisig': + return false; + case 'witnesspubkeyhash': + return false; + default: + return false; + } + } + signInput(inputIndex: number, keyPair: Signer): Psbt { // TODO: Implement BIP174 pre-sign checks: // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer @@ -121,7 +178,7 @@ export class Psbt extends PsbtBase { } else { script = input.witnessUtxo.script; } - if (isP2WPKH(script)) { + if (isPayment(script, payments.p2wpkh)) { // P2WPKH uses the P2PKH template for prevoutScript when signing const signingScript = payments.p2pkh({ hash: script.slice(2) }).output!; hash = unsignedTx.hashForWitnessV0( diff --git a/types/psbt.d.ts b/types/psbt.d.ts index a58b982..fda7e6b 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,6 +1,9 @@ import { Psbt as PsbtBase } from 'bip174'; import { Signer } from './ecpair'; +import { Network } from './networks'; export declare class Psbt extends PsbtBase { - constructor(); + network?: Network | undefined; + constructor(network?: Network | undefined); + canFinalize(inputIndex: number): boolean; signInput(inputIndex: number, keyPair: Signer): Psbt; } From f28e9cef71d457366e97423ca254e8c6da28dcda Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 2 Jul 2019 15:03:24 +0900 Subject: [PATCH 334/568] Refactor - Clean up sign - Get the meaningful script - Search for pubkey and prevent sign if can't find self - Tests failed, so comment out for now --- src/psbt.js | 289 +++++++++++++++++++++++--------------------- ts_src/psbt.ts | 318 ++++++++++++++++++++++++++++--------------------- 2 files changed, 335 insertions(+), 272 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 9d00e41..fa4ba9f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -2,10 +2,153 @@ Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); const utils_1 = require('bip174/src/lib/utils'); -const classify = require('./classify'); const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); +class Psbt extends bip174_1.Psbt { + constructor(network) { + super(); + this.network = network; + } + canFinalize(inputIndex) { + const input = utils_1.checkForInput(this.inputs, inputIndex); + const script = getScriptFromInput( + inputIndex, + input, + this.globalMap.unsignedTx, + ); + if (!script) return false; + const scriptType = classifyScript(script); + // TODO: for each type + switch (scriptType) { + case 'pubkey': + return false; + case 'pubkeyhash': + return false; + case 'multisig': + return false; + case 'witnesspubkeyhash': + return false; + default: + return false; + } + } + signInput(inputIndex, keyPair) { + const input = this.inputs[inputIndex]; + if (input === undefined) throw new Error(`No input #${inputIndex}`); + const { hash, sighashType } = getHashForSig( + inputIndex, + input, + this.globalMap.unsignedTx, + ); + const pubkey = keyPair.publicKey; + // // TODO: throw error when the pubkey or pubkey hash is not found anywhere + // // in the script + // const pubkeyHash = hash160(keyPair.publicKey); + // + // const decompiled = bscript.decompile(script); + // if (decompiled === null) throw new Error('Unknown script error'); + // + // const hasKey = decompiled.some(element => { + // if (typeof element === 'number') return false; + // return element.equals(pubkey) || element.equals(pubkeyHash); + // }); + // + // if (!hasKey) { + // throw new Error( + // `Can not sign for this input with the key ${pubkey.toString('hex')}`, + // ); + // } + const partialSig = { + pubkey, + signature: bscript.signature.encode(keyPair.sign(hash), sighashType), + }; + return this.addPartialSigToInput(inputIndex, partialSig); + } +} +exports.Psbt = Psbt; +const getHashForSig = (inputIndex, input, txBuf) => { + const unsignedTx = transaction_1.Transaction.fromBuffer(txBuf); + const sighashType = + input.sighashType || transaction_1.Transaction.SIGHASH_ALL; + let hash; + let script; + if (input.nonWitnessUtxo) { + const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( + input.nonWitnessUtxo, + ); + const prevoutHash = unsignedTx.ins[inputIndex].hash; + const utxoHash = nonWitnessUtxoTx.getHash(); + // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout + if (!prevoutHash.equals(utxoHash)) { + throw new Error( + `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, + ); + } + const prevoutIndex = unsignedTx.ins[inputIndex].index; + const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; + if (input.redeemScript) { + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + checkRedeemScript(inputIndex, prevout.script, input.redeemScript); + script = input.redeemScript; + hash = unsignedTx.hashForSignature( + inputIndex, + input.redeemScript, + sighashType, + ); + } else { + script = prevout.script; + hash = unsignedTx.hashForSignature( + inputIndex, + prevout.script, + sighashType, + ); + } + } else if (input.witnessUtxo) { + let _script; // so we don't shadow the `let script` above + if (input.redeemScript) { + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + checkRedeemScript( + inputIndex, + input.witnessUtxo.script, + input.redeemScript, + ); + _script = input.redeemScript; + } else { + _script = input.witnessUtxo.script; + } + if (isP2WPKH(_script)) { + // P2WPKH uses the P2PKH template for prevoutScript when signing + const signingScript = payments.p2pkh({ hash: _script.slice(2) }).output; + hash = unsignedTx.hashForWitnessV0( + inputIndex, + signingScript, + input.witnessUtxo.value, + sighashType, + ); + script = _script; + } else { + if (!input.witnessScript) + throw new Error('Segwit input needs witnessScript if not P2WPKH'); + checkWitnessScript(inputIndex, _script, input.witnessScript); + hash = unsignedTx.hashForWitnessV0( + inputIndex, + _script, + input.witnessUtxo.value, + sighashType, + ); + // want to make sure the script we return is the actual meaningful script + script = input.witnessScript; + } + } else { + throw new Error('Need a Utxo input item for signing'); + } + return { + script, + sighashType, + hash, + }; +}; const scriptCheckerFactory = (payment, paymentScriptName) => ( inputIndex, scriptPubKey, @@ -25,7 +168,7 @@ const checkWitnessScript = scriptCheckerFactory( payments.p2wsh, 'Witness script', ); -const isPayment = (script, payment) => { +const isPaymentFactory = payment => script => { try { payment({ output: script }); return true; @@ -33,6 +176,17 @@ const isPayment = (script, payment) => { return false; } }; +const isP2WPKH = isPaymentFactory(payments.p2wpkh); +const isP2PKH = isPaymentFactory(payments.p2pkh); +const isP2MS = isPaymentFactory(payments.p2ms); +const isP2PK = isPaymentFactory(payments.p2pk); +const classifyScript = script => { + if (isP2WPKH(script)) return 'witnesspubkeyhash'; + if (isP2PKH(script)) return 'pubkeyhash'; + if (isP2MS(script)) return 'multisig'; + if (isP2PK(script)) return 'pubkey'; + return 'nonstandard'; +}; function getScriptFromInput(inputIndex, input, _unsignedTx) { let script; if (input.nonWitnessUtxo) { @@ -60,134 +214,3 @@ function getScriptFromInput(inputIndex, input, _unsignedTx) { } return script; } -class Psbt extends bip174_1.Psbt { - constructor(network) { - super(); - this.network = network; - } - canFinalize(inputIndex) { - const input = utils_1.checkForInput(this.inputs, inputIndex); - const script = getScriptFromInput( - inputIndex, - input, - this.globalMap.unsignedTx, - ); - if (!script) return false; - const scriptType = classify.output(script); - switch (scriptType) { - case 'pubkey': - return false; - case 'pubkeyhash': - return false; - case 'multisig': - return false; - case 'witnesspubkeyhash': - return false; - default: - return false; - } - } - signInput(inputIndex, keyPair) { - // TODO: Implement BIP174 pre-sign checks: - // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer - // - // if non_witness_utxo.exists: - // assert(sha256d(non_witness_utxo) == psbt.tx.innput[i].prevout.hash) - // if redeemScript.exists: - // assert(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey == P2SH(redeemScript)) - // sign_non_witness(redeemScript) - // else: - // sign_non_witness(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey) - // else if witness_utxo.exists: - // if redeemScript.exists: - // assert(witness_utxo.scriptPubKey == P2SH(redeemScript)) - // script = redeemScript - // else: - // script = witness_utxo.scriptPubKey - // if IsP2WPKH(script): - // sign_witness(P2PKH(script[2:22])) - // else if IsP2WSH(script): - // assert(script == P2WSH(witnessScript)) - // sign_witness(witnessScript) - // else: - // assert False - const input = this.inputs[inputIndex]; - if (input === undefined) throw new Error(`No input #${inputIndex}`); - const unsignedTx = transaction_1.Transaction.fromBuffer( - this.globalMap.unsignedTx, - ); - const sighashType = input.sighashType || 0x01; - let hash; - if (input.nonWitnessUtxo) { - const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( - input.nonWitnessUtxo, - ); - const prevoutHash = unsignedTx.ins[inputIndex].hash; - const utxoHash = nonWitnessUtxoTx.getHash(); - // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout - if (!prevoutHash.equals(utxoHash)) { - throw new Error( - `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, - ); - } - const prevoutIndex = unsignedTx.ins[inputIndex].index; - const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; - if (input.redeemScript) { - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - checkRedeemScript(inputIndex, prevout.script, input.redeemScript); - hash = unsignedTx.hashForSignature( - inputIndex, - input.redeemScript, - sighashType, - ); - } else { - hash = unsignedTx.hashForSignature( - inputIndex, - prevout.script, - sighashType, - ); - } - } else if (input.witnessUtxo) { - let script; - if (input.redeemScript) { - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - checkRedeemScript( - inputIndex, - input.witnessUtxo.script, - input.redeemScript, - ); - script = input.redeemScript; - } else { - script = input.witnessUtxo.script; - } - if (isPayment(script, payments.p2wpkh)) { - // P2WPKH uses the P2PKH template for prevoutScript when signing - const signingScript = payments.p2pkh({ hash: script.slice(2) }).output; - hash = unsignedTx.hashForWitnessV0( - inputIndex, - signingScript, - input.witnessUtxo.value, - sighashType, - ); - } else { - if (!input.witnessScript) - throw new Error('Segwit input needs witnessScript if not P2WPKH'); - checkWitnessScript(inputIndex, script, input.witnessScript); - hash = unsignedTx.hashForWitnessV0( - inputIndex, - script, - input.witnessUtxo.value, - sighashType, - ); - } - } else { - throw new Error('Need a Utxo input item for signing'); - } - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(keyPair.sign(hash), sighashType), - }; - return this.addPartialSigToInput(inputIndex, partialSig); - } -} -exports.Psbt = Psbt; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 2f0b6ea..63cc318 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,13 +1,174 @@ import { Psbt as PsbtBase } from 'bip174'; import { PsbtInput } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; -import * as classify from './classify'; +// import { hash160 } from './crypto'; // TODO: used in pubkey check import { Signer } from './ecpair'; import { Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; import { Transaction } from './transaction'; +export class Psbt extends PsbtBase { + constructor(public network?: Network) { + super(); + } + + canFinalize(inputIndex: number): boolean { + const input = checkForInput(this.inputs, inputIndex); + const script = getScriptFromInput( + inputIndex, + input, + this.globalMap.unsignedTx!, + ); + if (!script) return false; + const scriptType = classifyScript(script); + // TODO: for each type + switch (scriptType) { + case 'pubkey': + return false; + case 'pubkeyhash': + return false; + case 'multisig': + return false; + case 'witnesspubkeyhash': + return false; + default: + return false; + } + } + + signInput(inputIndex: number, keyPair: Signer): Psbt { + const input = this.inputs[inputIndex]; + if (input === undefined) throw new Error(`No input #${inputIndex}`); + const { + hash, + sighashType, + // script, // TODO: use for pubkey check below + } = getHashForSig(inputIndex, input, this.globalMap.unsignedTx!); + + const pubkey = keyPair.publicKey; + // // TODO: throw error when the pubkey or pubkey hash is not found anywhere + // // in the script + // const pubkeyHash = hash160(keyPair.publicKey); + // + // const decompiled = bscript.decompile(script); + // if (decompiled === null) throw new Error('Unknown script error'); + // + // const hasKey = decompiled.some(element => { + // if (typeof element === 'number') return false; + // return element.equals(pubkey) || element.equals(pubkeyHash); + // }); + // + // if (!hasKey) { + // throw new Error( + // `Can not sign for this input with the key ${pubkey.toString('hex')}`, + // ); + // } + + const partialSig = { + pubkey, + signature: bscript.signature.encode(keyPair.sign(hash), sighashType), + }; + + return this.addPartialSigToInput(inputIndex, partialSig); + } +} + +interface HashForSigData { + script: Buffer; + hash: Buffer; + sighashType: number; +} + +const getHashForSig = ( + inputIndex: number, + input: PsbtInput, + txBuf: Buffer, +): HashForSigData => { + const unsignedTx = Transaction.fromBuffer(txBuf); + const sighashType = input.sighashType || Transaction.SIGHASH_ALL; + let hash: Buffer; + let script: Buffer; + + if (input.nonWitnessUtxo) { + const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); + + const prevoutHash = unsignedTx.ins[inputIndex].hash; + const utxoHash = nonWitnessUtxoTx.getHash(); + + // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout + if (!prevoutHash.equals(utxoHash)) { + throw new Error( + `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, + ); + } + + const prevoutIndex = unsignedTx.ins[inputIndex].index; + const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; + + if (input.redeemScript) { + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + checkRedeemScript(inputIndex, prevout.script, input.redeemScript); + script = input.redeemScript; + hash = unsignedTx.hashForSignature( + inputIndex, + input.redeemScript, + sighashType, + ); + } else { + script = prevout.script; + hash = unsignedTx.hashForSignature( + inputIndex, + prevout.script, + sighashType, + ); + } + } else if (input.witnessUtxo) { + let _script: Buffer; // so we don't shadow the `let script` above + if (input.redeemScript) { + // If a redeemScript is provided, the scriptPubKey must be for that redeemScript + checkRedeemScript( + inputIndex, + input.witnessUtxo.script, + input.redeemScript, + ); + _script = input.redeemScript; + } else { + _script = input.witnessUtxo.script; + } + if (isP2WPKH(_script)) { + // P2WPKH uses the P2PKH template for prevoutScript when signing + const signingScript = payments.p2pkh({ hash: _script.slice(2) }).output!; + hash = unsignedTx.hashForWitnessV0( + inputIndex, + signingScript, + input.witnessUtxo.value, + sighashType, + ); + script = _script; + } else { + if (!input.witnessScript) + throw new Error('Segwit input needs witnessScript if not P2WPKH'); + checkWitnessScript(inputIndex, _script, input.witnessScript); + hash = unsignedTx.hashForWitnessV0( + inputIndex, + _script, + input.witnessUtxo.value, + sighashType, + ); + // want to make sure the script we return is the actual meaningful script + script = input.witnessScript; + } + } else { + throw new Error('Need a Utxo input item for signing'); + } + return { + script, + sighashType, + hash, + }; +}; + type ScriptCheckerFunction = (idx: number, spk: Buffer, rs: Buffer) => void; const scriptCheckerFactory = ( @@ -35,7 +196,11 @@ const checkWitnessScript = scriptCheckerFactory( 'Witness script', ); -const isPayment = (script: Buffer, payment: any): boolean => { +type isPaymentFunction = (script: Buffer) => boolean; + +const isPaymentFactory = (payment: any): isPaymentFunction => ( + script: Buffer, +): boolean => { try { payment({ output: script }); return true; @@ -43,6 +208,18 @@ const isPayment = (script: Buffer, payment: any): boolean => { return false; } }; +const isP2WPKH = isPaymentFactory(payments.p2wpkh); +const isP2PKH = isPaymentFactory(payments.p2pkh); +const isP2MS = isPaymentFactory(payments.p2ms); +const isP2PK = isPaymentFactory(payments.p2pk); + +const classifyScript = (script: Buffer): string => { + if (isP2WPKH(script)) return 'witnesspubkeyhash'; + if (isP2PKH(script)) return 'pubkeyhash'; + if (isP2MS(script)) return 'multisig'; + if (isP2PK(script)) return 'pubkey'; + return 'nonstandard'; +}; function getScriptFromInput( inputIndex: number, @@ -73,140 +250,3 @@ function getScriptFromInput( } return script; } - -export class Psbt extends PsbtBase { - constructor(public network?: Network) { - super(); - } - - canFinalize(inputIndex: number): boolean { - const input = checkForInput(this.inputs, inputIndex); - const script = getScriptFromInput( - inputIndex, - input, - this.globalMap.unsignedTx!, - ); - if (!script) return false; - const scriptType = classify.output(script); - switch (scriptType) { - case 'pubkey': - return false; - case 'pubkeyhash': - return false; - case 'multisig': - return false; - case 'witnesspubkeyhash': - return false; - default: - return false; - } - } - - signInput(inputIndex: number, keyPair: Signer): Psbt { - // TODO: Implement BIP174 pre-sign checks: - // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer - // - // if non_witness_utxo.exists: - // assert(sha256d(non_witness_utxo) == psbt.tx.innput[i].prevout.hash) - // if redeemScript.exists: - // assert(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey == P2SH(redeemScript)) - // sign_non_witness(redeemScript) - // else: - // sign_non_witness(non_witness_utxo.vout[psbt.tx.input[i].prevout.n].scriptPubKey) - // else if witness_utxo.exists: - // if redeemScript.exists: - // assert(witness_utxo.scriptPubKey == P2SH(redeemScript)) - // script = redeemScript - // else: - // script = witness_utxo.scriptPubKey - // if IsP2WPKH(script): - // sign_witness(P2PKH(script[2:22])) - // else if IsP2WSH(script): - // assert(script == P2WSH(witnessScript)) - // sign_witness(witnessScript) - // else: - // assert False - - const input = this.inputs[inputIndex]; - if (input === undefined) throw new Error(`No input #${inputIndex}`); - - const unsignedTx = Transaction.fromBuffer(this.globalMap.unsignedTx!); - const sighashType = input.sighashType || 0x01; - let hash: Buffer; - - if (input.nonWitnessUtxo) { - const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); - - const prevoutHash = unsignedTx.ins[inputIndex].hash; - const utxoHash = nonWitnessUtxoTx.getHash(); - - // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout - if (!prevoutHash.equals(utxoHash)) { - throw new Error( - `Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`, - ); - } - - const prevoutIndex = unsignedTx.ins[inputIndex].index; - const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; - - if (input.redeemScript) { - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - checkRedeemScript(inputIndex, prevout.script, input.redeemScript); - hash = unsignedTx.hashForSignature( - inputIndex, - input.redeemScript, - sighashType, - ); - } else { - hash = unsignedTx.hashForSignature( - inputIndex, - prevout.script, - sighashType, - ); - } - } else if (input.witnessUtxo) { - let script: Buffer; - if (input.redeemScript) { - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - checkRedeemScript( - inputIndex, - input.witnessUtxo.script, - input.redeemScript, - ); - script = input.redeemScript; - } else { - script = input.witnessUtxo.script; - } - if (isPayment(script, payments.p2wpkh)) { - // P2WPKH uses the P2PKH template for prevoutScript when signing - const signingScript = payments.p2pkh({ hash: script.slice(2) }).output!; - hash = unsignedTx.hashForWitnessV0( - inputIndex, - signingScript, - input.witnessUtxo.value, - sighashType, - ); - } else { - if (!input.witnessScript) - throw new Error('Segwit input needs witnessScript if not P2WPKH'); - checkWitnessScript(inputIndex, script, input.witnessScript); - hash = unsignedTx.hashForWitnessV0( - inputIndex, - script, - input.witnessUtxo.value, - sighashType, - ); - } - } else { - throw new Error('Need a Utxo input item for signing'); - } - - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(keyPair.sign(hash), sighashType), - }; - - return this.addPartialSigToInput(inputIndex, partialSig); - } -} From 4644e9d2ebc65fe7822070c6ed3bbe2821e5a9e7 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 2 Jul 2019 15:18:00 +0900 Subject: [PATCH 335/568] Finish canFinalize --- src/psbt.js | 16 +++++++++++----- ts_src/psbt.ts | 18 +++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index fa4ba9f..cf1edca 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -19,16 +19,22 @@ class Psbt extends bip174_1.Psbt { ); if (!script) return false; const scriptType = classifyScript(script); - // TODO: for each type + const hasSigs = (neededSigs, partialSig) => { + if (!partialSig) return false; + if (partialSig.length > neededSigs) + throw new Error('Too many signatures'); + return partialSig.length === neededSigs; + }; switch (scriptType) { case 'pubkey': - return false; + return hasSigs(1, input.partialSig); case 'pubkeyhash': - return false; + return hasSigs(1, input.partialSig); case 'multisig': - return false; + const p2ms = payments.p2ms({ output: script }); + return hasSigs(p2ms.m, input.partialSig); case 'witnesspubkeyhash': - return false; + return hasSigs(1, input.partialSig); default: return false; } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 63cc318..b1190aa 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -22,16 +22,24 @@ export class Psbt extends PsbtBase { ); if (!script) return false; const scriptType = classifyScript(script); - // TODO: for each type + + const hasSigs = (neededSigs: number, partialSig?: any[]): boolean => { + if (!partialSig) return false; + if (partialSig.length > neededSigs) + throw new Error('Too many signatures'); + return partialSig.length === neededSigs; + }; + switch (scriptType) { case 'pubkey': - return false; + return hasSigs(1, input.partialSig); case 'pubkeyhash': - return false; + return hasSigs(1, input.partialSig); case 'multisig': - return false; + const p2ms = payments.p2ms({ output: script }); + return hasSigs(p2ms.m!, input.partialSig); case 'witnesspubkeyhash': - return false; + return hasSigs(1, input.partialSig); default: return false; } From 354d67a31aaca6c04dc352d361371810ed8ddfa1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 2 Jul 2019 15:35:23 +0900 Subject: [PATCH 336/568] Just some ideas, TODO mostly. --- src/psbt.js | 12 ++++++++++++ ts_src/psbt.ts | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index cf1edca..651aeb2 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -6,9 +6,21 @@ const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); class Psbt extends bip174_1.Psbt { + // protected __TX: Transaction; constructor(network) { super(); this.network = network; + // // TODO: figure out a way to use a Transaction Object instead of a Buffer + // // TODO: Caching, since .toBuffer() calls every time we get is lame. + // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); + // delete this.globalMap.unsignedTx; + // Object.defineProperty(this.globalMap, 'unsignedTx', { + // enumerable: true, + // writable: false, + // get(): Buffer { + // return this.__TX.toBuffer(); + // } + // }); } canFinalize(inputIndex) { const input = utils_1.checkForInput(this.inputs, inputIndex); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index b1190aa..cf60f91 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -9,8 +9,20 @@ import * as bscript from './script'; import { Transaction } from './transaction'; export class Psbt extends PsbtBase { + // protected __TX: Transaction; constructor(public network?: Network) { super(); + // // TODO: figure out a way to use a Transaction Object instead of a Buffer + // // TODO: Caching, since .toBuffer() calls every time we get is lame. + // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); + // delete this.globalMap.unsignedTx; + // Object.defineProperty(this.globalMap, 'unsignedTx', { + // enumerable: true, + // writable: false, + // get(): Buffer { + // return this.__TX.toBuffer(); + // } + // }); } canFinalize(inputIndex: number): boolean { From 7ff40cebc4abb81b83cb8c0a287e066d8d64e094 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 2 Jul 2019 18:15:30 +0700 Subject: [PATCH 337/568] Recreate test case PSBTs and try and sign them with the valid key --- test/fixtures/psbt.json | 32 +++++++++++++++++++------------- test/psbt.js | 13 ++++++++----- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 1b72874..4aaec98 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -4,37 +4,43 @@ { "description": "checks non-witness UTXO matches the hash specified in the prevout", "shouldSign": { - "psbt": "cHNidP8BADMBAAAAAZIApPIHomdoPo/sOJ8NI9jzILd0cnGn0yAZHRv3D9DSAAAAAAD/////AAAAAAAAAQD9tQEBAAAAAn3dz3mi5UEDC8dThx0cnU3BY+TWvVrvrkvYTeZOFqZSAAAAAItIMEUCIQDTosO1iuDwtVFxGqiUn0eHJEKO+gPzF5w6UNwsms5GqgIgGy2oSiFCmhCvGHcxyIL8H3J+e4lXPgfwGS6ePeefq/ABQQQOOnWcM7A+Gvjl2G+0R6QO/yRMhHpPgnQnbbSQBU6L4Hb4gB3cnFJG7oa28zz+OOi35Xq52zkOs+wexq6e7qET/f///0/vbX88Hl0L6nM7L9ZE+kVs33PyHrfohmonIdeSZunoAQAAAIpHMEQCIChKKYnUXEimyKVWsws0Z+r2q/hm7HXE2fDjhyB09iwHAiBoatgoacFmnmvhYsSjTkxhepcXZvK5aIeJ6y9Jj+XrawFBBA46dZwzsD4a+OXYb7RHpA7/JEyEek+CdCdttJAFTovgdviAHdycUkbuhrbzPP446LflernbOQ6z7B7Grp7uoRP9////AiTHDQAAAAAAGXapFNpkc+03Pgj0bdgAP8p7py++nFVeiKycsA4AAAAAABl2qRRJcHmSWY+FoxqmcVr3D+UHYQtvi4isEcAIAAAA", - "inputToCheck": 0 + "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" }, "shouldThrow": { "errorMessage": "Non-witness UTXO hash for input #0 doesn't match the hash specified in the prevout", - "psbt": "cHNidP8BADMBAAAAAZIApPIHomdoPo/sOJ8NI9jzILd0cnGn0yAZHRv3D9DSAAAAAAD/////AAAAAAAAAQC/AgAAAAHBYCumjIwkFFCni2Hb/eJymJGB0HU3secNMbfbk5VX8gAAAABqRzBEAiAphyuXV5hQyHZY5DG7nfSj8+QVkHd1KaVeJesR7Mr+9QIgURcAqh6iws1JklH5kBTyLFr2OgDHb9JNplABSgqBmekBIQJkGH2e53OqMzrCI2eEeLHfPqJoF4/JRH4KYMRD7dqnSf3///8BmV0AAAAAAAAZdqkUdZ1mdwkelzuenZnxnGj79D4/BfmIrMjTCAAAAA==", - "inputToCheck": 0 + "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQD4AQAAAAABAbD7u8z1SxTjfvhwmkQQvdbbWA+n3GKBBmGecSIAaM5jBQAAABcWABS0SqIhdn2LbW4TAJc3GVh7SnD/eP////8CNg8AAAAAAAAWABSmNm8WWVF+wq+QAeRo9d763jEXhRAnAAAAAAAAGXapFNpkc+03Pgj0bdgAP8p7py++nFVeiKwCRzBEAiB/u0BLwdeerqWf0JH33wwMv8Nn3sKblFvj+CntdC4B9gIgKVVHBH1c9ewnzkuyW6dnz1YARujBJnle1eBNSBAJD9IBIQOmYxHmd2Yz53FpC9+nv+pKdM+5OyEAW3BAN2cccQ0LkgAAAAAAAA==", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" } }, { "description": "checks redeem script matches the scriptPubKey in a non-witness prevout", "shouldSign": { - "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuAAA=", - "inputToCheck": 0 + "psbt": "cHNidP8BADMBAAAAAR2dq8JwBaxnbWHZGw0HdxuUGFcg6dvx3pgjWMm+Pzf2AAAAAAD/////AAAAAAAAAQC9AgAAAAH//////////////////////////////////////////wAAAABqRzBEAiAf7N+IK1uuxTxvEOoVGabNsiT7jMlfSDCd0VYxv+sQTQIgQVYM7ig9TIx1LzrX2RXgw2zW2fMKuRs/bT9eZx6jmYwBIQJpKKFOB6PrPJhRAtaQ+cHHryY5QYIi5dxZtkMwCtuFYf////8BAOH1BQAAAAAXqRRdh8wk5NRiF7VGQ4Zb4i8Vl1YFMocAAAAAAQRpUiECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWEhAgHGF3SgP82qhvqMptNluTLHhLtzDsmc0pNWEDETNj/rIQIFIl+T3Z90vBFGN8uYJHCrUO4DvrOGVWkVDsBeEzBUi1OuAAA=", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" }, "shouldThrow": { "errorMessage": "Redeem script for input #0 doesn't match the scriptPubKey in the prevout", - "psbt": "cHNidP8BADMBAAAAAVjoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////AAAAAAAAAQC7AgAAAAGq1zkxAYvSX4SuQAtohIvgnbcG6sKsGCmLq+5xq2VviwAAAABIRzBEAiBY9vx8ajPhsxVI1IHIJsAVvTATWq1CzWd5Datm0q0kOwIgShztJgTGc1tjk+W0FpHdeLAPDFlC+591GFb6qTgVfboB/v///wKA8PoCAAAAABepFA+5RjQhaWuCyDOvJBx4wX3b3kk0h9DyCicBAAAAF6kUKcp0+KCPgZmUKBhcl7XYUuQGP2GHZQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMAAA==", - "inputToCheck": 0 + "psbt": "cHNidP8BADMBAAAAAR2dq8JwBaxnbWHZGw0HdxuUGFcg6dvx3pgjWMm+Pzf2AAAAAAD/////AAAAAAAAAQC9AgAAAAH//////////////////////////////////////////wAAAABqRzBEAiAf7N+IK1uuxTxvEOoVGabNsiT7jMlfSDCd0VYxv+sQTQIgQVYM7ig9TIx1LzrX2RXgw2zW2fMKuRs/bT9eZx6jmYwBIQJpKKFOB6PrPJhRAtaQ+cHHryY5QYIi5dxZtkMwCtuFYf////8BAOH1BQAAAAAXqRRdh8wk5NRiF7VGQ4Zb4i8Vl1YFMocAAAAAAQRpUiEDGMZFrWWJBIIu33FdV9Q+Zit0fcoBOdgS7ooA2h2QlbAhAuAzQeDZh730hBbfTPzlaXJgCh2Jyui/ufS0k8wqJ55FIQKMg6lgEnyRnGIZ90eP4MmuRdT3EcO4+irJEm5yTCiko1OuAAA=", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" } }, { "description": "checks redeem script matches the scriptPubKey in a witness prevout", "shouldSign": { - "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", - "inputToCheck": 1 + "psbt": "cHNidP8BADMBAAAAAYaq+PdOUY2PnV9kZKa82XlqrPByOqwH2TRg2+LQdqs2AAAAAAD/////AAAAAAAAAQEgAOH1BQAAAAAXqRSTNeWHqa9INvSnQ120SZeJc+6JSocBBBYAFC8spHIOpiw9giaEPd5RGkMYvXRHAAA=", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" }, "shouldThrow": { - "errorMessage": "Redeem script for input #1 doesn't match the scriptPubKey in the prevout", - "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", - "inputToCheck": 1 + "errorMessage": "Redeem script for input #0 doesn't match the scriptPubKey in the prevout", + "psbt": "cHNidP8BADMBAAAAAYaq+PdOUY2PnV9kZKa82XlqrPByOqwH2TRg2+LQdqs2AAAAAAD/////AAAAAAAAAQEgAOH1BQAAAAAXqRSTNeWHqa9INvSnQ120SZeJc+6JSocBBBYAFA3zpl6FMnlgCviVJgbcnBj01iLgAAA=", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" } } ] diff --git a/test/psbt.js b/test/psbt.js index 3bf873d..8584bd6 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -7,20 +7,23 @@ const Psbt = require('..').Psbt const fixtures = require('./fixtures/psbt') describe(`Psbt`, () => { - // constants - const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) - describe('signInput', () => { fixtures.signInput.checks.forEach(f => { it(f.description, () => { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) assert.doesNotThrow(() => { - psbtThatShouldsign.signInput(f.shouldSign.inputToCheck, keyPair) + psbtThatShouldsign.signInput( + f.shouldSign.inputToCheck, + ECPair.fromWIF(f.shouldSign.WIF), + ) }) const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) assert.throws(() => { - psbtThatShouldThrow.signInput(f.shouldThrow.inputToCheck, keyPair) + psbtThatShouldThrow.signInput( + f.shouldThrow.inputToCheck, + ECPair.fromWIF(f.shouldThrow.WIF), + ) }, {message: f.shouldThrow.errorMessage}) }) }) From 8d74bebe044c76c4229e424ebf10dc35feb45a06 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 2 Jul 2019 18:17:37 +0700 Subject: [PATCH 338/568] Throw error when signing with a privkey that doesn't match the pubkey --- src/psbt.js | 32 ++++++++++++++------------------ ts_src/psbt.ts | 44 +++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 41 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 651aeb2..e03969d 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -2,6 +2,7 @@ Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); const utils_1 = require('bip174/src/lib/utils'); +const crypto_1 = require('./crypto'); const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); @@ -54,29 +55,24 @@ class Psbt extends bip174_1.Psbt { signInput(inputIndex, keyPair) { const input = this.inputs[inputIndex]; if (input === undefined) throw new Error(`No input #${inputIndex}`); - const { hash, sighashType } = getHashForSig( + const { hash, sighashType, script } = getHashForSig( inputIndex, input, this.globalMap.unsignedTx, ); const pubkey = keyPair.publicKey; - // // TODO: throw error when the pubkey or pubkey hash is not found anywhere - // // in the script - // const pubkeyHash = hash160(keyPair.publicKey); - // - // const decompiled = bscript.decompile(script); - // if (decompiled === null) throw new Error('Unknown script error'); - // - // const hasKey = decompiled.some(element => { - // if (typeof element === 'number') return false; - // return element.equals(pubkey) || element.equals(pubkeyHash); - // }); - // - // if (!hasKey) { - // throw new Error( - // `Can not sign for this input with the key ${pubkey.toString('hex')}`, - // ); - // } + const pubkeyHash = crypto_1.hash160(keyPair.publicKey); + const decompiled = bscript.decompile(script); + if (decompiled === null) throw new Error('Unknown script error'); + const hasKey = decompiled.some(element => { + if (typeof element === 'number') return false; + return element.equals(pubkey) || element.equals(pubkeyHash); + }); + if (!hasKey) { + throw new Error( + `Can not sign for this input with the key ${pubkey.toString('hex')}`, + ); + } const partialSig = { pubkey, signature: bscript.signature.encode(keyPair.sign(hash), sighashType), diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index cf60f91..194a1f7 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,7 +1,7 @@ import { Psbt as PsbtBase } from 'bip174'; import { PsbtInput } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; -// import { hash160 } from './crypto'; // TODO: used in pubkey check +import { hash160 } from './crypto'; import { Signer } from './ecpair'; import { Network } from './networks'; import * as payments from './payments'; @@ -60,30 +60,28 @@ export class Psbt extends PsbtBase { signInput(inputIndex: number, keyPair: Signer): Psbt { const input = this.inputs[inputIndex]; if (input === undefined) throw new Error(`No input #${inputIndex}`); - const { - hash, - sighashType, - // script, // TODO: use for pubkey check below - } = getHashForSig(inputIndex, input, this.globalMap.unsignedTx!); + const { hash, sighashType, script } = getHashForSig( + inputIndex, + input, + this.globalMap.unsignedTx!, + ); const pubkey = keyPair.publicKey; - // // TODO: throw error when the pubkey or pubkey hash is not found anywhere - // // in the script - // const pubkeyHash = hash160(keyPair.publicKey); - // - // const decompiled = bscript.decompile(script); - // if (decompiled === null) throw new Error('Unknown script error'); - // - // const hasKey = decompiled.some(element => { - // if (typeof element === 'number') return false; - // return element.equals(pubkey) || element.equals(pubkeyHash); - // }); - // - // if (!hasKey) { - // throw new Error( - // `Can not sign for this input with the key ${pubkey.toString('hex')}`, - // ); - // } + const pubkeyHash = hash160(keyPair.publicKey); + + const decompiled = bscript.decompile(script); + if (decompiled === null) throw new Error('Unknown script error'); + + const hasKey = decompiled.some(element => { + if (typeof element === 'number') return false; + return element.equals(pubkey) || element.equals(pubkeyHash); + }); + + if (!hasKey) { + throw new Error( + `Can not sign for this input with the key ${pubkey.toString('hex')}`, + ); + } const partialSig = { pubkey, From 658ea845b12a9212567b7bdb262594c009510117 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 2 Jul 2019 18:20:55 +0700 Subject: [PATCH 339/568] Test matching privkey check --- test/fixtures/psbt.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 4aaec98..696cdcc 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -1,6 +1,20 @@ { "signInput": { "checks": [ + { + "description": "checks privkey matches the input it's signing", + "shouldSign": { + "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" + }, + "shouldThrow": { + "errorMessage": "Can not sign for this input with the key 02e717fee6be913148f9fd676c0876b7e4574118542c6758b4a9fb9f38f171842b", + "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=", + "inputToCheck": 0, + "WIF": "Kz4mjzErKCH5eQ97RXNQd3Wv7WsLA83BjynfQk4N7BB8J5xuUjAv" + } + }, { "description": "checks non-witness UTXO matches the hash specified in the prevout", "shouldSign": { From b8789c5d13be55d136eb24904fa285ccfcaa8668 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 2 Jul 2019 18:29:14 +0700 Subject: [PATCH 340/568] Test input exists check --- test/fixtures/psbt.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 696cdcc..5e008a4 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -1,6 +1,20 @@ { "signInput": { "checks": [ + { + "description": "checks the input exists", + "shouldSign": { + "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" + }, + "shouldThrow": { + "errorMessage": "No input #1", + "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=", + "inputToCheck": 1, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" + } + }, { "description": "checks privkey matches the input it's signing", "shouldSign": { From 343297a3597141a25ef69035f8c20f66f8ae18e1 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 2 Jul 2019 18:31:46 +0700 Subject: [PATCH 341/568] Test error if UTXO doesn't exist --- test/fixtures/psbt.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 5e008a4..371a8b7 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -15,6 +15,20 @@ "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" } }, + { + "description": "checks a UTXO value exists for the input", + "shouldSign": { + "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" + }, + "shouldThrow": { + "errorMessage": "Need a Utxo input item for signing", + "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAAA=", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" + } + }, { "description": "checks privkey matches the input it's signing", "shouldSign": { From 813b84f91f0294c89fc11cdb66aefd80e9adb28a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 3 Jul 2019 15:13:36 +0900 Subject: [PATCH 342/568] Finalize and extract done --- src/psbt.js | 217 +++++++++++++++++++++++++++++++-------- ts_src/psbt.ts | 268 +++++++++++++++++++++++++++++++++++++++--------- types/psbt.d.ts | 8 +- 3 files changed, 400 insertions(+), 93 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index e03969d..59d898c 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -6,6 +6,7 @@ const crypto_1 = require('./crypto'); const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); +const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { // protected __TX: Transaction; constructor(network) { @@ -23,56 +24,75 @@ class Psbt extends bip174_1.Psbt { // } // }); } - canFinalize(inputIndex) { + extractTransaction() { + if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + const tx = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); + this.inputs.forEach((input, idx) => { + if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; + if (input.finalScriptWitness) { + const decompiled = bscript.decompile(input.finalScriptWitness); + if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled); + } + }); + return tx; + } + finalizeAllInputs() { + const inputResults = range(this.inputs.length).map(idx => + this.finalizeInput(idx), + ); + const result = inputResults.every(val => val === true); + return { + result, + inputResults, + }; + } + finalizeInput(inputIndex) { const input = utils_1.checkForInput(this.inputs, inputIndex); - const script = getScriptFromInput( + const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, this.globalMap.unsignedTx, ); if (!script) return false; const scriptType = classifyScript(script); - const hasSigs = (neededSigs, partialSig) => { - if (!partialSig) return false; - if (partialSig.length > neededSigs) - throw new Error('Too many signatures'); - return partialSig.length === neededSigs; - }; - switch (scriptType) { - case 'pubkey': - return hasSigs(1, input.partialSig); - case 'pubkeyhash': - return hasSigs(1, input.partialSig); - case 'multisig': - const p2ms = payments.p2ms({ output: script }); - return hasSigs(p2ms.m, input.partialSig); - case 'witnesspubkeyhash': - return hasSigs(1, input.partialSig); - default: - return false; + if (!canFinalize(input, script, scriptType)) return false; + let finalScriptSig; + let finalScriptWitness; + // Wow, the payments API is very handy + const payment = getPayment(script, scriptType, input.partialSig); + const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment }); + const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment }); + if (isSegwit) { + if (p2wsh) { + finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness); + } else { + finalScriptWitness = witnessStackToScriptWitness(payment.witness); + } + if (p2sh) { + finalScriptSig = bscript.compile([p2sh.redeem.output]); + } + } else { + finalScriptSig = payment.input; } + if (finalScriptSig) + this.addFinalScriptSigToInput(inputIndex, finalScriptSig); + if (finalScriptWitness) + this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); + if (!finalScriptSig && !finalScriptWitness) return false; + this.clearFinalizedInput(inputIndex); + return true; } signInput(inputIndex, keyPair) { - const input = this.inputs[inputIndex]; - if (input === undefined) throw new Error(`No input #${inputIndex}`); + const input = utils_1.checkForInput(this.inputs, inputIndex); + if (!keyPair || !keyPair.publicKey) + throw new Error('Need Signer to sign input'); const { hash, sighashType, script } = getHashForSig( inputIndex, input, this.globalMap.unsignedTx, ); const pubkey = keyPair.publicKey; - const pubkeyHash = crypto_1.hash160(keyPair.publicKey); - const decompiled = bscript.decompile(script); - if (decompiled === null) throw new Error('Unknown script error'); - const hasKey = decompiled.some(element => { - if (typeof element === 'number') return false; - return element.equals(pubkey) || element.equals(pubkeyHash); - }); - if (!hasKey) { - throw new Error( - `Can not sign for this input with the key ${pubkey.toString('hex')}`, - ); - } + checkScriptForPubkey(pubkey, script); const partialSig = { pubkey, signature: bscript.signature.encode(keyPair.sign(hash), sighashType), @@ -81,6 +101,77 @@ class Psbt extends bip174_1.Psbt { } } exports.Psbt = Psbt; +// +// +// +// +// Helper functions +// +// +// +// +function isFinalized(input) { + return !!input.finalScriptSig || !!input.finalScriptWitness; +} +function getPayment(script, scriptType, partialSig) { + let payment; + switch (scriptType) { + case 'multisig': + payment = payments.p2ms({ + output: script, + signatures: partialSig.map(ps => ps.signature), + }); + break; + case 'pubkey': + payment = payments.p2pk({ + output: script, + signature: partialSig[0].signature, + }); + break; + case 'pubkeyhash': + payment = payments.p2pkh({ + output: script, + pubkey: partialSig[0].pubkey, + signature: partialSig[0].signature, + }); + break; + case 'witnesspubkeyhash': + payment = payments.p2wpkh({ + output: script, + pubkey: partialSig[0].pubkey, + signature: partialSig[0].signature, + }); + break; + } + return payment; +} +function canFinalize(input, script, scriptType) { + switch (scriptType) { + case 'pubkey': + case 'pubkeyhash': + case 'witnesspubkeyhash': + return hasSigs(1, input.partialSig); + case 'multisig': + const p2ms = payments.p2ms({ output: script }); + return hasSigs(p2ms.m, input.partialSig); + default: + return false; + } +} +function checkScriptForPubkey(pubkey, script) { + const pubkeyHash = crypto_1.hash160(pubkey); + const decompiled = bscript.decompile(script); + if (decompiled === null) throw new Error('Unknown script error'); + const hasKey = decompiled.some(element => { + if (typeof element === 'number') return false; + return element.equals(pubkey) || element.equals(pubkeyHash); + }); + if (!hasKey) { + throw new Error( + `Can not sign for this input with the key ${pubkey.toString('hex')}`, + ); + } +} const getHashForSig = (inputIndex, input, txBuf) => { const unsignedTx = transaction_1.Transaction.fromBuffer(txBuf); const sighashType = @@ -202,29 +293,67 @@ const classifyScript = script => { return 'nonstandard'; }; function getScriptFromInput(inputIndex, input, _unsignedTx) { - let script; + const res = { + script: null, + isSegwit: false, + isP2SH: false, + isP2WSH: false, + }; if (input.nonWitnessUtxo) { if (input.redeemScript) { - script = input.redeemScript; + res.isP2SH = true; + res.script = input.redeemScript; } else { const unsignedTx = transaction_1.Transaction.fromBuffer(_unsignedTx); const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( input.nonWitnessUtxo, ); const prevoutIndex = unsignedTx.ins[inputIndex].index; - script = nonWitnessUtxoTx.outs[prevoutIndex].script; + res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; } } else if (input.witnessUtxo) { + res.isSegwit = true; + res.isP2SH = !!input.redeemScript; + res.isP2WSH = !!input.witnessScript; if (input.witnessScript) { - script = input.witnessScript; + res.script = input.witnessScript; } else if (input.redeemScript) { - script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output; + res.script = payments.p2pkh({ + hash: input.redeemScript.slice(2), + }).output; } else { - script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) }) - .output; + res.script = payments.p2pkh({ + hash: input.witnessUtxo.script.slice(2), + }).output; } - } else { - return; } - return script; + return res; } +const hasSigs = (neededSigs, partialSig) => { + if (!partialSig) return false; + if (partialSig.length > neededSigs) throw new Error('Too many signatures'); + return partialSig.length === neededSigs; +}; +function witnessStackToScriptWitness(witness) { + let buffer = Buffer.allocUnsafe(0); + function writeSlice(slice) { + buffer = Buffer.concat([buffer, Buffer.from(slice)]); + } + function writeVarInt(i) { + const currentLen = buffer.length; + const varintLen = varuint.encodingLength(i); + buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); + varuint.encode(i, buffer, currentLen); + } + function writeVarSlice(slice) { + writeVarInt(slice.length); + writeSlice(slice); + } + function writeVector(vector) { + writeVarInt(vector.length); + vector.forEach(writeVarSlice); + } + writeVector(witness); + return buffer; +} +const range = n => [...Array(n).keys()]; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 194a1f7..273a1c3 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,5 +1,5 @@ import { Psbt as PsbtBase } from 'bip174'; -import { PsbtInput } from 'bip174/src/lib/interfaces'; +import { PartialSig, PsbtInput } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; import { hash160 } from './crypto'; import { Signer } from './ecpair'; @@ -7,6 +7,7 @@ import { Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; import { Transaction } from './transaction'; +const varuint = require('varuint-bitcoin'); export class Psbt extends PsbtBase { // protected __TX: Transaction; @@ -25,41 +26,84 @@ export class Psbt extends PsbtBase { // }); } - canFinalize(inputIndex: number): boolean { + extractTransaction(): Transaction { + if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + const tx = Transaction.fromBuffer(this.globalMap.unsignedTx!); + this.inputs.forEach((input, idx) => { + if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; + if (input.finalScriptWitness) { + const decompiled = bscript.decompile(input.finalScriptWitness); + if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled); + } + }); + return tx; + } + + finalizeAllInputs(): { + result: boolean; + inputResults: boolean[]; + } { + const inputResults = range(this.inputs.length).map(idx => + this.finalizeInput(idx), + ); + const result = inputResults.every(val => val === true); + return { + result, + inputResults, + }; + } + + finalizeInput(inputIndex: number): boolean { const input = checkForInput(this.inputs, inputIndex); - const script = getScriptFromInput( + const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, this.globalMap.unsignedTx!, ); if (!script) return false; + const scriptType = classifyScript(script); + if (!canFinalize(input, script, scriptType)) return false; - const hasSigs = (neededSigs: number, partialSig?: any[]): boolean => { - if (!partialSig) return false; - if (partialSig.length > neededSigs) - throw new Error('Too many signatures'); - return partialSig.length === neededSigs; - }; + let finalScriptSig: Buffer | undefined; + let finalScriptWitness: Buffer | undefined; - switch (scriptType) { - case 'pubkey': - return hasSigs(1, input.partialSig); - case 'pubkeyhash': - return hasSigs(1, input.partialSig); - case 'multisig': - const p2ms = payments.p2ms({ output: script }); - return hasSigs(p2ms.m!, input.partialSig); - case 'witnesspubkeyhash': - return hasSigs(1, input.partialSig); - default: - return false; + // Wow, the payments API is very handy + const payment: payments.Payment = getPayment( + script, + scriptType, + input.partialSig!, + ); + const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment }); + const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment }); + + if (isSegwit) { + if (p2wsh) { + finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness!); + } else { + finalScriptWitness = witnessStackToScriptWitness(payment.witness!); + } + if (p2sh) { + finalScriptSig = bscript.compile([p2sh.redeem!.output!]); + } + } else { + finalScriptSig = payment.input; } + + if (finalScriptSig) + this.addFinalScriptSigToInput(inputIndex, finalScriptSig); + if (finalScriptWitness) + this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); + if (!finalScriptSig && !finalScriptWitness) return false; + + this.clearFinalizedInput(inputIndex); + return true; } signInput(inputIndex: number, keyPair: Signer): Psbt { - const input = this.inputs[inputIndex]; - if (input === undefined) throw new Error(`No input #${inputIndex}`); + const input = checkForInput(this.inputs, inputIndex); + if (!keyPair || !keyPair.publicKey) + throw new Error('Need Signer to sign input'); const { hash, sighashType, script } = getHashForSig( inputIndex, input, @@ -67,21 +111,8 @@ export class Psbt extends PsbtBase { ); const pubkey = keyPair.publicKey; - const pubkeyHash = hash160(keyPair.publicKey); - const decompiled = bscript.decompile(script); - if (decompiled === null) throw new Error('Unknown script error'); - - const hasKey = decompiled.some(element => { - if (typeof element === 'number') return false; - return element.equals(pubkey) || element.equals(pubkeyHash); - }); - - if (!hasKey) { - throw new Error( - `Can not sign for this input with the key ${pubkey.toString('hex')}`, - ); - } + checkScriptForPubkey(pubkey, script); const partialSig = { pubkey, @@ -92,6 +123,93 @@ export class Psbt extends PsbtBase { } } +// +// +// +// +// Helper functions +// +// +// +// + +function isFinalized(input: PsbtInput): boolean { + return !!input.finalScriptSig || !!input.finalScriptWitness; +} + +function getPayment( + script: Buffer, + scriptType: string, + partialSig: PartialSig[], +): payments.Payment { + let payment: payments.Payment; + switch (scriptType) { + case 'multisig': + payment = payments.p2ms({ + output: script, + signatures: partialSig.map(ps => ps.signature), + }); + break; + case 'pubkey': + payment = payments.p2pk({ + output: script, + signature: partialSig[0].signature, + }); + break; + case 'pubkeyhash': + payment = payments.p2pkh({ + output: script, + pubkey: partialSig[0].pubkey, + signature: partialSig[0].signature, + }); + break; + case 'witnesspubkeyhash': + payment = payments.p2wpkh({ + output: script, + pubkey: partialSig[0].pubkey, + signature: partialSig[0].signature, + }); + break; + } + return payment!; +} + +function canFinalize( + input: PsbtInput, + script: Buffer, + scriptType: string, +): boolean { + switch (scriptType) { + case 'pubkey': + case 'pubkeyhash': + case 'witnesspubkeyhash': + return hasSigs(1, input.partialSig); + case 'multisig': + const p2ms = payments.p2ms({ output: script }); + return hasSigs(p2ms.m!, input.partialSig); + default: + return false; + } +} + +function checkScriptForPubkey(pubkey: Buffer, script: Buffer): void { + const pubkeyHash = hash160(pubkey); + + const decompiled = bscript.decompile(script); + if (decompiled === null) throw new Error('Unknown script error'); + + const hasKey = decompiled.some(element => { + if (typeof element === 'number') return false; + return element.equals(pubkey) || element.equals(pubkeyHash); + }); + + if (!hasKey) { + throw new Error( + `Can not sign for this input with the key ${pubkey.toString('hex')}`, + ); + } +} + interface HashForSigData { script: Buffer; hash: Buffer; @@ -239,32 +357,86 @@ const classifyScript = (script: Buffer): string => { return 'nonstandard'; }; +interface GetScriptReturn { + script: Buffer | null; + isSegwit: boolean; + isP2SH: boolean; + isP2WSH: boolean; +} function getScriptFromInput( inputIndex: number, input: PsbtInput, _unsignedTx: Buffer, -): Buffer | undefined { - let script: Buffer; +): GetScriptReturn { + const res: GetScriptReturn = { + script: null, + isSegwit: false, + isP2SH: false, + isP2WSH: false, + }; if (input.nonWitnessUtxo) { if (input.redeemScript) { - script = input.redeemScript; + res.isP2SH = true; + res.script = input.redeemScript; } else { const unsignedTx = Transaction.fromBuffer(_unsignedTx); const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); const prevoutIndex = unsignedTx.ins[inputIndex].index; - script = nonWitnessUtxoTx.outs[prevoutIndex].script; + res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; } } else if (input.witnessUtxo) { + res.isSegwit = true; + res.isP2SH = !!input.redeemScript; + res.isP2WSH = !!input.witnessScript; if (input.witnessScript) { - script = input.witnessScript; + res.script = input.witnessScript; } else if (input.redeemScript) { - script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output!; + res.script = payments.p2pkh({ + hash: input.redeemScript.slice(2), + }).output!; } else { - script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) }) - .output!; + res.script = payments.p2pkh({ + hash: input.witnessUtxo.script.slice(2), + }).output!; } - } else { - return; } - return script; + return res; } + +const hasSigs = (neededSigs: number, partialSig?: any[]): boolean => { + if (!partialSig) return false; + if (partialSig.length > neededSigs) throw new Error('Too many signatures'); + return partialSig.length === neededSigs; +}; + +function witnessStackToScriptWitness(witness: Buffer[]): Buffer { + let buffer = Buffer.allocUnsafe(0); + + function writeSlice(slice: Buffer): void { + buffer = Buffer.concat([buffer, Buffer.from(slice)]); + } + + function writeVarInt(i: number): void { + const currentLen = buffer.length; + const varintLen = varuint.encodingLength(i); + + buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); + varuint.encode(i, buffer, currentLen); + } + + function writeVarSlice(slice: Buffer): void { + writeVarInt(slice.length); + writeSlice(slice); + } + + function writeVector(vector: Buffer[]): void { + writeVarInt(vector.length); + vector.forEach(writeVarSlice); + } + + writeVector(witness); + + return buffer; +} + +const range = (n: number): number[] => [...Array(n).keys()]; diff --git a/types/psbt.d.ts b/types/psbt.d.ts index fda7e6b..f5b6430 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,9 +1,15 @@ import { Psbt as PsbtBase } from 'bip174'; import { Signer } from './ecpair'; import { Network } from './networks'; +import { Transaction } from './transaction'; export declare class Psbt extends PsbtBase { network?: Network | undefined; constructor(network?: Network | undefined); - canFinalize(inputIndex: number): boolean; + extractTransaction(): Transaction; + finalizeAllInputs(): { + result: boolean; + inputResults: boolean[]; + }; + finalizeInput(inputIndex: number): boolean; signInput(inputIndex: number, keyPair: Signer): Psbt; } From 77dde89acc047607ed9a30506379bc80fdba3642 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 3 Jul 2019 15:34:18 +0900 Subject: [PATCH 343/568] Add async signing method --- src/psbt.js | 94 ++++++++++++++++++++++++++++---------- ts_src/psbt.ts | 117 ++++++++++++++++++++++++++++++++++++------------ tsconfig.json | 2 +- types/psbt.d.ts | 3 +- 4 files changed, 161 insertions(+), 55 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 59d898c..ae60861 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -56,24 +56,14 @@ class Psbt extends bip174_1.Psbt { if (!script) return false; const scriptType = classifyScript(script); if (!canFinalize(input, script, scriptType)) return false; - let finalScriptSig; - let finalScriptWitness; - // Wow, the payments API is very handy - const payment = getPayment(script, scriptType, input.partialSig); - const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment }); - const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment }); - if (isSegwit) { - if (p2wsh) { - finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness); - } else { - finalScriptWitness = witnessStackToScriptWitness(payment.witness); - } - if (p2sh) { - finalScriptSig = bscript.compile([p2sh.redeem.output]); - } - } else { - finalScriptSig = payment.input; - } + const { finalScriptSig, finalScriptWitness } = getFinalScripts( + script, + scriptType, + input.partialSig, + isSegwit, + isP2SH, + isP2WSH, + ); if (finalScriptSig) this.addFinalScriptSigToInput(inputIndex, finalScriptSig); if (finalScriptWitness) @@ -83,22 +73,38 @@ class Psbt extends bip174_1.Psbt { return true; } signInput(inputIndex, keyPair) { - const input = utils_1.checkForInput(this.inputs, inputIndex); if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); - const { hash, sighashType, script } = getHashForSig( + const { hash, sighashType } = getHashAndSighashType( + this.inputs, inputIndex, - input, + keyPair.publicKey, this.globalMap.unsignedTx, ); - const pubkey = keyPair.publicKey; - checkScriptForPubkey(pubkey, script); const partialSig = { - pubkey, + pubkey: keyPair.publicKey, signature: bscript.signature.encode(keyPair.sign(hash), sighashType), }; return this.addPartialSigToInput(inputIndex, partialSig); } + async signInputAsync(inputIndex, keyPair) { + if (!keyPair || !keyPair.publicKey) + throw new Error('Need Signer to sign input'); + const { hash, sighashType } = getHashAndSighashType( + this.inputs, + inputIndex, + keyPair.publicKey, + this.globalMap.unsignedTx, + ); + const partialSig = { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode( + await keyPair.sign(hash), + sighashType, + ), + }; + this.addPartialSigToInput(inputIndex, partialSig); + } } exports.Psbt = Psbt; // @@ -113,6 +119,46 @@ exports.Psbt = Psbt; function isFinalized(input) { return !!input.finalScriptSig || !!input.finalScriptWitness; } +function getHashAndSighashType(inputs, inputIndex, pubkey, txBuf) { + const input = utils_1.checkForInput(inputs, inputIndex); + const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf); + checkScriptForPubkey(pubkey, script); + return { + hash, + sighashType, + }; +} +function getFinalScripts( + script, + scriptType, + partialSig, + isSegwit, + isP2SH, + isP2WSH, +) { + let finalScriptSig; + let finalScriptWitness; + // Wow, the payments API is very handy + const payment = getPayment(script, scriptType, partialSig); + const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment }); + const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment }); + if (isSegwit) { + if (p2wsh) { + finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness); + } else { + finalScriptWitness = witnessStackToScriptWitness(payment.witness); + } + if (p2sh) { + finalScriptSig = bscript.compile([p2sh.redeem.output]); + } + } else { + finalScriptSig = payment.input; + } + return { + finalScriptSig, + finalScriptWitness, + }; +} function getPayment(script, scriptType, partialSig) { let payment; switch (scriptType) { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 273a1c3..d9a059c 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -2,7 +2,7 @@ import { Psbt as PsbtBase } from 'bip174'; import { PartialSig, PsbtInput } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; import { hash160 } from './crypto'; -import { Signer } from './ecpair'; +import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; @@ -65,30 +65,14 @@ export class Psbt extends PsbtBase { const scriptType = classifyScript(script); if (!canFinalize(input, script, scriptType)) return false; - let finalScriptSig: Buffer | undefined; - let finalScriptWitness: Buffer | undefined; - - // Wow, the payments API is very handy - const payment: payments.Payment = getPayment( + const { finalScriptSig, finalScriptWitness } = getFinalScripts( script, scriptType, input.partialSig!, + isSegwit, + isP2SH, + isP2WSH, ); - const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment }); - const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment }); - - if (isSegwit) { - if (p2wsh) { - finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness!); - } else { - finalScriptWitness = witnessStackToScriptWitness(payment.witness!); - } - if (p2sh) { - finalScriptSig = bscript.compile([p2sh.redeem!.output!]); - } - } else { - finalScriptSig = payment.input; - } if (finalScriptSig) this.addFinalScriptSigToInput(inputIndex, finalScriptSig); @@ -101,26 +85,46 @@ export class Psbt extends PsbtBase { } signInput(inputIndex: number, keyPair: Signer): Psbt { - const input = checkForInput(this.inputs, inputIndex); if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); - const { hash, sighashType, script } = getHashForSig( + const { hash, sighashType } = getHashAndSighashType( + this.inputs, inputIndex, - input, + keyPair.publicKey, this.globalMap.unsignedTx!, ); - const pubkey = keyPair.publicKey; - - checkScriptForPubkey(pubkey, script); - const partialSig = { - pubkey, + pubkey: keyPair.publicKey, signature: bscript.signature.encode(keyPair.sign(hash), sighashType), }; return this.addPartialSigToInput(inputIndex, partialSig); } + + async signInputAsync( + inputIndex: number, + keyPair: SignerAsync, + ): Promise<void> { + if (!keyPair || !keyPair.publicKey) + throw new Error('Need Signer to sign input'); + const { hash, sighashType } = getHashAndSighashType( + this.inputs, + inputIndex, + keyPair.publicKey, + this.globalMap.unsignedTx!, + ); + + const partialSig = { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode( + await keyPair.sign(hash), + sighashType, + ), + }; + + this.addPartialSigToInput(inputIndex, partialSig); + } } // @@ -137,6 +141,61 @@ function isFinalized(input: PsbtInput): boolean { return !!input.finalScriptSig || !!input.finalScriptWitness; } +function getHashAndSighashType( + inputs: PsbtInput[], + inputIndex: number, + pubkey: Buffer, + txBuf: Buffer, +): { + hash: Buffer; + sighashType: number; +} { + const input = checkForInput(inputs, inputIndex); + const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf); + checkScriptForPubkey(pubkey, script); + return { + hash, + sighashType, + }; +} + +function getFinalScripts( + script: Buffer, + scriptType: string, + partialSig: PartialSig[], + isSegwit: boolean, + isP2SH: boolean, + isP2WSH: boolean, +): { + finalScriptSig: Buffer | undefined; + finalScriptWitness: Buffer | undefined; +} { + let finalScriptSig: Buffer | undefined; + let finalScriptWitness: Buffer | undefined; + + // Wow, the payments API is very handy + const payment: payments.Payment = getPayment(script, scriptType, partialSig); + const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment }); + const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment }); + + if (isSegwit) { + if (p2wsh) { + finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness!); + } else { + finalScriptWitness = witnessStackToScriptWitness(payment.witness!); + } + if (p2sh) { + finalScriptSig = bscript.compile([p2sh.redeem!.output!]); + } + } else { + finalScriptSig = payment.input; + } + return { + finalScriptSig, + finalScriptWitness, + }; +} + function getPayment( script: Buffer, scriptType: string, diff --git a/tsconfig.json b/tsconfig.json index f770a45..1de632d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2015", + "target": "ES2017", "module": "commonjs", "outDir": "./src", "declaration": true, diff --git a/types/psbt.d.ts b/types/psbt.d.ts index f5b6430..26ae0a7 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,5 +1,5 @@ import { Psbt as PsbtBase } from 'bip174'; -import { Signer } from './ecpair'; +import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; export declare class Psbt extends PsbtBase { @@ -12,4 +12,5 @@ export declare class Psbt extends PsbtBase { }; finalizeInput(inputIndex: number): boolean; signInput(inputIndex: number, keyPair: Signer): Psbt; + signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>; } From 1c8fc6978015ae86f6950eccfdffb68f5d73d5a1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 3 Jul 2019 15:48:56 +0900 Subject: [PATCH 344/568] Stick with ES2015 for now --- src/psbt.js | 17 ++++++++--------- ts_src/psbt.ts | 20 ++++++++------------ tsconfig.json | 2 +- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index ae60861..1ed7354 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -87,7 +87,7 @@ class Psbt extends bip174_1.Psbt { }; return this.addPartialSigToInput(inputIndex, partialSig); } - async signInputAsync(inputIndex, keyPair) { + signInputAsync(inputIndex, keyPair) { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); const { hash, sighashType } = getHashAndSighashType( @@ -96,14 +96,13 @@ class Psbt extends bip174_1.Psbt { keyPair.publicKey, this.globalMap.unsignedTx, ); - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode( - await keyPair.sign(hash), - sighashType, - ), - }; - this.addPartialSigToInput(inputIndex, partialSig); + return keyPair.sign(hash).then(signature => { + const partialSig = { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(signature, sighashType), + }; + this.addPartialSigToInput(inputIndex, partialSig); + }); } } exports.Psbt = Psbt; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d9a059c..680ea92 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -102,10 +102,7 @@ export class Psbt extends PsbtBase { return this.addPartialSigToInput(inputIndex, partialSig); } - async signInputAsync( - inputIndex: number, - keyPair: SignerAsync, - ): Promise<void> { + signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void> { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); const { hash, sighashType } = getHashAndSighashType( @@ -115,15 +112,14 @@ export class Psbt extends PsbtBase { this.globalMap.unsignedTx!, ); - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode( - await keyPair.sign(hash), - sighashType, - ), - }; + return keyPair.sign(hash).then(signature => { + const partialSig = { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(signature, sighashType), + }; - this.addPartialSigToInput(inputIndex, partialSig); + this.addPartialSigToInput(inputIndex, partialSig); + }); } } diff --git a/tsconfig.json b/tsconfig.json index 1de632d..f770a45 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2017", + "target": "ES2015", "module": "commonjs", "outDir": "./src", "declaration": true, From 48fc75c4f016f199c734e7433a9622027e3f81ad Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 3 Jul 2019 18:42:31 +0900 Subject: [PATCH 345/568] Fix p2sh and p2wsh not working --- src/psbt.js | 60 ++++++++++++++++++++++++++++++++++++++------ ts_src/psbt.ts | 67 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 111 insertions(+), 16 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 1ed7354..3cbd899 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -30,8 +30,9 @@ class Psbt extends bip174_1.Psbt { this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { - const decompiled = bscript.decompile(input.finalScriptWitness); - if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled); + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); } }); return tx; @@ -148,23 +149,44 @@ function getFinalScripts( finalScriptWitness = witnessStackToScriptWitness(payment.witness); } if (p2sh) { - finalScriptSig = bscript.compile([p2sh.redeem.output]); + finalScriptSig = p2sh.input; } } else { - finalScriptSig = payment.input; + if (p2sh) { + finalScriptSig = p2sh.input; + } else { + finalScriptSig = payment.input; + } } return { finalScriptSig, finalScriptWitness, }; } +function getSortedSigs(script, partialSig) { + const p2ms = payments.p2ms({ output: script }); + // for each pubkey in order of p2ms script + return p2ms.pubkeys + .map(pk => { + // filter partialSig array by pubkey being equal + return ( + partialSig.filter(ps => { + return ps.pubkey.equals(pk); + })[0] || {} + ).signature; + // Any pubkey without a match will return undefined + // this last filter removes all the undefined items in the array. + }) + .filter(v => !!v); +} function getPayment(script, scriptType, partialSig) { let payment; switch (scriptType) { case 'multisig': + const sigs = getSortedSigs(script, partialSig); payment = payments.p2ms({ output: script, - signatures: partialSig.map(ps => ps.signature), + signatures: sigs, }); break; case 'pubkey': @@ -283,7 +305,7 @@ const getHashForSig = (inputIndex, input, txBuf) => { checkWitnessScript(inputIndex, _script, input.witnessScript); hash = unsignedTx.hashForWitnessV0( inputIndex, - _script, + input.witnessScript, input.witnessUtxo.value, sighashType, ); @@ -363,11 +385,11 @@ function getScriptFromInput(inputIndex, input, _unsignedTx) { if (input.witnessScript) { res.script = input.witnessScript; } else if (input.redeemScript) { - res.script = payments.p2pkh({ + res.script = payments.p2wpkh({ hash: input.redeemScript.slice(2), }).output; } else { - res.script = payments.p2pkh({ + res.script = payments.p2wpkh({ hash: input.witnessUtxo.script.slice(2), }).output; } @@ -401,4 +423,26 @@ function witnessStackToScriptWitness(witness) { writeVector(witness); return buffer; } +function scriptWitnessToWitnessStack(buffer) { + let offset = 0; + function readSlice(n) { + offset += n; + return buffer.slice(offset - n, offset); + } + function readVarInt() { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + } + function readVarSlice() { + return readSlice(readVarInt()); + } + function readVector() { + const count = readVarInt(); + const vector = []; + for (let i = 0; i < count; i++) vector.push(readVarSlice()); + return vector; + } + return readVector(); +} const range = n => [...Array(n).keys()]; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 680ea92..7c60a1d 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -32,8 +32,9 @@ export class Psbt extends PsbtBase { this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { - const decompiled = bscript.decompile(input.finalScriptWitness); - if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled); + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); } }); return tx; @@ -181,10 +182,14 @@ function getFinalScripts( finalScriptWitness = witnessStackToScriptWitness(payment.witness!); } if (p2sh) { - finalScriptSig = bscript.compile([p2sh.redeem!.output!]); + finalScriptSig = p2sh.input; } } else { - finalScriptSig = payment.input; + if (p2sh) { + finalScriptSig = p2sh.input; + } else { + finalScriptSig = payment.input; + } } return { finalScriptSig, @@ -192,6 +197,23 @@ function getFinalScripts( }; } +function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] { + const p2ms = payments.p2ms({ output: script }); + // for each pubkey in order of p2ms script + return p2ms + .pubkeys!.map(pk => { + // filter partialSig array by pubkey being equal + return ( + partialSig.filter(ps => { + return ps.pubkey.equals(pk); + })[0] || {} + ).signature; + // Any pubkey without a match will return undefined + // this last filter removes all the undefined items in the array. + }) + .filter(v => !!v); +} + function getPayment( script: Buffer, scriptType: string, @@ -200,9 +222,10 @@ function getPayment( let payment: payments.Payment; switch (scriptType) { case 'multisig': + const sigs = getSortedSigs(script, partialSig); payment = payments.p2ms({ output: script, - signatures: partialSig.map(ps => ps.signature), + signatures: sigs, }); break; case 'pubkey': @@ -343,7 +366,7 @@ const getHashForSig = ( checkWitnessScript(inputIndex, _script, input.witnessScript); hash = unsignedTx.hashForWitnessV0( inputIndex, - _script, + input.witnessScript, input.witnessUtxo.value, sighashType, ); @@ -446,11 +469,11 @@ function getScriptFromInput( if (input.witnessScript) { res.script = input.witnessScript; } else if (input.redeemScript) { - res.script = payments.p2pkh({ + res.script = payments.p2wpkh({ hash: input.redeemScript.slice(2), }).output!; } else { - res.script = payments.p2pkh({ + res.script = payments.p2wpkh({ hash: input.witnessUtxo.script.slice(2), }).output!; } @@ -494,4 +517,32 @@ function witnessStackToScriptWitness(witness: Buffer[]): Buffer { return buffer; } +function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] { + let offset = 0; + + function readSlice(n: number): Buffer { + offset += n; + return buffer.slice(offset - n, offset); + } + + function readVarInt(): number { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + } + + function readVarSlice(): Buffer { + return readSlice(readVarInt()); + } + + function readVector(): Buffer[] { + const count = readVarInt(); + const vector: Buffer[] = []; + for (let i = 0; i < count; i++) vector.push(readVarSlice()); + return vector; + } + + return readVector(); +} + const range = (n: number): number[] => [...Array(n).keys()]; From 1fc2e146ea461feb26fb3872f02b26dd91ff7e18 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 3 Jul 2019 16:40:37 +0700 Subject: [PATCH 346/568] Test BIP174 invalid test cases --- test/fixtures/psbt.json | 76 +++++++++++++++++++++++++++++++++++++++++ test/psbt.js | 10 ++++++ 2 files changed, 86 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 371a8b7..b2104e8 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -1,4 +1,80 @@ { + "bip174": { + "invalid": [ + { + "errorMessage": "Format Error: Invalid Magic Number", + "psbt": "AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA==" + }, + { + "errorMessage": "Format Error: Unexpected End of PSBT", + "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==" + }, + { + "errorMessage": "Format Error: Transaction ScriptSigs are not empty", + "psbt": "cHNidP8BAP0KAQIAAAACqwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QAAAAAakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpL+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAA=" + }, + { + "errorMessage": "Format Error: Only one UNSIGNED_TX allowed", + "psbt": "cHNidP8AAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==" + }, + { + "errorMessage": "Format Error: Keys must be unique for each input: input index 0 key 00", + "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQA/AgAAAAH//////////////////////////////////////////wAAAAAA/////wEAAAAAAAAAAANqAQAAAAAAAAAA" + }, + { + "errorMessage": "Format Error: Invalid global key: 0001", + "psbt": "cHNidP8CAAFVAgAAAAEnmiMjpd+1H8RfIg+liw/BPh4zQnkqhdfjbNYzO1y8OQAAAAAA/////wGgWuoLAAAAABl2qRT/6cAGEJfMO2NvLLBGD6T8Qn0rRYisAAAAAAABASCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA" + }, + { + "errorMessage": "Format Error: Invalid input key: 0100", + "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAIBACCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA" + }, + { + "errorMessage": "Format Error: invalid pubkey in key 0x0203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd", + "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIQIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYwQwIgBCS1jv+qppThVZ6lyTu/1KiQZCJAVc3wcLZ3FGlELQcCH1yOsP6mUW1guKyzOtZO3mDoeFv7OqlLmb34YVHbmpoBAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GIQPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvVKuIgYDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYQtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA==" + }, + { + "errorMessage": "Format Error: Invalid input key: 0400", + "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQIEACIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA" + }, + { + "errorMessage": "Format Error: Invalid input key: 0500", + "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoECBQBHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA" + }, + { + "errorMessage": "Format Error: invalid pubkey in key 0x0603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd", + "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriEGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb0QtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA==" + }, + { + "errorMessage": "Format Error: Invalid input key: 0000", + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAIAALsCAAAAAarXOTEBi9JfhK5AC2iEi+CdtwbqwqwYKYur7nGrZW+LAAAAAEhHMEQCIFj2/HxqM+GzFUjUgcgmwBW9MBNarULNZ3kNq2bSrSQ7AiBKHO0mBMZzW2OT5bQWkd14sA8MWUL7n3UYVvqpOBV9ugH+////AoDw+gIAAAAAF6kUD7lGNCFpa4LIM68kHHjBfdveSTSH0PIKJwEAAAAXqRQpynT4oI+BmZQoGFyXtdhS5AY/YYdlAAAAAQfaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" + }, + { + "errorMessage": "Format Error: Invalid input key: 0700", + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAACBwDaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" + }, + { + "errorMessage": "Format Error: Invalid input key: 0800", + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAggA2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" + }, + { + "errorMessage": "Format Error: invalid pubkey in key 0x0203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca587", + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" + }, + { + "errorMessage": "Format Error: Invalid input key: 0300", + "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A" + }, + { + "errorMessage": "Format Error: Invalid output key: 0000", + "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A" + }, + { + "errorMessage": "Format Error: Unexpected End of PSBT", + "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A" + } + ] + }, "signInput": { "checks": [ { diff --git a/test/psbt.js b/test/psbt.js index 8584bd6..b26e5cb 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -7,6 +7,16 @@ const Psbt = require('..').Psbt const fixtures = require('./fixtures/psbt') describe(`Psbt`, () => { + describe('BIP174 Test Vectors', () => { + fixtures.bip174.invalid.forEach(f => { + it(`Invalid: "${f.errorMessage}"`, () => { + assert.throws(() => { + Psbt.fromBase64(f.psbt) + }, {message: f.errorMessage}) + }) + }) + }) + describe('signInput', () => { fixtures.signInput.checks.forEach(f => { it(f.description, () => { From c24a6e1ad31ebcf06f286912e2a7f7939d71b65f Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 3 Jul 2019 17:01:47 +0700 Subject: [PATCH 347/568] Include test case number in test output --- test/psbt.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/psbt.js b/test/psbt.js index b26e5cb..c75bce8 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -8,8 +8,8 @@ const fixtures = require('./fixtures/psbt') describe(`Psbt`, () => { describe('BIP174 Test Vectors', () => { - fixtures.bip174.invalid.forEach(f => { - it(`Invalid: "${f.errorMessage}"`, () => { + fixtures.bip174.invalid.forEach((f, i) => { + it(`Invalid #${i + 1}: "${f.errorMessage}"`, () => { assert.throws(() => { Psbt.fromBase64(f.psbt) }, {message: f.errorMessage}) From 2662e46987313e88b17d1f1e1d2590511204294c Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 3 Jul 2019 17:02:02 +0700 Subject: [PATCH 348/568] Test BIP174 valid test cases --- test/fixtures/psbt.json | 8 ++++++++ test/psbt.js | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index b2104e8..451fe91 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -73,6 +73,14 @@ "errorMessage": "Format Error: Unexpected End of PSBT", "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A" } + ], + "valid": [ + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA", + "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA", + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA==", + "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=", + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=", + "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=" ] }, "signInput": { diff --git a/test/psbt.js b/test/psbt.js index c75bce8..6ae59cd 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -15,6 +15,14 @@ describe(`Psbt`, () => { }, {message: f.errorMessage}) }) }) + + fixtures.bip174.valid.forEach((psbt, i) => { + it(`Valid #${i + 1}`, () => { + assert.doesNotThrow(() => { + Psbt.fromBase64(psbt) + }) + }) + }) }) describe('signInput', () => { From 336c76bfda711b9171110abcbc6d933e94d114ab Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 3 Jul 2019 17:20:27 +0700 Subject: [PATCH 349/568] Add descriptions to invalid test cases from BIP174 spec --- test/fixtures/psbt.json | 18 ++++++++++++++++++ test/psbt.js | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 451fe91..a758eb2 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -2,74 +2,92 @@ "bip174": { "invalid": [ { + "description": "Network transaction, not PSBT format", "errorMessage": "Format Error: Invalid Magic Number", "psbt": "AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA==" }, { + "description": "PSBT missing outputs", "errorMessage": "Format Error: Unexpected End of PSBT", "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==" }, { + "description": "PSBT where one input has a filled scriptSig in the unsigned tx", "errorMessage": "Format Error: Transaction ScriptSigs are not empty", "psbt": "cHNidP8BAP0KAQIAAAACqwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QAAAAAakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpL+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAA=" }, { + "description": "PSBT where inputs and outputs are provided but without an unsigned tx", "errorMessage": "Format Error: Only one UNSIGNED_TX allowed", "psbt": "cHNidP8AAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA==" }, { + "description": "PSBT with duplicate keys in an input", "errorMessage": "Format Error: Keys must be unique for each input: input index 0 key 00", "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQA/AgAAAAH//////////////////////////////////////////wAAAAAA/////wEAAAAAAAAAAANqAQAAAAAAAAAA" }, { + "description": "PSBT With invalid global transaction typed key", "errorMessage": "Format Error: Invalid global key: 0001", "psbt": "cHNidP8CAAFVAgAAAAEnmiMjpd+1H8RfIg+liw/BPh4zQnkqhdfjbNYzO1y8OQAAAAAA/////wGgWuoLAAAAABl2qRT/6cAGEJfMO2NvLLBGD6T8Qn0rRYisAAAAAAABASCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA" }, { + "description": "PSBT With invalid input witness utxo typed key", "errorMessage": "Format Error: Invalid input key: 0100", "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAIBACCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA" }, { + "description": "PSBT With invalid pubkey length for input partial signature typed key", "errorMessage": "Format Error: invalid pubkey in key 0x0203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd", "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIQIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYwQwIgBCS1jv+qppThVZ6lyTu/1KiQZCJAVc3wcLZ3FGlELQcCH1yOsP6mUW1guKyzOtZO3mDoeFv7OqlLmb34YVHbmpoBAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GIQPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvVKuIgYDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYQtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA==" }, { + "description": "PSBT With invalid redeemscript typed key", "errorMessage": "Format Error: Invalid input key: 0400", "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQIEACIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA" }, { + "description": "PSBT With invalid witnessscript typed key", "errorMessage": "Format Error: Invalid input key: 0500", "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoECBQBHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA" }, { + "description": "PSBT With invalid bip32 typed key", "errorMessage": "Format Error: invalid pubkey in key 0x0603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd", "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriEGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb0QtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA==" }, { + "description": "PSBT With invalid non-witness utxo typed key", "errorMessage": "Format Error: Invalid input key: 0000", "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAIAALsCAAAAAarXOTEBi9JfhK5AC2iEi+CdtwbqwqwYKYur7nGrZW+LAAAAAEhHMEQCIFj2/HxqM+GzFUjUgcgmwBW9MBNarULNZ3kNq2bSrSQ7AiBKHO0mBMZzW2OT5bQWkd14sA8MWUL7n3UYVvqpOBV9ugH+////AoDw+gIAAAAAF6kUD7lGNCFpa4LIM68kHHjBfdveSTSH0PIKJwEAAAAXqRQpynT4oI+BmZQoGFyXtdhS5AY/YYdlAAAAAQfaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" }, { + "description": "PSBT With invalid final scriptsig typed key", "errorMessage": "Format Error: Invalid input key: 0700", "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAACBwDaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" }, { + "description": "PSBT With invalid final script witness typed key", "errorMessage": "Format Error: Invalid input key: 0800", "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAggA2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" }, { + "description": "PSBT With invalid pubkey in output BIP 32 derivation paths typed key", "errorMessage": "Format Error: invalid pubkey in key 0x0203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca587", "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" }, { + "description": "PSBT With invalid input sighash type typed key", "errorMessage": "Format Error: Invalid input key: 0300", "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A" }, { + "description": "PSBT With invalid output redeemScript typed key", "errorMessage": "Format Error: Invalid output key: 0000", "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A" }, { + "description": "PSBT With invalid output witnessScript typed key", "errorMessage": "Format Error: Unexpected End of PSBT", "psbt": "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A" } diff --git a/test/psbt.js b/test/psbt.js index 6ae59cd..f0d8846 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -8,8 +8,8 @@ const fixtures = require('./fixtures/psbt') describe(`Psbt`, () => { describe('BIP174 Test Vectors', () => { - fixtures.bip174.invalid.forEach((f, i) => { - it(`Invalid #${i + 1}: "${f.errorMessage}"`, () => { + fixtures.bip174.invalid.forEach(f => { + it(`Invalid: ${f.description}`, () => { assert.throws(() => { Psbt.fromBase64(f.psbt) }, {message: f.errorMessage}) From 54e2e55ef73247b6b489430dd15e015cd7312620 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 3 Jul 2019 17:24:43 +0700 Subject: [PATCH 350/568] Add descriptions to valid test cases from BIP174 spec --- test/fixtures/psbt.json | 30 ++++++++++++++++++++++++------ test/psbt.js | 6 +++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index a758eb2..3eaed01 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -93,12 +93,30 @@ } ], "valid": [ - "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA", - "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA", - "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA==", - "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=", - "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=", - "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=" + { + "description": "PSBT with one P2PKH input. Outputs are empty", + "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA" + }, + { + "description": "PSBT with one P2PKH input and one P2SH-P2WPKH input. First input is signed and finalized. Outputs are empty", + "psbt": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA" + }, + { + "description": "PSBT with one P2PKH input which has a non-final scriptSig and has a sighash type specified. Outputs are empty", + "psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA==" + }, + { + "description": "PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled.", + "psbt": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=" + }, + { + "description": "PSBT with one P2SH-P2WSH input of a 2-of-2 multisig, redeemScript, witnessScript, and keypaths are available. Contains one signature.", + "psbt": "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=" + }, + { + "description": "PSBT with unknown types in the inputs.", + "psbt": "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=" + } ] }, "signInput": { diff --git a/test/psbt.js b/test/psbt.js index f0d8846..ea9131f 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -16,10 +16,10 @@ describe(`Psbt`, () => { }) }) - fixtures.bip174.valid.forEach((psbt, i) => { - it(`Valid #${i + 1}`, () => { + fixtures.bip174.valid.forEach(f => { + it(`Valid: ${f.description}`, () => { assert.doesNotThrow(() => { - Psbt.fromBase64(psbt) + Psbt.fromBase64(f.psbt) }) }) }) From a876698d15be214a5a6424446e553151473887a2 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 3 Jul 2019 17:38:09 +0700 Subject: [PATCH 351/568] Test BIP174 signer check test cases --- test/fixtures/psbt.json | 26 ++++++++++++++++++++++++++ test/psbt.js | 10 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 3eaed01..d57894a 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -117,6 +117,32 @@ "description": "PSBT with unknown types in the inputs.", "psbt": "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=" } + ], + "failSignChecks": [ + { + "description": "A Witness UTXO is provided for a non-witness input", + "errorMessage": "Segwit input needs witnessScript if not P2WPKH", + "psbt": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEBItPf9QUAAAAAGXapFNSO0xELlAFMsRS9Mtb00GbcdCVriKwAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=", + "inputToCheck": 0 + }, + { + "description": "redeemScript with non-witness UTXO does not match the scriptPubKey", + "errorMessage": "Redeem script for input #0 doesn't match the scriptPubKey in the prevout", + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq8iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "inputToCheck": 0 + }, + { + "description": "redeemScript with witness UTXO does not match the scriptPubKey", + "errorMessage": "Redeem script for input #1 doesn't match the scriptPubKey in the prevout", + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQABBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "inputToCheck": 1 + }, + { + "description": "witnessScript with witness UTXO does not match the redeemScript", + "errorMessage": "Witness script for input #1 doesn't match the scriptPubKey in the prevout", + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSrSIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", + "inputToCheck": 1 + } ] }, "signInput": { diff --git a/test/psbt.js b/test/psbt.js index ea9131f..e99e592 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -23,6 +23,16 @@ describe(`Psbt`, () => { }) }) }) + + fixtures.bip174.failSignChecks.forEach(f => { + const keyPair = ECPair.makeRandom() + it(`Fails Signer checks: ${f.description}`, () => { + const psbt = Psbt.fromBase64(f.psbt) + assert.throws(() => { + psbt.signInput(f.inputToCheck, keyPair) + }, {message: f.errorMessage}) + }) + }) }) describe('signInput', () => { From df9008bae70981109201d4a565bb6a47de406c53 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Jul 2019 10:57:23 +0900 Subject: [PATCH 352/568] Update bip174 --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4195bf2..0eb81d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.10.tgz", - "integrity": "sha512-gFtSEMayg7HPKGnIQcEx5CqD/qHWuMlxLJ/+VV4k4Q2mcA0rY040JbNpFuCGVI6rJYv211f0NA7nkU4xkPX4nQ==" + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.11.tgz", + "integrity": "sha512-/WuRQOwgT0cGKDrNROOhOHpdtXyeuBJMqEgsBUdlCeFRLXIA8g5pHzbFmVm/v+OcYdenHfwzW/hOEf1xJOI27Q==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index 1346928..54d94f4 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.10", + "bip174": "0.0.11", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", From 2b8e8001bc6d8ebbafa83067119e4b229f5d8ef9 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Jul 2019 11:26:23 +0900 Subject: [PATCH 353/568] Support Addresses for outputs --- src/psbt.js | 28 ++++++++++++++++------------ ts_src/psbt.ts | 48 +++++++++++++++++++++++++++++++++++++++++++++--- types/psbt.d.ts | 12 ++++++++++-- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 3cbd899..d47bdb2 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -2,16 +2,17 @@ Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); const utils_1 = require('bip174/src/lib/utils'); +const address_1 = require('./address'); const crypto_1 = require('./crypto'); +const networks_1 = require('./networks'); const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { - // protected __TX: Transaction; - constructor(network) { + constructor(opts = {}) { super(); - this.network = network; + this.opts = Object.assign({}, DEFAULT_OPTS, opts); // // TODO: figure out a way to use a Transaction Object instead of a Buffer // // TODO: Caching, since .toBuffer() calls every time we get is lame. // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); @@ -24,6 +25,15 @@ class Psbt extends bip174_1.Psbt { // } // }); } + addOutput(outputData, allowNoInput = false, transactionOutputAdder) { + const { address } = outputData; + if (typeof address === 'string') { + const { network } = this.opts; + const script = address_1.toOutputScript(address, network); + outputData = Object.assign(outputData, { script }); + } + return super.addOutput(outputData, allowNoInput, transactionOutputAdder); + } extractTransaction() { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); const tx = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); @@ -107,15 +117,9 @@ class Psbt extends bip174_1.Psbt { } } exports.Psbt = Psbt; -// -// -// -// -// Helper functions -// -// -// -// +const DEFAULT_OPTS = { + network: networks_1.bitcoin, +}; function isFinalized(input) { return !!input.finalScriptSig || !!input.finalScriptWitness; } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 7c60a1d..eff576b 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,9 +1,14 @@ import { Psbt as PsbtBase } from 'bip174'; -import { PartialSig, PsbtInput } from 'bip174/src/lib/interfaces'; +import { + PartialSig, + PsbtInput, + TransactionOutput, +} from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; +import { toOutputScript } from './address'; import { hash160 } from './crypto'; import { Signer, SignerAsync } from './ecpair'; -import { Network } from './networks'; +import { bitcoin as btcNetwork, Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; import { Transaction } from './transaction'; @@ -11,8 +16,10 @@ const varuint = require('varuint-bitcoin'); export class Psbt extends PsbtBase { // protected __TX: Transaction; - constructor(public network?: Network) { + private opts: PsbtOpts; + constructor(opts: PsbtOptsOptional = {}) { super(); + this.opts = Object.assign({}, DEFAULT_OPTS, opts); // // TODO: figure out a way to use a Transaction Object instead of a Buffer // // TODO: Caching, since .toBuffer() calls every time we get is lame. // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); @@ -26,6 +33,29 @@ export class Psbt extends PsbtBase { // }); } + addOutput(outputData: TransactionOutput, allowNoInput?: boolean): this; + addOutput<T>( + outputData: T, + allowNoInput?: boolean, + transactionOutputAdder?: (output: T, txBuffer: Buffer) => Buffer, + ): this; + addOutput<T>( + outputData: T | TransactionOutput, + allowNoInput: boolean = false, + transactionOutputAdder?: ( + output: T | TransactionOutput, + txBuffer: Buffer, + ) => Buffer, + ): this { + const { address } = outputData as any; + if (typeof address === 'string') { + const { network } = this.opts; + const script = toOutputScript(address, network); + outputData = Object.assign(outputData, { script }); + } + return super.addOutput(outputData, allowNoInput, transactionOutputAdder); + } + extractTransaction(): Transaction { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); const tx = Transaction.fromBuffer(this.globalMap.unsignedTx!); @@ -134,6 +164,18 @@ export class Psbt extends PsbtBase { // // +interface PsbtOptsOptional { + network?: Network; +} + +interface PsbtOpts { + network: Network; +} + +const DEFAULT_OPTS = { + network: btcNetwork, +}; + function isFinalized(input: PsbtInput): boolean { return !!input.finalScriptSig || !!input.finalScriptWitness; } diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 26ae0a7..6275ee3 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,10 +1,14 @@ +/// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; +import { TransactionOutput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; export declare class Psbt extends PsbtBase { - network?: Network | undefined; - constructor(network?: Network | undefined); + private opts; + constructor(opts?: PsbtOptsOptional); + addOutput(outputData: TransactionOutput, allowNoInput?: boolean): this; + addOutput<T>(outputData: T, allowNoInput?: boolean, transactionOutputAdder?: (output: T, txBuffer: Buffer) => Buffer): this; extractTransaction(): Transaction; finalizeAllInputs(): { result: boolean; @@ -14,3 +18,7 @@ export declare class Psbt extends PsbtBase { signInput(inputIndex: number, keyPair: Signer): Psbt; signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>; } +interface PsbtOptsOptional { + network?: Network; +} +export {}; From b28c96d228ccfa9aadca3b53fd96b4a6e8bac4bd Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Jul 2019 12:03:48 +0900 Subject: [PATCH 354/568] Set to version 2 by default --- package-lock.json | 6 +++--- package.json | 2 +- src/psbt.js | 1 + ts_src/psbt.ts | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0eb81d0..a6aa1a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.11.tgz", - "integrity": "sha512-/WuRQOwgT0cGKDrNROOhOHpdtXyeuBJMqEgsBUdlCeFRLXIA8g5pHzbFmVm/v+OcYdenHfwzW/hOEf1xJOI27Q==" + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.12.tgz", + "integrity": "sha512-rYVuFTSCROv8iI07BhEddap+iXiFz2aiYEUSQR8rqv7JKWbMO2QQqCJMvr2E0df8wu5ySysd/CupIP4eG4ukqQ==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index 54d94f4..93d2188 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.11", + "bip174": "0.0.12", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", diff --git a/src/psbt.js b/src/psbt.js index d47bdb2..0157e9f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -12,6 +12,7 @@ const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { constructor(opts = {}) { super(); + this.setVersion(2); this.opts = Object.assign({}, DEFAULT_OPTS, opts); // // TODO: figure out a way to use a Transaction Object instead of a Buffer // // TODO: Caching, since .toBuffer() calls every time we get is lame. diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index eff576b..0752df2 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -19,6 +19,7 @@ export class Psbt extends PsbtBase { private opts: PsbtOpts; constructor(opts: PsbtOptsOptional = {}) { super(); + this.setVersion(2); this.opts = Object.assign({}, DEFAULT_OPTS, opts); // // TODO: figure out a way to use a Transaction Object instead of a Buffer // // TODO: Caching, since .toBuffer() calls every time we get is lame. From f7e726a8ebecb3e0cafe573f2b95258c12d3e4b7 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Jul 2019 13:33:08 +0900 Subject: [PATCH 355/568] Add TX cache and addInput addOutput --- package-lock.json | 6 +-- package.json | 2 +- src/psbt.js | 91 +++++++++++++++++++++++++++++----- ts_src/psbt.ts | 121 +++++++++++++++++++++++++++++++++++----------- types/psbt.d.ts | 9 ++-- 5 files changed, 181 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index a6aa1a4..768c79f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.12.tgz", - "integrity": "sha512-rYVuFTSCROv8iI07BhEddap+iXiFz2aiYEUSQR8rqv7JKWbMO2QQqCJMvr2E0df8wu5ySysd/CupIP4eG4ukqQ==" + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.13.tgz", + "integrity": "sha512-jWP7Lb27Nmbk6gaZKhJZOyk5LqRWs9z+R2xzgu3W8/iZXIIP2kcR6fh5lNg7GGOiWUaqanWC9rjrDVrBVbXKww==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index 93d2188..1fadfcb 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.12", + "bip174": "0.0.13", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", diff --git a/src/psbt.js b/src/psbt.js index 0157e9f..df1d491 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -3,6 +3,7 @@ Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); const utils_1 = require('bip174/src/lib/utils'); const address_1 = require('./address'); +const bufferutils_1 = require('./bufferutils'); const crypto_1 = require('./crypto'); const networks_1 = require('./networks'); const payments = require('./payments'); @@ -12,28 +13,92 @@ const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { constructor(opts = {}) { super(); + // set defaults this.setVersion(2); this.opts = Object.assign({}, DEFAULT_OPTS, opts); - // // TODO: figure out a way to use a Transaction Object instead of a Buffer - // // TODO: Caching, since .toBuffer() calls every time we get is lame. - // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); - // delete this.globalMap.unsignedTx; - // Object.defineProperty(this.globalMap, 'unsignedTx', { - // enumerable: true, - // writable: false, - // get(): Buffer { - // return this.__TX.toBuffer(); - // } - // }); + this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); + // set cache + const self = this; + delete this.globalMap.unsignedTx; + Object.defineProperty(this.globalMap, 'unsignedTx', { + enumerable: true, + get() { + if (self.__TX_BUF_CACHE !== undefined) { + return self.__TX_BUF_CACHE; + } else { + self.__TX_BUF_CACHE = self.__TX.toBuffer(); + return self.__TX_BUF_CACHE; + } + }, + set(data) { + self.__TX_BUF_CACHE = data; + }, + }); + // Make data hidden when enumerating + const dpew = (obj, attr, enumerable, writable) => + Object.defineProperty(obj, attr, { + enumerable, + writable, + }); + dpew(this, '__TX', false, false); + dpew(this, '__TX_BUF_CACHE', false, true); + dpew(this, 'opts', false, true); } - addOutput(outputData, allowNoInput = false, transactionOutputAdder) { + addInput(inputData) { + const self = this; + const inputAdder = (_inputData, txBuf) => { + if ( + !txBuf || + _inputData.hash === undefined || + _inputData.index === undefined || + (!Buffer.isBuffer(_inputData.hash) && + typeof _inputData.hash !== 'string') || + typeof _inputData.index !== 'number' + ) { + throw new Error('Error adding input.'); + } + const prevHash = Buffer.isBuffer(_inputData.hash) + ? _inputData.hash + : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex')); + self.__TX.ins.push({ + hash: prevHash, + index: _inputData.index, + script: Buffer.alloc(0), + sequence: + _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE, + witness: [], + }); + console.log(self.__TX); + return self.__TX.toBuffer(); + }; + return super.addInput(inputData, inputAdder); + } + addOutput(outputData) { const { address } = outputData; if (typeof address === 'string') { const { network } = this.opts; const script = address_1.toOutputScript(address, network); outputData = Object.assign(outputData, { script }); } - return super.addOutput(outputData, allowNoInput, transactionOutputAdder); + const self = this; + const outputAdder = (_outputData, txBuf) => { + if ( + !txBuf || + _outputData.script === undefined || + _outputData.value === undefined || + !Buffer.isBuffer(_outputData.script) || + typeof _outputData.value !== 'number' + ) { + throw new Error('Error adding output.'); + } + self.__TX.outs.push({ + script: _outputData.script, + value: _outputData.value, + }); + console.log(self.__TX); + return self.__TX.toBuffer(); + }; + return super.addOutput(outputData, true, outputAdder); } extractTransaction() { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 0752df2..58988f2 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -2,10 +2,12 @@ import { Psbt as PsbtBase } from 'bip174'; import { PartialSig, PsbtInput, + TransactionInput, TransactionOutput, } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; import { toOutputScript } from './address'; +import { reverseBuffer } from './bufferutils'; import { hash160 } from './crypto'; import { Signer, SignerAsync } from './ecpair'; import { bitcoin as btcNetwork, Network } from './networks'; @@ -15,46 +17,111 @@ import { Transaction } from './transaction'; const varuint = require('varuint-bitcoin'); export class Psbt extends PsbtBase { - // protected __TX: Transaction; + private __TX: Transaction; + private __TX_BUF_CACHE?: Buffer; private opts: PsbtOpts; constructor(opts: PsbtOptsOptional = {}) { super(); + // set defaults this.setVersion(2); this.opts = Object.assign({}, DEFAULT_OPTS, opts); - // // TODO: figure out a way to use a Transaction Object instead of a Buffer - // // TODO: Caching, since .toBuffer() calls every time we get is lame. - // this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); - // delete this.globalMap.unsignedTx; - // Object.defineProperty(this.globalMap, 'unsignedTx', { - // enumerable: true, - // writable: false, - // get(): Buffer { - // return this.__TX.toBuffer(); - // } - // }); + this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); + + // set cache + const self = this; + delete this.globalMap.unsignedTx; + Object.defineProperty(this.globalMap, 'unsignedTx', { + enumerable: true, + get(): Buffer { + if (self.__TX_BUF_CACHE !== undefined) { + return self.__TX_BUF_CACHE; + } else { + self.__TX_BUF_CACHE = self.__TX.toBuffer(); + return self.__TX_BUF_CACHE; + } + }, + set(data: Buffer): void { + self.__TX_BUF_CACHE = data; + }, + }); + + // Make data hidden when enumerating + const dpew = ( + obj: any, + attr: string, + enumerable: boolean, + writable: boolean, + ): any => + Object.defineProperty(obj, attr, { + enumerable, + writable, + }); + dpew(this, '__TX', false, false); + dpew(this, '__TX_BUF_CACHE', false, true); + dpew(this, 'opts', false, true); } - addOutput(outputData: TransactionOutput, allowNoInput?: boolean): this; - addOutput<T>( - outputData: T, - allowNoInput?: boolean, - transactionOutputAdder?: (output: T, txBuffer: Buffer) => Buffer, - ): this; - addOutput<T>( - outputData: T | TransactionOutput, - allowNoInput: boolean = false, - transactionOutputAdder?: ( - output: T | TransactionOutput, - txBuffer: Buffer, - ) => Buffer, - ): this { + addInput(inputData: TransactionInput): this { + const self = this; + const inputAdder = ( + _inputData: TransactionInput, + txBuf: Buffer, + ): Buffer => { + if ( + !txBuf || + (_inputData as any).hash === undefined || + (_inputData as any).index === undefined || + (!Buffer.isBuffer((_inputData as any).hash) && + typeof (_inputData as any).hash !== 'string') || + typeof (_inputData as any).index !== 'number' + ) { + throw new Error('Error adding input.'); + } + const prevHash = Buffer.isBuffer(_inputData.hash) + ? _inputData.hash + : reverseBuffer(Buffer.from(_inputData.hash, 'hex')); + self.__TX.ins.push({ + hash: prevHash, + index: _inputData.index, + script: Buffer.alloc(0), + sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE, + witness: [], + }); + console.log(self.__TX); + return self.__TX.toBuffer(); + }; + return super.addInput(inputData, inputAdder); + } + + addOutput(outputData: TransactionOutput): this { const { address } = outputData as any; if (typeof address === 'string') { const { network } = this.opts; const script = toOutputScript(address, network); outputData = Object.assign(outputData, { script }); } - return super.addOutput(outputData, allowNoInput, transactionOutputAdder); + const self = this; + const outputAdder = ( + _outputData: TransactionOutput, + txBuf: Buffer, + ): Buffer => { + if ( + !txBuf || + (_outputData as any).script === undefined || + (_outputData as any).value === undefined || + !Buffer.isBuffer((_outputData as any).script) || + typeof (_outputData as any).value !== 'number' + ) { + throw new Error('Error adding output.'); + } + self.__TX.outs.push({ + script: (_outputData as any).script!, + value: _outputData.value, + }); + console.log(self.__TX); + return self.__TX.toBuffer(); + }; + return super.addOutput(outputData, true, outputAdder); } extractTransaction(): Transaction { diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 6275ee3..a530cd0 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,14 +1,15 @@ -/// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; -import { TransactionOutput } from 'bip174/src/lib/interfaces'; +import { TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; export declare class Psbt extends PsbtBase { + private __TX; + private __TX_BUF_CACHE?; private opts; constructor(opts?: PsbtOptsOptional); - addOutput(outputData: TransactionOutput, allowNoInput?: boolean): this; - addOutput<T>(outputData: T, allowNoInput?: boolean, transactionOutputAdder?: (output: T, txBuffer: Buffer) => Buffer): this; + addInput(inputData: TransactionInput): this; + addOutput(outputData: TransactionOutput): this; extractTransaction(): Transaction; finalizeAllInputs(): { result: boolean; From 539c88596a4ae9f4ee4fac0a6adb3db6bb60708a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Jul 2019 13:42:34 +0900 Subject: [PATCH 356/568] Add version and locktime setters --- src/psbt.js | 12 +++++++++++- ts_src/psbt.ts | 14 +++++++++++++- types/psbt.d.ts | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index df1d491..ec91bd9 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -14,9 +14,9 @@ class Psbt extends bip174_1.Psbt { constructor(opts = {}) { super(); // set defaults - this.setVersion(2); this.opts = Object.assign({}, DEFAULT_OPTS, opts); this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); + this.setVersion(2); // set cache const self = this; delete this.globalMap.unsignedTx; @@ -44,6 +44,16 @@ class Psbt extends bip174_1.Psbt { dpew(this, '__TX_BUF_CACHE', false, true); dpew(this, 'opts', false, true); } + setVersion(version) { + this.__TX.version = version; + this.__TX_BUF_CACHE = undefined; + return this; + } + setLocktime(locktime) { + this.__TX.locktime = locktime; + this.__TX_BUF_CACHE = undefined; + return this; + } addInput(inputData) { const self = this; const inputAdder = (_inputData, txBuf) => { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 58988f2..46e9487 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -23,9 +23,9 @@ export class Psbt extends PsbtBase { constructor(opts: PsbtOptsOptional = {}) { super(); // set defaults - this.setVersion(2); this.opts = Object.assign({}, DEFAULT_OPTS, opts); this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); + this.setVersion(2); // set cache const self = this; @@ -61,6 +61,18 @@ export class Psbt extends PsbtBase { dpew(this, 'opts', false, true); } + setVersion(version: number): this { + this.__TX.version = version; + this.__TX_BUF_CACHE = undefined; + return this; + } + + setLocktime(locktime: number): this { + this.__TX.locktime = locktime; + this.__TX_BUF_CACHE = undefined; + return this; + } + addInput(inputData: TransactionInput): this { const self = this; const inputAdder = ( diff --git a/types/psbt.d.ts b/types/psbt.d.ts index a530cd0..213c31b 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -8,6 +8,8 @@ export declare class Psbt extends PsbtBase { private __TX_BUF_CACHE?; private opts; constructor(opts?: PsbtOptsOptional); + setVersion(version: number): this; + setLocktime(locktime: number): this; addInput(inputData: TransactionInput): this; addOutput(outputData: TransactionOutput): this; extractTransaction(): Transaction; From b98761a28329897ed400f8686bf87fa5c8489851 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Jul 2019 13:52:48 +0900 Subject: [PATCH 357/568] Promise fixes for async --- src/psbt.js | 31 +++++++++++++++++-------------- ts_src/psbt.ts | 37 +++++++++++++++++++++---------------- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index ec91bd9..b9ed061 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -175,20 +175,23 @@ class Psbt extends bip174_1.Psbt { return this.addPartialSigToInput(inputIndex, partialSig); } signInputAsync(inputIndex, keyPair) { - if (!keyPair || !keyPair.publicKey) - throw new Error('Need Signer to sign input'); - const { hash, sighashType } = getHashAndSighashType( - this.inputs, - inputIndex, - keyPair.publicKey, - this.globalMap.unsignedTx, - ); - return keyPair.sign(hash).then(signature => { - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(signature, sighashType), - }; - this.addPartialSigToInput(inputIndex, partialSig); + return new Promise((resolve, reject) => { + if (!keyPair || !keyPair.publicKey) + return reject(new Error('Need Signer to sign input')); + const { hash, sighashType } = getHashAndSighashType( + this.inputs, + inputIndex, + keyPair.publicKey, + this.globalMap.unsignedTx, + ); + Promise.resolve(keyPair.sign(hash)).then(signature => { + const partialSig = { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(signature, sighashType), + }; + this.addPartialSigToInput(inputIndex, partialSig); + resolve(); + }); }); } } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 46e9487..2c36130 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -214,23 +214,28 @@ export class Psbt extends PsbtBase { } signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void> { - if (!keyPair || !keyPair.publicKey) - throw new Error('Need Signer to sign input'); - const { hash, sighashType } = getHashAndSighashType( - this.inputs, - inputIndex, - keyPair.publicKey, - this.globalMap.unsignedTx!, + return new Promise( + (resolve, reject): void => { + if (!keyPair || !keyPair.publicKey) + return reject(new Error('Need Signer to sign input')); + const { hash, sighashType } = getHashAndSighashType( + this.inputs, + inputIndex, + keyPair.publicKey, + this.globalMap.unsignedTx!, + ); + + Promise.resolve(keyPair.sign(hash)).then(signature => { + const partialSig = { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(signature, sighashType), + }; + + this.addPartialSigToInput(inputIndex, partialSig); + resolve(); + }); + }, ); - - return keyPair.sign(hash).then(signature => { - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(signature, sighashType), - }; - - this.addPartialSigToInput(inputIndex, partialSig); - }); } } From 5b5daf84dd7deeffa3f933fa03a29001fcc6270d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Jul 2019 14:33:36 +0900 Subject: [PATCH 358/568] Remove unnecessary extra Transaction Buffer parsing --- src/psbt.js | 57 ++++++++++++++++++++++++++++++++-------- ts_src/psbt.ts | 70 ++++++++++++++++++++++++++++++++++++++++--------- types/psbt.d.ts | 5 +++- 3 files changed, 108 insertions(+), 24 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index b9ed061..4a6306f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -11,6 +11,39 @@ const bscript = require('./script'); const transaction_1 = require('./transaction'); const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { + static fromTransaction(txBuf) { + const tx = transaction_1.Transaction.fromBuffer(txBuf); + const psbt = new this(); + psbt.__TX = tx; + let inputCount = tx.ins.length; + let outputCount = tx.outs.length; + while (inputCount > 0) { + psbt.inputs.push({ + keyVals: [], + }); + inputCount--; + } + while (outputCount > 0) { + psbt.outputs.push({ + keyVals: [], + }); + outputCount--; + } + return psbt; + } + static fromBuffer(buffer) { + let tx; + const txCountGetter = txBuf => { + tx = transaction_1.Transaction.fromBuffer(txBuf); + return { + inputCount: tx.ins.length, + outputCount: tx.outs.length, + }; + }; + const psbt = super.fromBuffer(buffer, txCountGetter); + psbt.__TX = tx; + return psbt; + } constructor(opts = {}) { super(); // set defaults @@ -40,7 +73,7 @@ class Psbt extends bip174_1.Psbt { enumerable, writable, }); - dpew(this, '__TX', false, false); + dpew(this, '__TX', false, true); dpew(this, '__TX_BUF_CACHE', false, true); dpew(this, 'opts', false, true); } @@ -112,7 +145,7 @@ class Psbt extends bip174_1.Psbt { } extractTransaction() { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); - const tx = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); + const tx = this.__TX.clone(); this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { @@ -138,7 +171,7 @@ class Psbt extends bip174_1.Psbt { const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, - this.globalMap.unsignedTx, + this.__TX, ); if (!script) return false; const scriptType = classifyScript(script); @@ -166,7 +199,7 @@ class Psbt extends bip174_1.Psbt { this.inputs, inputIndex, keyPair.publicKey, - this.globalMap.unsignedTx, + this.__TX, ); const partialSig = { pubkey: keyPair.publicKey, @@ -182,7 +215,7 @@ class Psbt extends bip174_1.Psbt { this.inputs, inputIndex, keyPair.publicKey, - this.globalMap.unsignedTx, + this.__TX, ); Promise.resolve(keyPair.sign(hash)).then(signature => { const partialSig = { @@ -202,9 +235,13 @@ const DEFAULT_OPTS = { function isFinalized(input) { return !!input.finalScriptSig || !!input.finalScriptWitness; } -function getHashAndSighashType(inputs, inputIndex, pubkey, txBuf) { +function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx) { const input = utils_1.checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf); + const { hash, sighashType, script } = getHashForSig( + inputIndex, + input, + unsignedTx, + ); checkScriptForPubkey(pubkey, script); return { hash, @@ -322,8 +359,7 @@ function checkScriptForPubkey(pubkey, script) { ); } } -const getHashForSig = (inputIndex, input, txBuf) => { - const unsignedTx = transaction_1.Transaction.fromBuffer(txBuf); +const getHashForSig = (inputIndex, input, unsignedTx) => { const sighashType = input.sighashType || transaction_1.Transaction.SIGHASH_ALL; let hash; @@ -442,7 +478,7 @@ const classifyScript = script => { if (isP2PK(script)) return 'pubkey'; return 'nonstandard'; }; -function getScriptFromInput(inputIndex, input, _unsignedTx) { +function getScriptFromInput(inputIndex, input, unsignedTx) { const res = { script: null, isSegwit: false, @@ -454,7 +490,6 @@ function getScriptFromInput(inputIndex, input, _unsignedTx) { res.isP2SH = true; res.script = input.redeemScript; } else { - const unsignedTx = transaction_1.Transaction.fromBuffer(_unsignedTx); const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( input.nonWitnessUtxo, ); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 2c36130..0aee319 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -17,6 +17,50 @@ import { Transaction } from './transaction'; const varuint = require('varuint-bitcoin'); export class Psbt extends PsbtBase { + static fromTransaction<T extends typeof PsbtBase>( + this: T, + txBuf: Buffer, + ): InstanceType<T> { + const tx = Transaction.fromBuffer(txBuf); + const psbt = new this() as Psbt; + psbt.__TX = tx; + let inputCount = tx.ins.length; + let outputCount = tx.outs.length; + while (inputCount > 0) { + psbt.inputs.push({ + keyVals: [], + }); + inputCount--; + } + while (outputCount > 0) { + psbt.outputs.push({ + keyVals: [], + }); + outputCount--; + } + return psbt as InstanceType<T>; + } + static fromBuffer<T extends typeof PsbtBase>( + this: T, + buffer: Buffer, + ): InstanceType<T> { + let tx: Transaction | undefined; + const txCountGetter = ( + txBuf: Buffer, + ): { + inputCount: number; + outputCount: number; + } => { + tx = Transaction.fromBuffer(txBuf); + return { + inputCount: tx.ins.length, + outputCount: tx.outs.length, + }; + }; + const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt; + psbt.__TX = tx!; + return psbt as InstanceType<T>; + } private __TX: Transaction; private __TX_BUF_CACHE?: Buffer; private opts: PsbtOpts; @@ -56,7 +100,7 @@ export class Psbt extends PsbtBase { enumerable, writable, }); - dpew(this, '__TX', false, false); + dpew(this, '__TX', false, true); dpew(this, '__TX_BUF_CACHE', false, true); dpew(this, 'opts', false, true); } @@ -138,7 +182,7 @@ export class Psbt extends PsbtBase { extractTransaction(): Transaction { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); - const tx = Transaction.fromBuffer(this.globalMap.unsignedTx!); + const tx = this.__TX.clone(); this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { @@ -169,7 +213,7 @@ export class Psbt extends PsbtBase { const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, - this.globalMap.unsignedTx!, + this.__TX, ); if (!script) return false; @@ -195,14 +239,14 @@ export class Psbt extends PsbtBase { return true; } - signInput(inputIndex: number, keyPair: Signer): Psbt { + signInput(inputIndex: number, keyPair: Signer): this { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); const { hash, sighashType } = getHashAndSighashType( this.inputs, inputIndex, keyPair.publicKey, - this.globalMap.unsignedTx!, + this.__TX, ); const partialSig = { @@ -222,7 +266,7 @@ export class Psbt extends PsbtBase { this.inputs, inputIndex, keyPair.publicKey, - this.globalMap.unsignedTx!, + this.__TX, ); Promise.resolve(keyPair.sign(hash)).then(signature => { @@ -269,13 +313,17 @@ function getHashAndSighashType( inputs: PsbtInput[], inputIndex: number, pubkey: Buffer, - txBuf: Buffer, + unsignedTx: Transaction, ): { hash: Buffer; sighashType: number; } { const input = checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf); + const { hash, sighashType, script } = getHashForSig( + inputIndex, + input, + unsignedTx, + ); checkScriptForPubkey(pubkey, script); return { hash, @@ -424,9 +472,8 @@ interface HashForSigData { const getHashForSig = ( inputIndex: number, input: PsbtInput, - txBuf: Buffer, + unsignedTx: Transaction, ): HashForSigData => { - const unsignedTx = Transaction.fromBuffer(txBuf); const sighashType = input.sighashType || Transaction.SIGHASH_ALL; let hash: Buffer; let script: Buffer; @@ -571,7 +618,7 @@ interface GetScriptReturn { function getScriptFromInput( inputIndex: number, input: PsbtInput, - _unsignedTx: Buffer, + unsignedTx: Transaction, ): GetScriptReturn { const res: GetScriptReturn = { script: null, @@ -584,7 +631,6 @@ function getScriptFromInput( res.isP2SH = true; res.script = input.redeemScript; } else { - const unsignedTx = Transaction.fromBuffer(_unsignedTx); const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 213c31b..a203854 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,9 +1,12 @@ +/// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; import { TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; export declare class Psbt extends PsbtBase { + static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>; + static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>; private __TX; private __TX_BUF_CACHE?; private opts; @@ -18,7 +21,7 @@ export declare class Psbt extends PsbtBase { inputResults: boolean[]; }; finalizeInput(inputIndex: number): boolean; - signInput(inputIndex: number, keyPair: Signer): Psbt; + signInput(inputIndex: number, keyPair: Signer): this; signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>; } interface PsbtOptsOptional { From 3e7f490093303417e069a8a629103a774be97fad Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Jul 2019 14:45:50 +0900 Subject: [PATCH 359/568] Check for input empty on parse --- src/psbt.js | 14 ++++++++++++++ ts_src/psbt.ts | 15 +++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index 4a6306f..a85a23a 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -13,6 +13,7 @@ const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { static fromTransaction(txBuf) { const tx = transaction_1.Transaction.fromBuffer(txBuf); + checkTxEmpty(tx); const psbt = new this(); psbt.__TX = tx; let inputCount = tx.ins.length; @@ -35,6 +36,7 @@ class Psbt extends bip174_1.Psbt { let tx; const txCountGetter = txBuf => { tx = transaction_1.Transaction.fromBuffer(txBuf); + checkTxEmpty(tx); return { inputCount: tx.ins.length, outputCount: tx.outs.length, @@ -564,3 +566,15 @@ function scriptWitnessToWitnessStack(buffer) { return readVector(); } const range = n => [...Array(n).keys()]; +function checkTxEmpty(tx) { + const isEmpty = tx.ins.every( + input => + input.script && + input.script.length === 0 && + input.witness && + input.witness.length === 0, + ); + if (!isEmpty) { + throw new Error('Format Error: Transaction ScriptSigs are not empty'); + } +} diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 0aee319..14f0c08 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -22,6 +22,7 @@ export class Psbt extends PsbtBase { txBuf: Buffer, ): InstanceType<T> { const tx = Transaction.fromBuffer(txBuf); + checkTxEmpty(tx); const psbt = new this() as Psbt; psbt.__TX = tx; let inputCount = tx.ins.length; @@ -52,6 +53,7 @@ export class Psbt extends PsbtBase { outputCount: number; } => { tx = Transaction.fromBuffer(txBuf); + checkTxEmpty(tx); return { inputCount: tx.ins.length, outputCount: tx.outs.length, @@ -719,3 +721,16 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] { } const range = (n: number): number[] => [...Array(n).keys()]; + +function checkTxEmpty(tx: Transaction): void { + const isEmpty = tx.ins.every( + input => + input.script && + input.script.length === 0 && + input.witness && + input.witness.length === 0, + ); + if (!isEmpty) { + throw new Error('Format Error: Transaction ScriptSigs are not empty'); + } +} From 45bd5b47516dad0a1f1ba6d37fba9cf1218f2a8f Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Jul 2019 17:35:39 +0900 Subject: [PATCH 360/568] Check for signatures, add setSequence --- src/psbt.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++ ts_src/psbt.ts | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ types/psbt.d.ts | 1 + 3 files changed, 124 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index a85a23a..eaa8d62 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -80,16 +80,31 @@ class Psbt extends bip174_1.Psbt { dpew(this, 'opts', false, true); } setVersion(version) { + check32Bit(version); + checkInputsForPartialSig(this.inputs, 'setVersion'); this.__TX.version = version; this.__TX_BUF_CACHE = undefined; return this; } setLocktime(locktime) { + check32Bit(locktime); + checkInputsForPartialSig(this.inputs, 'setLocktime'); this.__TX.locktime = locktime; this.__TX_BUF_CACHE = undefined; return this; } + setSequence(inputIndex, sequence) { + check32Bit(sequence); + checkInputsForPartialSig(this.inputs, 'setSequence'); + if (this.__TX.ins.length <= inputIndex) { + throw new Error('Input index too high'); + } + this.__TX.ins[inputIndex].sequence = sequence; + this.__TX_BUF_CACHE = undefined; + return this; + } addInput(inputData) { + checkInputsForPartialSig(this.inputs, 'addInput'); const self = this; const inputAdder = (_inputData, txBuf) => { if ( @@ -119,6 +134,7 @@ class Psbt extends bip174_1.Psbt { return super.addInput(inputData, inputAdder); } addOutput(outputData) { + checkInputsForPartialSig(this.inputs, 'addOutput'); const { address } = outputData; if (typeof address === 'string') { const { network } = this.opts; @@ -578,3 +594,47 @@ function checkTxEmpty(tx) { throw new Error('Format Error: Transaction ScriptSigs are not empty'); } } +function checkInputsForPartialSig(inputs, action) { + inputs.forEach(input => { + let throws = false; + if ((input.partialSig || []).length > 0) { + if (input.sighashType !== undefined) { + const whitelist = []; + const isAnyoneCanPay = + input.sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY; + if (isAnyoneCanPay) whitelist.push('addInput'); + if (!isAnyoneCanPay && action === 'addInput') { + throws = true; + } + const hashType = input.sighashType & 0x1f; + switch (hashType) { + case transaction_1.Transaction.SIGHASH_ALL: + break; + case transaction_1.Transaction.SIGHASH_SINGLE: + case transaction_1.Transaction.SIGHASH_NONE: + whitelist.push('addOutput'); + whitelist.push('setSequence'); + break; + } + if (whitelist.indexOf(action) === -1) { + throws = true; + } + } else { + throws = true; + } + } + if (throws) { + throw new Error('Can not modify transaction, signatures exist.'); + } + }); +} +function check32Bit(num) { + if ( + typeof num !== 'number' || + num !== Math.floor(num) || + num > 0xffffffff || + num < 0 + ) { + throw new Error('Invalid 32 bit integer'); + } +} diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 14f0c08..0b019a3 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -108,18 +108,34 @@ export class Psbt extends PsbtBase { } setVersion(version: number): this { + check32Bit(version); + checkInputsForPartialSig(this.inputs, 'setVersion'); this.__TX.version = version; this.__TX_BUF_CACHE = undefined; return this; } setLocktime(locktime: number): this { + check32Bit(locktime); + checkInputsForPartialSig(this.inputs, 'setLocktime'); this.__TX.locktime = locktime; this.__TX_BUF_CACHE = undefined; return this; } + setSequence(inputIndex: number, sequence: number): this { + check32Bit(sequence); + checkInputsForPartialSig(this.inputs, 'setSequence'); + if (this.__TX.ins.length <= inputIndex) { + throw new Error('Input index too high'); + } + this.__TX.ins[inputIndex].sequence = sequence; + this.__TX_BUF_CACHE = undefined; + return this; + } + addInput(inputData: TransactionInput): this { + checkInputsForPartialSig(this.inputs, 'addInput'); const self = this; const inputAdder = ( _inputData: TransactionInput, @@ -152,6 +168,7 @@ export class Psbt extends PsbtBase { } addOutput(outputData: TransactionOutput): this { + checkInputsForPartialSig(this.inputs, 'addOutput'); const { address } = outputData as any; if (typeof address === 'string') { const { network } = this.opts; @@ -734,3 +751,49 @@ function checkTxEmpty(tx: Transaction): void { throw new Error('Format Error: Transaction ScriptSigs are not empty'); } } + +function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void { + inputs.forEach(input => { + let throws = false; + if ((input.partialSig || []).length > 0) { + if (input.sighashType !== undefined) { + const whitelist: string[] = []; + const isAnyoneCanPay = + input.sighashType & Transaction.SIGHASH_ANYONECANPAY; + if (isAnyoneCanPay) whitelist.push('addInput'); + if (!isAnyoneCanPay && action === 'addInput') { + throws = true; + } + const hashType = input.sighashType & 0x1f; + switch (hashType) { + case Transaction.SIGHASH_ALL: + break; + case Transaction.SIGHASH_SINGLE: + case Transaction.SIGHASH_NONE: + whitelist.push('addOutput'); + whitelist.push('setSequence'); + break; + } + if (whitelist.indexOf(action) === -1) { + throws = true; + } + } else { + throws = true; + } + } + if (throws) { + throw new Error('Can not modify transaction, signatures exist.'); + } + }); +} + +function check32Bit(num: number): void { + if ( + typeof num !== 'number' || + num !== Math.floor(num) || + num > 0xffffffff || + num < 0 + ) { + throw new Error('Invalid 32 bit integer'); + } +} diff --git a/types/psbt.d.ts b/types/psbt.d.ts index a203854..1aa6f28 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -13,6 +13,7 @@ export declare class Psbt extends PsbtBase { constructor(opts?: PsbtOptsOptional); setVersion(version: number): this; setLocktime(locktime: number): this; + setSequence(inputIndex: number, sequence: number): this; addInput(inputData: TransactionInput): this; addOutput(outputData: TransactionOutput): this; extractTransaction(): Transaction; From 2501fc92bcd8f2467e715c36b1be554186b35cee Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 13:47:18 +0700 Subject: [PATCH 361/568] Test BIP174 creator check test cases --- test/fixtures/psbt.json | 25 +++++++++++++++++++++++++ test/psbt.js | 14 ++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index d57894a..49d4f1f 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -143,6 +143,31 @@ "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSrSIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=", "inputToCheck": 1 } + ], + "creator": [ + { + "inputs": [ + { + "hash": "75ddabb27b8845f5247975c8a5ba7c6f336c4570708ebe230caf6db5217ae858", + "index": 0 + }, + { + "hash": "1dea7cd05979072a3578cab271c02244ea8a090bbb46aa680a65ecd027048d83", + "index": 1 + } + ], + "outputs": [ + { + "script": "0014d85c2b71d0060b09c9886aeb815e50991dda124d", + "value": 149990000 + }, + { + "script": "001400aea9a2e5f0f876a588df5546e8742d1d87008f", + "value": 100000000 + } + ], + "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=" + } ] }, "signInput": { diff --git a/test/psbt.js b/test/psbt.js index e99e592..98f407b 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -33,6 +33,20 @@ describe(`Psbt`, () => { }, {message: f.errorMessage}) }) }) + + fixtures.bip174.creator.forEach(f => { + it('Creates expected PSBT', () => { + const psbt = new Psbt() + for (const input of f.inputs) { + psbt.addInput(input) + } + for (const output of f.outputs) { + const script = Buffer.from(output.script, 'hex'); + psbt.addOutput({...output, script}) + } + assert.strictEqual(psbt.toBase64(), f.result) + }) + }) }) describe('signInput', () => { From 275618ed43e1eb533c37fdbc2967cf1bfc916b93 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 13:52:19 +0700 Subject: [PATCH 362/568] Remove console.log --- src/psbt.js | 2 -- ts_src/psbt.ts | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index eaa8d62..b2bbeda 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -128,7 +128,6 @@ class Psbt extends bip174_1.Psbt { _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE, witness: [], }); - console.log(self.__TX); return self.__TX.toBuffer(); }; return super.addInput(inputData, inputAdder); @@ -156,7 +155,6 @@ class Psbt extends bip174_1.Psbt { script: _outputData.script, value: _outputData.value, }); - console.log(self.__TX); return self.__TX.toBuffer(); }; return super.addOutput(outputData, true, outputAdder); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 0b019a3..fea9a67 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -161,7 +161,6 @@ export class Psbt extends PsbtBase { sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE, witness: [], }); - console.log(self.__TX); return self.__TX.toBuffer(); }; return super.addInput(inputData, inputAdder); @@ -193,7 +192,6 @@ export class Psbt extends PsbtBase { script: (_outputData as any).script!, value: _outputData.value, }); - console.log(self.__TX); return self.__TX.toBuffer(); }; return super.addOutput(outputData, true, outputAdder); From a32d1c3eac4e4fa6e2a3ddfcfa59008b98e206e6 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 14:53:51 +0700 Subject: [PATCH 363/568] Test BIP174 updater check test cases --- test/fixtures/psbt.json | 76 +++++++++++++++++++++++++++++++++++++++++ test/psbt.js | 51 +++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 49d4f1f..ab9a470 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -168,6 +168,82 @@ ], "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=" } + ], + "updater": [ + { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=", + "inputData": [ + { + "nonWitnessUtxo": "0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000", + "redeemScript": "5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae", + "bip32Derivation": [ + { + "masterFingerprint": "d90c6a4f", + "pubkey": "029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f", + "path": "m/0'/0'/0'" + }, + { + "masterFingerprint": "d90c6a4f", + "pubkey": "02dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7", + "path": "m/0'/0'/1'" + } + ] + }, + { + "witnessUtxo": { + "script": "a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887", + "value": 200000000 + }, + "redeemScript": "00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903", + "witnessScript": "522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae", + "bip32Derivation": [ + { + "masterFingerprint": "d90c6a4f", + "pubkey": "023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73", + "path": "m/0'/0'/3'" + }, + { + "masterFingerprint": "d90c6a4f", + "pubkey": "03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc", + "path": "m/0'/0'/2'" + } + ] + } + ], + "outputData": [ + { + "bip32Derivation": [ + { + "masterFingerprint": "d90c6a4f", + "pubkey": "03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58771", + "path": "m/0'/0'/4'" + } + ] + }, + { + "bip32Derivation": [ + { + "masterFingerprint": "d90c6a4f", + "pubkey": "027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b50051096", + "path": "m/0'/0'/5'" + } + ] + } + ], + "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==" + }, + { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", + "inputData": [ + { + "sighashType": 1 + }, + { + "sighashType": 1 + } + ], + "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" + } ] }, "signInput": { diff --git a/test/psbt.js b/test/psbt.js index 98f407b..cd80d73 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -6,6 +6,27 @@ const Psbt = require('..').Psbt const fixtures = require('./fixtures/psbt') +const upperCaseFirstLetter = str => str.replace(/^./, s => s.toUpperCase()) + +const b = hex => Buffer.from(hex, 'hex'); + +const initBuffers = (attr, data) => { + if ([ + 'nonWitnessUtxo', + 'redeemScript', + 'witnessScript' + ].includes(attr)) { + data = b(data) + } else if (attr === 'bip32Derivation') { + data.masterFingerprint = b(data.masterFingerprint) + data.pubkey = b(data.pubkey) + } else if (attr === 'witnessUtxo') { + data.script = b(data.script) + } + + return data +}; + describe(`Psbt`, () => { describe('BIP174 Test Vectors', () => { fixtures.bip174.invalid.forEach(f => { @@ -47,6 +68,36 @@ describe(`Psbt`, () => { assert.strictEqual(psbt.toBase64(), f.result) }) }) + + fixtures.bip174.updater.forEach(f => { + it('Updates PSBT to the expected result', () => { + const psbt = Psbt.fromBase64(f.psbt) + + for (const inputOrOutput of ['input', 'output']) { + const fixtureData = f[`${inputOrOutput}Data`] + if (fixtureData) { + for (const [i, data] of fixtureData.entries()) { + const attrs = Object.keys(data) + for (const attr of attrs) { + const upperAttr = upperCaseFirstLetter(attr) + let adder = psbt[`add${upperAttr}To${upperCaseFirstLetter(inputOrOutput)}`] + if (adder !== undefined) { + adder = adder.bind(psbt) + const arg = data[attr] + if (Array.isArray(arg)) { + arg.forEach(a => adder(i, initBuffers(attr, a))) + } else { + adder(i, initBuffers(attr, arg)) + } + } + } + } + } + } + + assert.strictEqual(psbt.toBase64(), f.result) + }) + }) }) describe('signInput', () => { From 30815e9e8fe5a9a74129800013a1193848b854b0 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 15:32:16 +0700 Subject: [PATCH 364/568] Test BIP174 signer test cases --- test/fixtures/psbt.json | 30 ++++++++++++++++++++++++++++++ test/psbt.js | 14 ++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index ab9a470..3e65598 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -244,6 +244,36 @@ ], "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" } + ], + "signer": [ + { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", + "keys": [ + { + "inputToSign": 0, + "WIF": "cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr" + }, + { + "inputToSign": 1, + "WIF": "cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d" + } + ], + "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEBAwQBAAAAAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==" + }, + { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", + "keys": [ + { + "inputToSign": 0, + "WIF": "cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au" + }, + { + "inputToSign": 1, + "WIF": "cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE" + } + ], + "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" + } ] }, "signInput": { diff --git a/test/psbt.js b/test/psbt.js index cd80d73..00f50ba 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -3,6 +3,7 @@ const assert = require('assert') const ECPair = require('../src/ecpair') const Psbt = require('..').Psbt +const NETWORKS = require('../src/networks') const fixtures = require('./fixtures/psbt') @@ -100,6 +101,19 @@ describe(`Psbt`, () => { }) }) + fixtures.bip174.signer.forEach(f => { + it('Signs PSBT to the expected result', () => { + const psbt = Psbt.fromBase64(f.psbt) + + f.keys.forEach(({inputToSign, WIF}) => { + const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet); + psbt.signInput(inputToSign, keyPair); + }) + + assert.strictEqual(psbt.toBase64(), f.result) + }) + }) + describe('signInput', () => { fixtures.signInput.checks.forEach(f => { it(f.description, () => { From 4e55ab0f2057b796441f938efbfcd78f02028ef8 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 16:11:19 +0700 Subject: [PATCH 365/568] Test BIP174 combiner test cases --- test/fixtures/psbt.json | 9 +++++++++ test/psbt.js | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 3e65598..5928ff6 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -274,6 +274,15 @@ ], "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" } + ], + "combiner": [ + { + "psbts": [ + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEBAwQBAAAAAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" + ], + "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" + } ] }, "signInput": { diff --git a/test/psbt.js b/test/psbt.js index 00f50ba..f417079 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -114,6 +114,20 @@ describe(`Psbt`, () => { }) }) + fixtures.bip174.combiner.forEach(f => { + it('Combines two PSBTs to the expected result', () => { + const psbts = f.psbts.map(psbt => Psbt.fromBase64(psbt)) + + psbts[0].combine(psbts[1]) + + // Produces a different Base64 string due to implemetation specific key-value ordering. + // That means this test will fail: + // assert.strictEqual(psbts[0].toBase64(), f.result) + // However, if we compare the actual PSBT properties we can see they are logically identical: + assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result)) + }) + }) + describe('signInput', () => { fixtures.signInput.checks.forEach(f => { it(f.description, () => { From a80155dbdbd1ceff2466fb2054792ecc327a1f3a Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 16:16:19 +0700 Subject: [PATCH 366/568] Test BIP174 finalizer test cases --- test/fixtures/psbt.json | 6 ++++++ test/psbt.js | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 5928ff6..5344894 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -283,6 +283,12 @@ ], "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" } + ], + "finalizer": [ + { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", + "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==" + } ] }, "signInput": { diff --git a/test/psbt.js b/test/psbt.js index f417079..213afc5 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -128,6 +128,16 @@ describe(`Psbt`, () => { }) }) + fixtures.bip174.finalizer.forEach(f => { + it('Finalizes inputs and gives the expected PSBT', () => { + const psbt = Psbt.fromBase64(f.psbt) + + psbt.finalizeAllInputs() + + assert.strictEqual(psbt.toBase64(), f.result) + }) + }) + describe('signInput', () => { fixtures.signInput.checks.forEach(f => { it(f.description, () => { From 35cf120c33b64a57c396a6a1132a8d9b825d2078 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 16:17:50 +0700 Subject: [PATCH 367/568] Add extra combiner test case --- test/fixtures/psbt.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 5344894..37416cf 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -282,6 +282,13 @@ "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210gwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gEBAwQBAAAAAQRHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4iBgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfxDZDGpPAAAAgAAAAIAAAACAIgYC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtcQ2QxqTwAAAIAAAACAAQAAgAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohyICAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBAQMEAQAAAAEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA=" ], "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" + }, + { + "psbts": [ + "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcICQ8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgJDwECAwQFBgcICQoLDA0ODwA=", + "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcIEA8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCBAPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgQDwECAwQFBgcICQoLDA0ODwA=" + ], + "result": "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAKDwECAwQFBgcICQ8BAgMEBQYHCAkKCwwNDg8KDwECAwQFBgcIEA8BAgMEBQYHCAkKCwwNDg8ACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PCg8BAgMEBQYHCBAPAQIDBAUGBwgJCgsMDQ4PAAoPAQIDBAUGBwgJDwECAwQFBgcICQoLDA0ODwoPAQIDBAUGBwgQDwECAwQFBgcICQoLDA0ODwA=" } ], "finalizer": [ From e3efdbdb99e904aba5d888dedced11ead9fddd1f Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 16:23:55 +0700 Subject: [PATCH 368/568] Test BIP174 extractor test cases --- test/fixtures/psbt.json | 6 ++++++ test/psbt.js | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 37416cf..7242b03 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -296,6 +296,12 @@ "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==" } + ], + "extractor": [ + { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==", + "transaction": "0200000000010258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500000000da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752aeffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d01000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000" + } ] }, "signInput": { diff --git a/test/psbt.js b/test/psbt.js index 213afc5..5d59a88 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -138,6 +138,16 @@ describe(`Psbt`, () => { }) }) + fixtures.bip174.extractor.forEach(f => { + it('Extracts the expected transaction from a PSBT', () => { + const psbt = Psbt.fromBase64(f.psbt) + + const transaction = psbt.extractTransaction().toHex() + + assert.strictEqual(transaction, f.transaction) + }) + }) + describe('signInput', () => { fixtures.signInput.checks.forEach(f => { it(f.description, () => { From dc23b8cce0b9a59e6f54046c32f8fe323dda363a Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 17:00:01 +0700 Subject: [PATCH 369/568] Test fromTransaction --- test/fixtures/psbt.json | 8 +++++++- test/psbt.js | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 7242b03..00877f9 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -391,5 +391,11 @@ } } ] - } + }, + "fromTransaction": [ + { + "transaction": "020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000", + "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=" + } + ] } diff --git a/test/psbt.js b/test/psbt.js index 5d59a88..290c5e5 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -169,4 +169,13 @@ describe(`Psbt`, () => { }) }) }) + + describe('fromTransaction', () => { + fixtures.fromTransaction.forEach(f => { + it('Creates the expected PSBT from a transaction buffer', () => { + const psbt = Psbt.fromTransaction(Buffer.from(f.transaction, 'hex')) + assert.strictEqual(psbt.toBase64(), f.result) + }) + }) + }) }) From 09a6c37430c76eb90a583f6e164dc6f98e7daee3 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 17:00:20 +0700 Subject: [PATCH 370/568] Test setVersion --- test/psbt.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/psbt.js b/test/psbt.js index 290c5e5..0f9a2f8 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -178,4 +178,14 @@ describe(`Psbt`, () => { }) }) }) + + describe('setVersion', () => { + it('Sets the version value of the unsigned transaction', () => { + const psbt = new Psbt() + + assert.strictEqual(psbt.extractTransaction().version, 2) + psbt.setVersion(1) + assert.strictEqual(psbt.extractTransaction().version, 1) + }) + }) }) From 871e5877117de551affeca88f2cbab84f3d3b240 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 17:00:34 +0700 Subject: [PATCH 371/568] Test setLocktime --- test/psbt.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/psbt.js b/test/psbt.js index 0f9a2f8..d790b63 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -188,4 +188,14 @@ describe(`Psbt`, () => { assert.strictEqual(psbt.extractTransaction().version, 1) }) }) + + describe('setLocktime', () => { + it('Sets the nLockTime value of the unsigned transaction', () => { + const psbt = new Psbt() + + assert.strictEqual(psbt.extractTransaction().locktime, 0) + psbt.setLocktime(1) + assert.strictEqual(psbt.extractTransaction().locktime, 1) + }) + }) }) From ba5f336e02785560166d3812908264189cca765c Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 4 Jul 2019 17:20:16 +0700 Subject: [PATCH 372/568] Test setSequence --- test/psbt.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/psbt.js b/test/psbt.js index d790b63..67f3611 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -198,4 +198,30 @@ describe(`Psbt`, () => { assert.strictEqual(psbt.extractTransaction().locktime, 1) }) }) + + describe('setSequence', () => { + it('Sets the sequence number for a given input', () => { + const psbt = new Psbt() + psbt.addInput({ + hash: '0000000000000000000000000000000000000000000000000000000000000000', + index: 0 + }); + + assert.strictEqual(psbt.__TX.ins[0].sequence, 0xffffffff) + psbt.setSequence(0, 0) + assert.strictEqual(psbt.__TX.ins[0].sequence, 0) + }) + + it('throws if input index is too high', () => { + const psbt = new Psbt() + psbt.addInput({ + hash: '0000000000000000000000000000000000000000000000000000000000000000', + index: 0 + }); + + assert.throws(() => { + psbt.setSequence(1, 0) + }, {message: 'Input index too high'}) + }) + }) }) From 14eeb309df25c59ebf01f05078036425c33b9a65 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 5 Jul 2019 12:28:04 +0900 Subject: [PATCH 373/568] Add fee checking before extract --- src/psbt.js | 204 ++++++++++++++++++++++++++++++++++++++---------- ts_src/psbt.ts | 156 +++++++++++++++++++++++++++++++++++- types/psbt.d.ts | 12 ++- 3 files changed, 324 insertions(+), 48 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index b2bbeda..272c716 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -11,6 +11,45 @@ const bscript = require('./script'); const transaction_1 = require('./transaction'); const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { + constructor(opts = {}) { + super(); + this.__NON_WITNESS_UTXO_TX_CACHE = []; + this.__NON_WITNESS_UTXO_BUF_CACHE = []; + // set defaults + this.opts = Object.assign({}, DEFAULT_OPTS, opts); + this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); + this.setVersion(2); + // set cache + const self = this; + delete this.globalMap.unsignedTx; + Object.defineProperty(this.globalMap, 'unsignedTx', { + enumerable: true, + get() { + if (self.__TX_BUF_CACHE !== undefined) { + return self.__TX_BUF_CACHE; + } else { + self.__TX_BUF_CACHE = self.__TX.toBuffer(); + return self.__TX_BUF_CACHE; + } + }, + set(data) { + self.__TX_BUF_CACHE = data; + }, + }); + // Make data hidden when enumerating + const dpew = (obj, attr, enumerable, writable) => + Object.defineProperty(obj, attr, { + enumerable, + writable, + }); + dpew(this, '__TX', false, true); + dpew(this, '__EXTRACTED_TX', false, true); + dpew(this, '__FEE_RATE', false, true); + dpew(this, '__TX_BUF_CACHE', false, true); + dpew(this, '__NON_WITNESS_UTXO_TX_CACHE', false, true); + dpew(this, '__NON_WITNESS_UTXO_BUF_CACHE', false, true); + dpew(this, 'opts', false, true); + } static fromTransaction(txBuf) { const tx = transaction_1.Transaction.fromBuffer(txBuf); checkTxEmpty(tx); @@ -46,44 +85,16 @@ class Psbt extends bip174_1.Psbt { psbt.__TX = tx; return psbt; } - constructor(opts = {}) { - super(); - // set defaults - this.opts = Object.assign({}, DEFAULT_OPTS, opts); - this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); - this.setVersion(2); - // set cache - const self = this; - delete this.globalMap.unsignedTx; - Object.defineProperty(this.globalMap, 'unsignedTx', { - enumerable: true, - get() { - if (self.__TX_BUF_CACHE !== undefined) { - return self.__TX_BUF_CACHE; - } else { - self.__TX_BUF_CACHE = self.__TX.toBuffer(); - return self.__TX_BUF_CACHE; - } - }, - set(data) { - self.__TX_BUF_CACHE = data; - }, - }); - // Make data hidden when enumerating - const dpew = (obj, attr, enumerable, writable) => - Object.defineProperty(obj, attr, { - enumerable, - writable, - }); - dpew(this, '__TX', false, true); - dpew(this, '__TX_BUF_CACHE', false, true); - dpew(this, 'opts', false, true); + setMaximumFeeRate(satoshiPerByte) { + check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw + this.opts.maximumFeeRate = satoshiPerByte; } setVersion(version) { check32Bit(version); checkInputsForPartialSig(this.inputs, 'setVersion'); this.__TX.version = version; this.__TX_BUF_CACHE = undefined; + this.__EXTRACTED_TX = undefined; return this; } setLocktime(locktime) { @@ -91,6 +102,7 @@ class Psbt extends bip174_1.Psbt { checkInputsForPartialSig(this.inputs, 'setLocktime'); this.__TX.locktime = locktime; this.__TX_BUF_CACHE = undefined; + this.__EXTRACTED_TX = undefined; return this; } setSequence(inputIndex, sequence) { @@ -101,6 +113,7 @@ class Psbt extends bip174_1.Psbt { } this.__TX.ins[inputIndex].sequence = sequence; this.__TX_BUF_CACHE = undefined; + this.__EXTRACTED_TX = undefined; return this; } addInput(inputData) { @@ -159,8 +172,29 @@ class Psbt extends bip174_1.Psbt { }; return super.addOutput(outputData, true, outputAdder); } - extractTransaction() { + addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) { + super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); + const input = this.inputs[inputIndex]; + addNonWitnessTxCache(this, input, inputIndex); + return this; + } + extractTransaction(disableFeeCheck) { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + if (!disableFeeCheck) { + const feeRate = this.__FEE_RATE || this.getFeeRate(); + const vsize = this.__EXTRACTED_TX.virtualSize(); + const satoshis = feeRate * vsize; + if (feeRate >= this.opts.maximumFeeRate) { + throw new Error( + `Warning: You are paying around ${satoshis / 1e8} in fees, which ` + + `is ${feeRate} satoshi per byte for a transaction with a VSize of ` + + `${vsize} bytes (segwit counted as 0.25 byte per byte)\n` + + `Use setMaximumFeeRate method to raise your threshold, or pass ` + + `true to the first arg of extractTransaction.`, + ); + } + } + if (this.__EXTRACTED_TX) return this.__EXTRACTED_TX; const tx = this.__TX.clone(); this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; @@ -170,8 +204,51 @@ class Psbt extends bip174_1.Psbt { ); } }); + this.__EXTRACTED_TX = tx; return tx; } + getFeeRate() { + if (!this.inputs.every(isFinalized)) + throw new Error('PSBT must be finalized to calculate fee rate'); + if (this.__FEE_RATE) return this.__FEE_RATE; + let tx; + let inputAmount = 0; + let mustFinalize = true; + if (this.__EXTRACTED_TX) { + tx = this.__EXTRACTED_TX; + mustFinalize = false; + } else { + tx = this.__TX.clone(); + } + this.inputs.forEach((input, idx) => { + if (mustFinalize && input.finalScriptSig) + tx.ins[idx].script = input.finalScriptSig; + if (mustFinalize && input.finalScriptWitness) { + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); + } + if (input.witnessUtxo) { + inputAmount += input.witnessUtxo.value; + } else if (input.nonWitnessUtxo) { + // @ts-ignore + if (!this.__NON_WITNESS_UTXO_TX_CACHE[idx]) { + addNonWitnessTxCache(this, input, idx); + } + const vout = this.__TX.ins[idx].index; + const out = this.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout]; + inputAmount += out.value; + } else { + throw new Error('Missing input value: index #' + idx); + } + }); + this.__EXTRACTED_TX = tx; + const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0); + const fee = inputAmount - outputAmount; + const bytes = tx.virtualSize(); + this.__FEE_RATE = Math.floor(fee / bytes); + return this.__FEE_RATE; + } finalizeAllInputs() { const inputResults = range(this.inputs.length).map(idx => this.finalizeInput(idx), @@ -188,6 +265,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, input, this.__TX, + this, ); if (!script) return false; const scriptType = classifyScript(script); @@ -216,6 +294,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, keyPair.publicKey, this.__TX, + this, ); const partialSig = { pubkey: keyPair.publicKey, @@ -232,6 +311,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, keyPair.publicKey, this.__TX, + this, ); Promise.resolve(keyPair.sign(hash)).then(signature => { const partialSig = { @@ -247,16 +327,50 @@ class Psbt extends bip174_1.Psbt { exports.Psbt = Psbt; const DEFAULT_OPTS = { network: networks_1.bitcoin, + maximumFeeRate: 5000, }; +function addNonWitnessTxCache(psbt, input, inputIndex) { + // @ts-ignore + psbt.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo; + const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo); + // @ts-ignore + psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; + const self = psbt; + const selfIndex = inputIndex; + delete input.nonWitnessUtxo; + Object.defineProperty(input, 'nonWitnessUtxo', { + enumerable: true, + get() { + // @ts-ignore + if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) { + // @ts-ignore + return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + } else { + // @ts-ignore + self.__NON_WITNESS_UTXO_BUF_CACHE[ + selfIndex + // @ts-ignore + ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer(); + // @ts-ignore + return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + } + }, + set(data) { + // @ts-ignore + self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data; + }, + }); +} function isFinalized(input) { return !!input.finalScriptSig || !!input.finalScriptWitness; } -function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx) { +function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, psbt) { const input = utils_1.checkForInput(inputs, inputIndex); const { hash, sighashType, script } = getHashForSig( inputIndex, input, unsignedTx, + psbt, ); checkScriptForPubkey(pubkey, script); return { @@ -375,15 +489,18 @@ function checkScriptForPubkey(pubkey, script) { ); } } -const getHashForSig = (inputIndex, input, unsignedTx) => { +const getHashForSig = (inputIndex, input, unsignedTx, psbt) => { const sighashType = input.sighashType || transaction_1.Transaction.SIGHASH_ALL; let hash; let script; if (input.nonWitnessUtxo) { - const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( - input.nonWitnessUtxo, - ); + // @ts-ignore + if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(psbt, input, inputIndex); + } + // @ts-ignore + const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; const prevoutHash = unsignedTx.ins[inputIndex].hash; const utxoHash = nonWitnessUtxoTx.getHash(); // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout @@ -494,7 +611,7 @@ const classifyScript = script => { if (isP2PK(script)) return 'pubkey'; return 'nonstandard'; }; -function getScriptFromInput(inputIndex, input, unsignedTx) { +function getScriptFromInput(inputIndex, input, unsignedTx, psbt) { const res = { script: null, isSegwit: false, @@ -506,9 +623,12 @@ function getScriptFromInput(inputIndex, input, unsignedTx) { res.isP2SH = true; res.script = input.redeemScript; } else { - const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( - input.nonWitnessUtxo, - ); + // @ts-ignore + if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(psbt, input, inputIndex); + } + // @ts-ignore + const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index fea9a67..bd6aeb4 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,5 +1,6 @@ import { Psbt as PsbtBase } from 'bip174'; import { + NonWitnessUtxo, PartialSig, PsbtInput, TransactionInput, @@ -13,7 +14,7 @@ import { Signer, SignerAsync } from './ecpair'; import { bitcoin as btcNetwork, Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; -import { Transaction } from './transaction'; +import { Output, Transaction } from './transaction'; const varuint = require('varuint-bitcoin'); export class Psbt extends PsbtBase { @@ -65,6 +66,10 @@ export class Psbt extends PsbtBase { } private __TX: Transaction; private __TX_BUF_CACHE?: Buffer; + private __FEE_RATE?: number; + private __EXTRACTED_TX?: Transaction; + private __NON_WITNESS_UTXO_TX_CACHE: Transaction[] = []; + private __NON_WITNESS_UTXO_BUF_CACHE: Buffer[] = []; private opts: PsbtOpts; constructor(opts: PsbtOptsOptional = {}) { super(); @@ -103,15 +108,25 @@ export class Psbt extends PsbtBase { writable, }); dpew(this, '__TX', false, true); + dpew(this, '__EXTRACTED_TX', false, true); + dpew(this, '__FEE_RATE', false, true); dpew(this, '__TX_BUF_CACHE', false, true); + dpew(this, '__NON_WITNESS_UTXO_TX_CACHE', false, true); + dpew(this, '__NON_WITNESS_UTXO_BUF_CACHE', false, true); dpew(this, 'opts', false, true); } + setMaximumFeeRate(satoshiPerByte: number): void { + check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw + this.opts.maximumFeeRate = satoshiPerByte; + } + setVersion(version: number): this { check32Bit(version); checkInputsForPartialSig(this.inputs, 'setVersion'); this.__TX.version = version; this.__TX_BUF_CACHE = undefined; + this.__EXTRACTED_TX = undefined; return this; } @@ -120,6 +135,7 @@ export class Psbt extends PsbtBase { checkInputsForPartialSig(this.inputs, 'setLocktime'); this.__TX.locktime = locktime; this.__TX_BUF_CACHE = undefined; + this.__EXTRACTED_TX = undefined; return this; } @@ -131,6 +147,7 @@ export class Psbt extends PsbtBase { } this.__TX.ins[inputIndex].sequence = sequence; this.__TX_BUF_CACHE = undefined; + this.__EXTRACTED_TX = undefined; return this; } @@ -197,8 +214,33 @@ export class Psbt extends PsbtBase { return super.addOutput(outputData, true, outputAdder); } - extractTransaction(): Transaction { + addNonWitnessUtxoToInput( + inputIndex: number, + nonWitnessUtxo: NonWitnessUtxo, + ): this { + super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); + const input = this.inputs[inputIndex]; + addNonWitnessTxCache(this, input, inputIndex); + return this; + } + + extractTransaction(disableFeeCheck?: boolean): Transaction { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + if (!disableFeeCheck) { + const feeRate = this.__FEE_RATE || this.getFeeRate(); + const vsize = this.__EXTRACTED_TX!.virtualSize(); + const satoshis = feeRate * vsize; + if (feeRate >= this.opts.maximumFeeRate) { + throw new Error( + `Warning: You are paying around ${satoshis / 1e8} in fees, which ` + + `is ${feeRate} satoshi per byte for a transaction with a VSize of ` + + `${vsize} bytes (segwit counted as 0.25 byte per byte)\n` + + `Use setMaximumFeeRate method to raise your threshold, or pass ` + + `true to the first arg of extractTransaction.`, + ); + } + } + if (this.__EXTRACTED_TX) return this.__EXTRACTED_TX; const tx = this.__TX.clone(); this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; @@ -208,9 +250,56 @@ export class Psbt extends PsbtBase { ); } }); + this.__EXTRACTED_TX = tx; return tx; } + getFeeRate(): number { + if (!this.inputs.every(isFinalized)) + throw new Error('PSBT must be finalized to calculate fee rate'); + if (this.__FEE_RATE) return this.__FEE_RATE; + let tx: Transaction; + let inputAmount = 0; + let mustFinalize = true; + if (this.__EXTRACTED_TX) { + tx = this.__EXTRACTED_TX; + mustFinalize = false; + } else { + tx = this.__TX.clone(); + } + this.inputs.forEach((input, idx) => { + if (mustFinalize && input.finalScriptSig) + tx.ins[idx].script = input.finalScriptSig; + if (mustFinalize && input.finalScriptWitness) { + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); + } + if (input.witnessUtxo) { + inputAmount += input.witnessUtxo.value; + } else if (input.nonWitnessUtxo) { + // @ts-ignore + if (!this.__NON_WITNESS_UTXO_TX_CACHE[idx]) { + addNonWitnessTxCache(this, input, idx); + } + const vout = this.__TX.ins[idx].index; + const out = this.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output; + inputAmount += out.value; + } else { + throw new Error('Missing input value: index #' + idx); + } + }); + this.__EXTRACTED_TX = tx; + const outputAmount = (tx.outs as Output[]).reduce( + (total, o) => total + o.value, + 0, + ); + const fee = inputAmount - outputAmount; + const bytes = tx.virtualSize(); + this.__FEE_RATE = Math.floor(fee / bytes); + return this.__FEE_RATE; + } + finalizeAllInputs(): { result: boolean; inputResults: boolean[]; @@ -231,6 +320,7 @@ export class Psbt extends PsbtBase { inputIndex, input, this.__TX, + this, ); if (!script) return false; @@ -264,6 +354,7 @@ export class Psbt extends PsbtBase { inputIndex, keyPair.publicKey, this.__TX, + this, ); const partialSig = { @@ -284,6 +375,7 @@ export class Psbt extends PsbtBase { inputIndex, keyPair.publicKey, this.__TX, + this, ); Promise.resolve(keyPair.sign(hash)).then(signature => { @@ -312,16 +404,58 @@ export class Psbt extends PsbtBase { interface PsbtOptsOptional { network?: Network; + maximumFeeRate?: number; } interface PsbtOpts { network: Network; + maximumFeeRate: number; } const DEFAULT_OPTS = { network: btcNetwork, + maximumFeeRate: 5000, // satoshi per byte }; +function addNonWitnessTxCache( + psbt: Psbt, + input: PsbtInput, + inputIndex: number, +): void { + // @ts-ignore + psbt.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!; + + const tx = Transaction.fromBuffer(input.nonWitnessUtxo!); + // @ts-ignore + psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; + + const self = psbt; + const selfIndex = inputIndex; + delete input.nonWitnessUtxo; + Object.defineProperty(input, 'nonWitnessUtxo', { + enumerable: true, + get(): Buffer { + // @ts-ignore + if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) { + // @ts-ignore + return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + } else { + // @ts-ignore + self.__NON_WITNESS_UTXO_BUF_CACHE[ + selfIndex + // @ts-ignore + ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer(); + // @ts-ignore + return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + } + }, + set(data: Buffer): void { + // @ts-ignore + self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data; + }, + }); +} + function isFinalized(input: PsbtInput): boolean { return !!input.finalScriptSig || !!input.finalScriptWitness; } @@ -331,6 +465,7 @@ function getHashAndSighashType( inputIndex: number, pubkey: Buffer, unsignedTx: Transaction, + psbt: Psbt, ): { hash: Buffer; sighashType: number; @@ -340,6 +475,7 @@ function getHashAndSighashType( inputIndex, input, unsignedTx, + psbt, ); checkScriptForPubkey(pubkey, script); return { @@ -490,13 +626,19 @@ const getHashForSig = ( inputIndex: number, input: PsbtInput, unsignedTx: Transaction, + psbt: Psbt, ): HashForSigData => { const sighashType = input.sighashType || Transaction.SIGHASH_ALL; let hash: Buffer; let script: Buffer; if (input.nonWitnessUtxo) { - const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); + // @ts-ignore + if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(psbt, input, inputIndex); + } + // @ts-ignore + const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; const prevoutHash = unsignedTx.ins[inputIndex].hash; const utxoHash = nonWitnessUtxoTx.getHash(); @@ -636,6 +778,7 @@ function getScriptFromInput( inputIndex: number, input: PsbtInput, unsignedTx: Transaction, + psbt: Psbt, ): GetScriptReturn { const res: GetScriptReturn = { script: null, @@ -648,7 +791,12 @@ function getScriptFromInput( res.isP2SH = true; res.script = input.redeemScript; } else { - const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); + // @ts-ignore + if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(psbt, input, inputIndex); + } + // @ts-ignore + const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; } diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 1aa6f28..af234d8 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,6 +1,6 @@ /// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; -import { TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; +import { NonWitnessUtxo, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; @@ -9,14 +9,21 @@ export declare class Psbt extends PsbtBase { static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>; private __TX; private __TX_BUF_CACHE?; + private __FEE_RATE?; + private __EXTRACTED_TX?; + private __NON_WITNESS_UTXO_TX_CACHE; + private __NON_WITNESS_UTXO_BUF_CACHE; private opts; constructor(opts?: PsbtOptsOptional); + setMaximumFeeRate(satoshiPerByte: number): void; setVersion(version: number): this; setLocktime(locktime: number): this; setSequence(inputIndex: number, sequence: number): this; addInput(inputData: TransactionInput): this; addOutput(outputData: TransactionOutput): this; - extractTransaction(): Transaction; + addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this; + extractTransaction(disableFeeCheck?: boolean): Transaction; + getFeeRate(): number; finalizeAllInputs(): { result: boolean; inputResults: boolean[]; @@ -27,5 +34,6 @@ export declare class Psbt extends PsbtBase { } interface PsbtOptsOptional { network?: Network; + maximumFeeRate?: number; } export {}; From 51133c8051458b9d9089aa0374be6fb20999b40d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 5 Jul 2019 12:51:13 +0900 Subject: [PATCH 374/568] Add type instance check tests --- src/psbt.js | 10 ++++++++-- test/psbt.js | 27 ++++++++++++++++++++++++++- ts_src/psbt.ts | 10 ++++++++-- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 272c716..ba42917 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -143,7 +143,10 @@ class Psbt extends bip174_1.Psbt { }); return self.__TX.toBuffer(); }; - return super.addInput(inputData, inputAdder); + super.addInput(inputData, inputAdder); + this.__FEE_RATE = undefined; + this.__EXTRACTED_TX = undefined; + return this; } addOutput(outputData) { checkInputsForPartialSig(this.inputs, 'addOutput'); @@ -170,7 +173,10 @@ class Psbt extends bip174_1.Psbt { }); return self.__TX.toBuffer(); }; - return super.addOutput(outputData, true, outputAdder); + super.addOutput(outputData, true, outputAdder); + this.__FEE_RATE = undefined; + this.__EXTRACTED_TX = undefined; + return this; } addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) { super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); diff --git a/test/psbt.js b/test/psbt.js index 67f3611..d65872c 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -158,7 +158,7 @@ describe(`Psbt`, () => { ECPair.fromWIF(f.shouldSign.WIF), ) }) - + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) assert.throws(() => { psbtThatShouldThrow.signInput( @@ -224,4 +224,29 @@ describe(`Psbt`, () => { }, {message: 'Input index too high'}) }) }) + + describe('Method return types', () => { + it('fromTransaction returns Psbt type (not base class)', () => { + const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0])); + assert.strictEqual(psbt instanceof Psbt, true); + assert.ok(psbt.__TX); + }) + it('fromBuffer returns Psbt type (not base class)', () => { + const psbt = Psbt.fromBuffer(Buffer.from( + '70736274ff01000a01000000000000000000000000', 'hex' //cHNidP8BAAoBAAAAAAAAAAAAAAAA + )); + assert.strictEqual(psbt instanceof Psbt, true); + assert.ok(psbt.__TX); + }) + it('fromBase64 returns Psbt type (not base class)', () => { + const psbt = Psbt.fromBase64('cHNidP8BAAoBAAAAAAAAAAAAAAAA'); + assert.strictEqual(psbt instanceof Psbt, true); + assert.ok(psbt.__TX); + }) + it('fromHex returns Psbt type (not base class)', () => { + const psbt = Psbt.fromHex('70736274ff01000a01000000000000000000000000'); + assert.strictEqual(psbt instanceof Psbt, true); + assert.ok(psbt.__TX); + }) + }) }) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index bd6aeb4..73ffe33 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -180,7 +180,10 @@ export class Psbt extends PsbtBase { }); return self.__TX.toBuffer(); }; - return super.addInput(inputData, inputAdder); + super.addInput(inputData, inputAdder); + this.__FEE_RATE = undefined; + this.__EXTRACTED_TX = undefined; + return this; } addOutput(outputData: TransactionOutput): this { @@ -211,7 +214,10 @@ export class Psbt extends PsbtBase { }); return self.__TX.toBuffer(); }; - return super.addOutput(outputData, true, outputAdder); + super.addOutput(outputData, true, outputAdder); + this.__FEE_RATE = undefined; + this.__EXTRACTED_TX = undefined; + return this; } addNonWitnessUtxoToInput( From 93e1661c6c7c1557203e6ff3e379b8f128fd0ea6 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 5 Jul 2019 14:30:08 +0900 Subject: [PATCH 375/568] Remove need for ts-ignore --- src/psbt.js | 65 +++++++++++++++++++-------------------------- ts_src/psbt.ts | 70 ++++++++++++++++++++++--------------------------- types/psbt.d.ts | 3 +-- 3 files changed, 60 insertions(+), 78 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index ba42917..aa2adf2 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -13,8 +13,10 @@ const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { constructor(opts = {}) { super(); - this.__NON_WITNESS_UTXO_TX_CACHE = []; - this.__NON_WITNESS_UTXO_BUF_CACHE = []; + this.__NON_WITNESS_UTXO_CACHE = { + __NON_WITNESS_UTXO_TX_CACHE: [], + __NON_WITNESS_UTXO_BUF_CACHE: [], + }; // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); @@ -46,8 +48,7 @@ class Psbt extends bip174_1.Psbt { dpew(this, '__EXTRACTED_TX', false, true); dpew(this, '__FEE_RATE', false, true); dpew(this, '__TX_BUF_CACHE', false, true); - dpew(this, '__NON_WITNESS_UTXO_TX_CACHE', false, true); - dpew(this, '__NON_WITNESS_UTXO_BUF_CACHE', false, true); + dpew(this, '__NON_WITNESS_UTXO_CACHE', false, true); dpew(this, 'opts', false, true); } static fromTransaction(txBuf) { @@ -181,7 +182,7 @@ class Psbt extends bip174_1.Psbt { addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) { super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); const input = this.inputs[inputIndex]; - addNonWitnessTxCache(this, input, inputIndex); + addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, inputIndex); return this; } extractTransaction(disableFeeCheck) { @@ -237,12 +238,12 @@ class Psbt extends bip174_1.Psbt { if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; } else if (input.nonWitnessUtxo) { - // @ts-ignore - if (!this.__NON_WITNESS_UTXO_TX_CACHE[idx]) { - addNonWitnessTxCache(this, input, idx); + const cache = this.__NON_WITNESS_UTXO_CACHE; + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) { + addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, idx); } const vout = this.__TX.ins[idx].index; - const out = this.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout]; + const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout]; inputAmount += out.value; } else { throw new Error('Missing input value: index #' + idx); @@ -271,7 +272,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, input, this.__TX, - this, + this.__NON_WITNESS_UTXO_CACHE, ); if (!script) return false; const scriptType = classifyScript(script); @@ -300,7 +301,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, keyPair.publicKey, this.__TX, - this, + this.__NON_WITNESS_UTXO_CACHE, ); const partialSig = { pubkey: keyPair.publicKey, @@ -317,7 +318,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, keyPair.publicKey, this.__TX, - this, + this.__NON_WITNESS_UTXO_CACHE, ); Promise.resolve(keyPair.sign(hash)).then(signature => { const partialSig = { @@ -335,34 +336,26 @@ const DEFAULT_OPTS = { network: networks_1.bitcoin, maximumFeeRate: 5000, }; -function addNonWitnessTxCache(psbt, input, inputIndex) { - // @ts-ignore - psbt.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo; +function addNonWitnessTxCache(cache, input, inputIndex) { + cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo; const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo); - // @ts-ignore - psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; - const self = psbt; + cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; + const self = cache; const selfIndex = inputIndex; delete input.nonWitnessUtxo; Object.defineProperty(input, 'nonWitnessUtxo', { enumerable: true, get() { - // @ts-ignore if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) { - // @ts-ignore return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; } else { - // @ts-ignore self.__NON_WITNESS_UTXO_BUF_CACHE[ selfIndex - // @ts-ignore ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer(); - // @ts-ignore return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; } }, set(data) { - // @ts-ignore self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data; }, }); @@ -370,13 +363,13 @@ function addNonWitnessTxCache(psbt, input, inputIndex) { function isFinalized(input) { return !!input.finalScriptSig || !!input.finalScriptWitness; } -function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, psbt) { +function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, cache) { const input = utils_1.checkForInput(inputs, inputIndex); const { hash, sighashType, script } = getHashForSig( inputIndex, input, unsignedTx, - psbt, + cache, ); checkScriptForPubkey(pubkey, script); return { @@ -495,18 +488,16 @@ function checkScriptForPubkey(pubkey, script) { ); } } -const getHashForSig = (inputIndex, input, unsignedTx, psbt) => { +const getHashForSig = (inputIndex, input, unsignedTx, cache) => { const sighashType = input.sighashType || transaction_1.Transaction.SIGHASH_ALL; let hash; let script; if (input.nonWitnessUtxo) { - // @ts-ignore - if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(psbt, input, inputIndex); + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(cache, input, inputIndex); } - // @ts-ignore - const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; const prevoutHash = unsignedTx.ins[inputIndex].hash; const utxoHash = nonWitnessUtxoTx.getHash(); // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout @@ -617,7 +608,7 @@ const classifyScript = script => { if (isP2PK(script)) return 'pubkey'; return 'nonstandard'; }; -function getScriptFromInput(inputIndex, input, unsignedTx, psbt) { +function getScriptFromInput(inputIndex, input, unsignedTx, cache) { const res = { script: null, isSegwit: false, @@ -629,12 +620,10 @@ function getScriptFromInput(inputIndex, input, unsignedTx, psbt) { res.isP2SH = true; res.script = input.redeemScript; } else { - // @ts-ignore - if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(psbt, input, inputIndex); + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(cache, input, inputIndex); } - // @ts-ignore - const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 73ffe33..e6c96f2 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -64,12 +64,14 @@ export class Psbt extends PsbtBase { psbt.__TX = tx!; return psbt as InstanceType<T>; } + private __NON_WITNESS_UTXO_CACHE = { + __NON_WITNESS_UTXO_TX_CACHE: [] as Transaction[], + __NON_WITNESS_UTXO_BUF_CACHE: [] as Buffer[], + }; private __TX: Transaction; private __TX_BUF_CACHE?: Buffer; private __FEE_RATE?: number; private __EXTRACTED_TX?: Transaction; - private __NON_WITNESS_UTXO_TX_CACHE: Transaction[] = []; - private __NON_WITNESS_UTXO_BUF_CACHE: Buffer[] = []; private opts: PsbtOpts; constructor(opts: PsbtOptsOptional = {}) { super(); @@ -111,8 +113,7 @@ export class Psbt extends PsbtBase { dpew(this, '__EXTRACTED_TX', false, true); dpew(this, '__FEE_RATE', false, true); dpew(this, '__TX_BUF_CACHE', false, true); - dpew(this, '__NON_WITNESS_UTXO_TX_CACHE', false, true); - dpew(this, '__NON_WITNESS_UTXO_BUF_CACHE', false, true); + dpew(this, '__NON_WITNESS_UTXO_CACHE', false, true); dpew(this, 'opts', false, true); } @@ -226,7 +227,7 @@ export class Psbt extends PsbtBase { ): this { super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); const input = this.inputs[inputIndex]; - addNonWitnessTxCache(this, input, inputIndex); + addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, inputIndex); return this; } @@ -284,12 +285,12 @@ export class Psbt extends PsbtBase { if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; } else if (input.nonWitnessUtxo) { - // @ts-ignore - if (!this.__NON_WITNESS_UTXO_TX_CACHE[idx]) { - addNonWitnessTxCache(this, input, idx); + const cache = this.__NON_WITNESS_UTXO_CACHE; + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) { + addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, idx); } const vout = this.__TX.ins[idx].index; - const out = this.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output; + const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output; inputAmount += out.value; } else { throw new Error('Missing input value: index #' + idx); @@ -326,7 +327,7 @@ export class Psbt extends PsbtBase { inputIndex, input, this.__TX, - this, + this.__NON_WITNESS_UTXO_CACHE, ); if (!script) return false; @@ -360,7 +361,7 @@ export class Psbt extends PsbtBase { inputIndex, keyPair.publicKey, this.__TX, - this, + this.__NON_WITNESS_UTXO_CACHE, ); const partialSig = { @@ -381,7 +382,7 @@ export class Psbt extends PsbtBase { inputIndex, keyPair.publicKey, this.__TX, - this, + this.__NON_WITNESS_UTXO_CACHE, ); Promise.resolve(keyPair.sign(hash)).then(signature => { @@ -408,6 +409,11 @@ export class Psbt extends PsbtBase { // // +interface NonWitnessUtxoCache { + __NON_WITNESS_UTXO_TX_CACHE: Transaction[]; + __NON_WITNESS_UTXO_BUF_CACHE: Buffer[]; +} + interface PsbtOptsOptional { network?: Network; maximumFeeRate?: number; @@ -424,39 +430,31 @@ const DEFAULT_OPTS = { }; function addNonWitnessTxCache( - psbt: Psbt, + cache: NonWitnessUtxoCache, input: PsbtInput, inputIndex: number, ): void { - // @ts-ignore - psbt.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!; + cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!; const tx = Transaction.fromBuffer(input.nonWitnessUtxo!); - // @ts-ignore - psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; + cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; - const self = psbt; + const self = cache; const selfIndex = inputIndex; delete input.nonWitnessUtxo; Object.defineProperty(input, 'nonWitnessUtxo', { enumerable: true, get(): Buffer { - // @ts-ignore if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) { - // @ts-ignore return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; } else { - // @ts-ignore self.__NON_WITNESS_UTXO_BUF_CACHE[ selfIndex - // @ts-ignore ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer(); - // @ts-ignore return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; } }, set(data: Buffer): void { - // @ts-ignore self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data; }, }); @@ -471,7 +469,7 @@ function getHashAndSighashType( inputIndex: number, pubkey: Buffer, unsignedTx: Transaction, - psbt: Psbt, + cache: NonWitnessUtxoCache, ): { hash: Buffer; sighashType: number; @@ -481,7 +479,7 @@ function getHashAndSighashType( inputIndex, input, unsignedTx, - psbt, + cache, ); checkScriptForPubkey(pubkey, script); return { @@ -632,19 +630,17 @@ const getHashForSig = ( inputIndex: number, input: PsbtInput, unsignedTx: Transaction, - psbt: Psbt, + cache: NonWitnessUtxoCache, ): HashForSigData => { const sighashType = input.sighashType || Transaction.SIGHASH_ALL; let hash: Buffer; let script: Buffer; if (input.nonWitnessUtxo) { - // @ts-ignore - if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(psbt, input, inputIndex); + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(cache, input, inputIndex); } - // @ts-ignore - const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; const prevoutHash = unsignedTx.ins[inputIndex].hash; const utxoHash = nonWitnessUtxoTx.getHash(); @@ -784,7 +780,7 @@ function getScriptFromInput( inputIndex: number, input: PsbtInput, unsignedTx: Transaction, - psbt: Psbt, + cache: NonWitnessUtxoCache, ): GetScriptReturn { const res: GetScriptReturn = { script: null, @@ -797,12 +793,10 @@ function getScriptFromInput( res.isP2SH = true; res.script = input.redeemScript; } else { - // @ts-ignore - if (!psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(psbt, input, inputIndex); + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(cache, input, inputIndex); } - // @ts-ignore - const nonWitnessUtxoTx = psbt.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; } diff --git a/types/psbt.d.ts b/types/psbt.d.ts index af234d8..fae4fff 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -7,12 +7,11 @@ import { Transaction } from './transaction'; export declare class Psbt extends PsbtBase { static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>; static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>; + private __NON_WITNESS_UTXO_CACHE; private __TX; private __TX_BUF_CACHE?; private __FEE_RATE?; private __EXTRACTED_TX?; - private __NON_WITNESS_UTXO_TX_CACHE; - private __NON_WITNESS_UTXO_BUF_CACHE; private opts; constructor(opts?: PsbtOptsOptional); setMaximumFeeRate(satoshiPerByte: number): void; From 8d52ce1668bc1ee73d56cdfc4bbf2b5e23fa653c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 5 Jul 2019 16:42:13 +0900 Subject: [PATCH 376/568] Add some tests and an input duplicate checker --- src/psbt.js | 54 ++++++++++++------ test/fixtures/psbt.json | 31 +++++++++++ test/psbt.js | 118 ++++++++++++++++++++++++++++++++++++++-- ts_src/psbt.ts | 58 +++++++++++++++----- types/psbt.d.ts | 3 +- 5 files changed, 228 insertions(+), 36 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index aa2adf2..e797949 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -13,9 +13,10 @@ const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { constructor(opts = {}) { super(); - this.__NON_WITNESS_UTXO_CACHE = { + this.__CACHE = { __NON_WITNESS_UTXO_TX_CACHE: [], __NON_WITNESS_UTXO_BUF_CACHE: [], + __TX_IN_CACHE: {}, }; // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); @@ -48,7 +49,7 @@ class Psbt extends bip174_1.Psbt { dpew(this, '__EXTRACTED_TX', false, true); dpew(this, '__FEE_RATE', false, true); dpew(this, '__TX_BUF_CACHE', false, true); - dpew(this, '__NON_WITNESS_UTXO_CACHE', false, true); + dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } static fromTransaction(txBuf) { @@ -56,6 +57,7 @@ class Psbt extends bip174_1.Psbt { checkTxEmpty(tx); const psbt = new this(); psbt.__TX = tx; + checkTxForDupeIns(tx, psbt.__CACHE); let inputCount = tx.ins.length; let outputCount = tx.outs.length; while (inputCount > 0) { @@ -84,8 +86,12 @@ class Psbt extends bip174_1.Psbt { }; const psbt = super.fromBuffer(buffer, txCountGetter); psbt.__TX = tx; + checkTxForDupeIns(tx, psbt.__CACHE); return psbt; } + get inputCount() { + return this.inputs.length; + } setMaximumFeeRate(satoshiPerByte) { check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw this.opts.maximumFeeRate = satoshiPerByte; @@ -134,14 +140,17 @@ class Psbt extends bip174_1.Psbt { const prevHash = Buffer.isBuffer(_inputData.hash) ? _inputData.hash : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex')); - self.__TX.ins.push({ - hash: prevHash, - index: _inputData.index, - script: Buffer.alloc(0), - sequence: - _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE, - witness: [], - }); + // Check if input already exists in cache. + const input = { hash: prevHash, index: _inputData.index }; + checkTxInputCache(self.__CACHE, input); + self.__TX.ins.push( + Object.assign({}, input, { + script: Buffer.alloc(0), + sequence: + _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE, + witness: [], + }), + ); return self.__TX.toBuffer(); }; super.addInput(inputData, inputAdder); @@ -182,7 +191,7 @@ class Psbt extends bip174_1.Psbt { addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) { super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); const input = this.inputs[inputIndex]; - addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, inputIndex); + addNonWitnessTxCache(this.__CACHE, input, inputIndex); return this; } extractTransaction(disableFeeCheck) { @@ -238,9 +247,9 @@ class Psbt extends bip174_1.Psbt { if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; } else if (input.nonWitnessUtxo) { - const cache = this.__NON_WITNESS_UTXO_CACHE; + const cache = this.__CACHE; if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) { - addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, idx); + addNonWitnessTxCache(this.__CACHE, input, idx); } const vout = this.__TX.ins[idx].index; const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout]; @@ -272,7 +281,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, input, this.__TX, - this.__NON_WITNESS_UTXO_CACHE, + this.__CACHE, ); if (!script) return false; const scriptType = classifyScript(script); @@ -301,7 +310,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, keyPair.publicKey, this.__TX, - this.__NON_WITNESS_UTXO_CACHE, + this.__CACHE, ); const partialSig = { pubkey: keyPair.publicKey, @@ -318,7 +327,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, keyPair.publicKey, this.__TX, - this.__NON_WITNESS_UTXO_CACHE, + this.__CACHE, ); Promise.resolve(keyPair.sign(hash)).then(signature => { const partialSig = { @@ -360,6 +369,19 @@ function addNonWitnessTxCache(cache, input, inputIndex) { }, }); } +function checkTxForDupeIns(tx, cache) { + tx.ins.forEach(input => { + checkTxInputCache(cache, input); + }); +} +function checkTxInputCache(cache, input) { + const key = + bufferutils_1.reverseBuffer(Buffer.from(input.hash)).toString('hex') + + ':' + + input.index; + if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.'); + cache.__TX_IN_CACHE[key] = 1; +} function isFinalized(input) { return !!input.finalScriptSig || !!input.finalScriptWitness; } diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 00877f9..59a74cd 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -304,6 +304,37 @@ } ] }, + "addInput": { + "checks": [ + { + "description": "checks for hash and index", + "inputData": { + "hash": 42 + }, + "exception": "Error adding input." + }, + { + "description": "checks for hash and index", + "inputData": { + "hash": "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", + "index": 2 + }, + "equals": "cHNidP8BADMCAAAAAQABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4PAgAAAAD/////AAAAAAAAAAA=" + } + ] + }, + "addOutput": { + "checks": [ + { + "description": "checks for hash and index", + "outputData": { + "address": "1P2NFEBp32V2arRwZNww6tgXEV58FG94mr", + "value": "xyz" + }, + "exception": "Error adding output." + } + ] + }, "signInput": { "checks": [ { diff --git a/test/psbt.js b/test/psbt.js index d65872c..9a3ac92 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -21,8 +21,16 @@ const initBuffers = (attr, data) => { } else if (attr === 'bip32Derivation') { data.masterFingerprint = b(data.masterFingerprint) data.pubkey = b(data.pubkey) - } else if (attr === 'witnessUtxo') { + } else if (attr === 'witnessUtxo') { data.script = b(data.script) + } else if (attr === 'hash') { + if ( + typeof data === 'string' && + data.match(/^[0-9a-f]*$/i) && + data.length % 2 === 0 + ) { + data = b(data) + } } return data @@ -140,11 +148,55 @@ describe(`Psbt`, () => { fixtures.bip174.extractor.forEach(f => { it('Extracts the expected transaction from a PSBT', () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt1 = Psbt.fromBase64(f.psbt) + const transaction1 = psbt1.extractTransaction(true).toHex() - const transaction = psbt.extractTransaction().toHex() + const psbt2 = Psbt.fromBase64(f.psbt) + const transaction2 = psbt2.extractTransaction().toHex() - assert.strictEqual(transaction, f.transaction) + assert.strictEqual(transaction1, transaction2) + assert.strictEqual(transaction1, f.transaction) + + const psbt3 = Psbt.fromBase64(f.psbt) + delete psbt3.inputs[0].finalScriptSig + delete psbt3.inputs[0].finalScriptWitness + assert.throws(() => { + psbt3.extractTransaction() + }, new RegExp('Not finalized')) + + const psbt4 = Psbt.fromBase64(f.psbt) + psbt4.setMaximumFeeRate(1) + assert.throws(() => { + psbt4.extractTransaction() + }, new RegExp('Warning: You are paying around [\\d.]+ in fees')) + + const psbt5 = Psbt.fromBase64(f.psbt) + psbt5.extractTransaction(true) + const fr1 = psbt5.getFeeRate() + const fr2 = psbt5.getFeeRate() + assert.strictEqual(fr1, fr2) + }) + }) + + describe('signInputAsync', () => { + fixtures.signInput.checks.forEach(f => { + it(f.description, async () => { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotReject(async () => { + await psbtThatShouldsign.signInputAsync( + f.shouldSign.inputToCheck, + ECPair.fromWIF(f.shouldSign.WIF), + ) + }) + + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.rejects(async () => { + await psbtThatShouldThrow.signInputAsync( + f.shouldThrow.inputToCheck, + ECPair.fromWIF(f.shouldThrow.WIF), + ) + }, {message: f.shouldThrow.errorMessage}) + }) }) }) @@ -179,6 +231,54 @@ describe(`Psbt`, () => { }) }) + describe('addInput', () => { + fixtures.addInput.checks.forEach(f => { + for (const attr of Object.keys(f.inputData)) { + f.inputData[attr] = initBuffers(attr, f.inputData[attr]) + } + it(f.description, () => { + const psbt = new Psbt() + + if (f.exception) { + assert.throws(() => { + psbt.addInput(f.inputData) + }, new RegExp(f.exception)) + } else { + assert.doesNotThrow(() => { + psbt.addInput(f.inputData) + if (f.equals) { + assert.strictEqual(psbt.toBase64(), f.equals) + } else { + console.log(psbt.toBase64()) + } + }) + } + }) + }) + }) + + describe('addOutput', () => { + fixtures.addOutput.checks.forEach(f => { + for (const attr of Object.keys(f.outputData)) { + f.outputData[attr] = initBuffers(attr, f.outputData[attr]) + } + it(f.description, () => { + const psbt = new Psbt() + + if (f.exception) { + assert.throws(() => { + psbt.addOutput(f.outputData) + }, new RegExp(f.exception)) + } else { + assert.doesNotThrow(() => { + psbt.addOutput(f.outputData) + console.log(psbt.toBase64()) + }) + } + }) + }) + }) + describe('setVersion', () => { it('Sets the version value of the unsigned transaction', () => { const psbt = new Psbt() @@ -225,6 +325,16 @@ describe(`Psbt`, () => { }) }) + describe('setMaximumFeeRate', () => { + it('Sets the maximumFeeRate value', () => { + const psbt = new Psbt() + + assert.strictEqual(psbt.opts.maximumFeeRate, 5000) + psbt.setMaximumFeeRate(6000) + assert.strictEqual(psbt.opts.maximumFeeRate, 6000) + }) + }) + describe('Method return types', () => { it('fromTransaction returns Psbt type (not base class)', () => { const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0])); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index e6c96f2..24a088e 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -26,6 +26,7 @@ export class Psbt extends PsbtBase { checkTxEmpty(tx); const psbt = new this() as Psbt; psbt.__TX = tx; + checkTxForDupeIns(tx, psbt.__CACHE); let inputCount = tx.ins.length; let outputCount = tx.outs.length; while (inputCount > 0) { @@ -62,11 +63,13 @@ export class Psbt extends PsbtBase { }; const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt; psbt.__TX = tx!; + checkTxForDupeIns(tx!, psbt.__CACHE); return psbt as InstanceType<T>; } - private __NON_WITNESS_UTXO_CACHE = { + private __CACHE = { __NON_WITNESS_UTXO_TX_CACHE: [] as Transaction[], __NON_WITNESS_UTXO_BUF_CACHE: [] as Buffer[], + __TX_IN_CACHE: {} as { [index: string]: number }, }; private __TX: Transaction; private __TX_BUF_CACHE?: Buffer; @@ -113,10 +116,14 @@ export class Psbt extends PsbtBase { dpew(this, '__EXTRACTED_TX', false, true); dpew(this, '__FEE_RATE', false, true); dpew(this, '__TX_BUF_CACHE', false, true); - dpew(this, '__NON_WITNESS_UTXO_CACHE', false, true); + dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } + get inputCount(): number { + return this.inputs.length; + } + setMaximumFeeRate(satoshiPerByte: number): void { check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw this.opts.maximumFeeRate = satoshiPerByte; @@ -172,9 +179,13 @@ export class Psbt extends PsbtBase { const prevHash = Buffer.isBuffer(_inputData.hash) ? _inputData.hash : reverseBuffer(Buffer.from(_inputData.hash, 'hex')); + + // Check if input already exists in cache. + const input = { hash: prevHash, index: _inputData.index }; + checkTxInputCache(self.__CACHE, input); + self.__TX.ins.push({ - hash: prevHash, - index: _inputData.index, + ...input, script: Buffer.alloc(0), sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE, witness: [], @@ -227,7 +238,7 @@ export class Psbt extends PsbtBase { ): this { super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); const input = this.inputs[inputIndex]; - addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, inputIndex); + addNonWitnessTxCache(this.__CACHE, input, inputIndex); return this; } @@ -285,9 +296,9 @@ export class Psbt extends PsbtBase { if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; } else if (input.nonWitnessUtxo) { - const cache = this.__NON_WITNESS_UTXO_CACHE; + const cache = this.__CACHE; if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) { - addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, idx); + addNonWitnessTxCache(this.__CACHE, input, idx); } const vout = this.__TX.ins[idx].index; const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output; @@ -327,7 +338,7 @@ export class Psbt extends PsbtBase { inputIndex, input, this.__TX, - this.__NON_WITNESS_UTXO_CACHE, + this.__CACHE, ); if (!script) return false; @@ -361,7 +372,7 @@ export class Psbt extends PsbtBase { inputIndex, keyPair.publicKey, this.__TX, - this.__NON_WITNESS_UTXO_CACHE, + this.__CACHE, ); const partialSig = { @@ -382,7 +393,7 @@ export class Psbt extends PsbtBase { inputIndex, keyPair.publicKey, this.__TX, - this.__NON_WITNESS_UTXO_CACHE, + this.__CACHE, ); Promise.resolve(keyPair.sign(hash)).then(signature => { @@ -409,9 +420,10 @@ export class Psbt extends PsbtBase { // // -interface NonWitnessUtxoCache { +interface PsbtCache { __NON_WITNESS_UTXO_TX_CACHE: Transaction[]; __NON_WITNESS_UTXO_BUF_CACHE: Buffer[]; + __TX_IN_CACHE: { [index: string]: number }; } interface PsbtOptsOptional { @@ -430,7 +442,7 @@ const DEFAULT_OPTS = { }; function addNonWitnessTxCache( - cache: NonWitnessUtxoCache, + cache: PsbtCache, input: PsbtInput, inputIndex: number, ): void { @@ -460,6 +472,22 @@ function addNonWitnessTxCache( }); } +function checkTxForDupeIns(tx: Transaction, cache: PsbtCache): void { + tx.ins.forEach(input => { + checkTxInputCache(cache, input); + }); +} + +function checkTxInputCache( + cache: PsbtCache, + input: { hash: Buffer; index: number }, +): void { + const key = + reverseBuffer(Buffer.from(input.hash)).toString('hex') + ':' + input.index; + if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.'); + cache.__TX_IN_CACHE[key] = 1; +} + function isFinalized(input: PsbtInput): boolean { return !!input.finalScriptSig || !!input.finalScriptWitness; } @@ -469,7 +497,7 @@ function getHashAndSighashType( inputIndex: number, pubkey: Buffer, unsignedTx: Transaction, - cache: NonWitnessUtxoCache, + cache: PsbtCache, ): { hash: Buffer; sighashType: number; @@ -630,7 +658,7 @@ const getHashForSig = ( inputIndex: number, input: PsbtInput, unsignedTx: Transaction, - cache: NonWitnessUtxoCache, + cache: PsbtCache, ): HashForSigData => { const sighashType = input.sighashType || Transaction.SIGHASH_ALL; let hash: Buffer; @@ -780,7 +808,7 @@ function getScriptFromInput( inputIndex: number, input: PsbtInput, unsignedTx: Transaction, - cache: NonWitnessUtxoCache, + cache: PsbtCache, ): GetScriptReturn { const res: GetScriptReturn = { script: null, diff --git a/types/psbt.d.ts b/types/psbt.d.ts index fae4fff..a708c21 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -7,13 +7,14 @@ import { Transaction } from './transaction'; export declare class Psbt extends PsbtBase { static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>; static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>; - private __NON_WITNESS_UTXO_CACHE; + private __CACHE; private __TX; private __TX_BUF_CACHE?; private __FEE_RATE?; private __EXTRACTED_TX?; private opts; constructor(opts?: PsbtOptsOptional); + readonly inputCount: number; setMaximumFeeRate(satoshiPerByte: number): void; setVersion(version: number): this; setLocktime(locktime: number): this; From 5f26654802a5b487fc99a7e82f5fbc89c977845c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 5 Jul 2019 18:26:52 +0900 Subject: [PATCH 377/568] Add tests --- src/psbt.js | 20 +++++++++----------- test/psbt.js | 28 ++++++++++++++++++++++++++++ ts_src/psbt.ts | 22 ++++++++++------------ 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index e797949..529f023 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -10,6 +10,10 @@ const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); const varuint = require('varuint-bitcoin'); +const DEFAULT_OPTS = { + network: networks_1.bitcoin, + maximumFeeRate: 5000, +}; class Psbt extends bip174_1.Psbt { constructor(opts = {}) { super(); @@ -202,11 +206,11 @@ class Psbt extends bip174_1.Psbt { const satoshis = feeRate * vsize; if (feeRate >= this.opts.maximumFeeRate) { throw new Error( - `Warning: You are paying around ${satoshis / 1e8} in fees, which ` + - `is ${feeRate} satoshi per byte for a transaction with a VSize of ` + - `${vsize} bytes (segwit counted as 0.25 byte per byte)\n` + - `Use setMaximumFeeRate method to raise your threshold, or pass ` + - `true to the first arg of extractTransaction.`, + `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + + `fees, which is ${feeRate} satoshi per byte for a transaction ` + + `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + + `byte). Use setMaximumFeeRate method to raise your threshold, or ` + + `pass true to the first arg of extractTransaction.`, ); } } @@ -254,8 +258,6 @@ class Psbt extends bip174_1.Psbt { const vout = this.__TX.ins[idx].index; const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout]; inputAmount += out.value; - } else { - throw new Error('Missing input value: index #' + idx); } }); this.__EXTRACTED_TX = tx; @@ -341,10 +343,6 @@ class Psbt extends bip174_1.Psbt { } } exports.Psbt = Psbt; -const DEFAULT_OPTS = { - network: networks_1.bitcoin, - maximumFeeRate: 5000, -}; function addNonWitnessTxCache(cache, input, inputIndex) { cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo; const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo); diff --git a/test/psbt.js b/test/psbt.js index 9a3ac92..ecb78ab 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -97,6 +97,16 @@ describe(`Psbt`, () => { arg.forEach(a => adder(i, initBuffers(attr, a))) } else { adder(i, initBuffers(attr, arg)) + if (attr === 'nonWitnessUtxo') { + const first = psbt.inputs[i].nonWitnessUtxo + psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined + const second = psbt.inputs[i].nonWitnessUtxo + psbt.inputs[i].nonWitnessUtxo = Buffer.from([1,2,3]) + psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined + const third = psbt.inputs[i].nonWitnessUtxo + assert.ok(first.equals(second)) + assert.ok(first.equals(third)) + } } } } @@ -140,6 +150,10 @@ describe(`Psbt`, () => { it('Finalizes inputs and gives the expected PSBT', () => { const psbt = Psbt.fromBase64(f.psbt) + assert.throws(() => { + psbt.getFeeRate() + }, new RegExp('PSBT must be finalized to calculate fee rate')) + psbt.finalizeAllInputs() assert.strictEqual(psbt.toBase64(), f.result) @@ -196,6 +210,11 @@ describe(`Psbt`, () => { ECPair.fromWIF(f.shouldThrow.WIF), ) }, {message: f.shouldThrow.errorMessage}) + assert.rejects(async () => { + await psbtThatShouldThrow.signInputAsync( + f.shouldThrow.inputToCheck, + ) + }, new RegExp('Need Signer to sign input')) }) }) }) @@ -218,6 +237,11 @@ describe(`Psbt`, () => { ECPair.fromWIF(f.shouldThrow.WIF), ) }, {message: f.shouldThrow.errorMessage}) + assert.throws(() => { + psbtThatShouldThrow.signInput( + f.shouldThrow.inputToCheck, + ) + }, new RegExp('Need Signer to sign input')) }) }) }) @@ -252,6 +276,9 @@ describe(`Psbt`, () => { console.log(psbt.toBase64()) } }) + assert.throws(() => { + psbt.addInput(f.inputData) + }, new RegExp('Duplicate input detected.')) } }) }) @@ -307,6 +334,7 @@ describe(`Psbt`, () => { index: 0 }); + assert.strictEqual(psbt.inputCount, 1) assert.strictEqual(psbt.__TX.ins[0].sequence, 0xffffffff) psbt.setSequence(0, 0) assert.strictEqual(psbt.__TX.ins[0].sequence, 0) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 24a088e..9072033 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -17,6 +17,11 @@ import * as bscript from './script'; import { Output, Transaction } from './transaction'; const varuint = require('varuint-bitcoin'); +const DEFAULT_OPTS: PsbtOpts = { + network: btcNetwork, + maximumFeeRate: 5000, // satoshi per byte +}; + export class Psbt extends PsbtBase { static fromTransaction<T extends typeof PsbtBase>( this: T, @@ -250,11 +255,11 @@ export class Psbt extends PsbtBase { const satoshis = feeRate * vsize; if (feeRate >= this.opts.maximumFeeRate) { throw new Error( - `Warning: You are paying around ${satoshis / 1e8} in fees, which ` + - `is ${feeRate} satoshi per byte for a transaction with a VSize of ` + - `${vsize} bytes (segwit counted as 0.25 byte per byte)\n` + - `Use setMaximumFeeRate method to raise your threshold, or pass ` + - `true to the first arg of extractTransaction.`, + `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + + `fees, which is ${feeRate} satoshi per byte for a transaction ` + + `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + + `byte). Use setMaximumFeeRate method to raise your threshold, or ` + + `pass true to the first arg of extractTransaction.`, ); } } @@ -303,8 +308,6 @@ export class Psbt extends PsbtBase { const vout = this.__TX.ins[idx].index; const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output; inputAmount += out.value; - } else { - throw new Error('Missing input value: index #' + idx); } }); this.__EXTRACTED_TX = tx; @@ -436,11 +439,6 @@ interface PsbtOpts { maximumFeeRate: number; } -const DEFAULT_OPTS = { - network: btcNetwork, - maximumFeeRate: 5000, // satoshi per byte -}; - function addNonWitnessTxCache( cache: PsbtCache, input: PsbtInput, From 02ba6c78d1e4827dd362cd400c343708bf36c0a1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 5 Jul 2019 19:40:31 +0900 Subject: [PATCH 378/568] Add integration tests with examples --- test/integration/transactions-psbt.js | 366 ++++++++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 test/integration/transactions-psbt.js diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js new file mode 100644 index 0000000..e48e335 --- /dev/null +++ b/test/integration/transactions-psbt.js @@ -0,0 +1,366 @@ +const { describe, it } = require('mocha') +const assert = require('assert') +const bitcoin = require('../../') +const regtestUtils = require('./_regtest') +const regtest = regtestUtils.network + +// See bottom of file for some helper functions used to make the payment objects needed. + +describe('bitcoinjs-lib (transactions with psbt)', () => { + it('can create (and broadcast via 3PBP) a typical Transaction', async () => { + // these are { payment: Payment; keys: ECPair[] } + const alice1 = createPayment('p2pkh') + const alice2 = createPayment('p2pkh') + + // give Alice 2 unspent outputs + const inputData1 = await getInputData(5e4, alice1.payment, false, 'noredeem') + const inputData2 = await getInputData(7e4, alice2.payment, false, 'noredeem') + { + const { + hash, // string of txid or Buffer of tx hash. (txid and hash are reverse order) + index, // the output index of the txo you are spending + nonWitnessUtxo, // the full previous transaction as a Buffer + } = inputData1 + assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1) + } + + // network is only needed if you pass an address to addOutput + // using script (Buffer of scriptPubkey) instead will avoid needed network. + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData1) // alice1 unspent + .addInput(inputData2) // alice2 unspent + .addOutput({ + address: 'mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', + value: 8e4 + }) // the actual "spend" + .addOutput({ + address: alice2.payment.address, // OR script, which is a Buffer. + value: 1e4 + }) // Alice's change + // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee + + // Let's show a new feature with PSBT. + // We can have multiple signers sign in parrallel and combine them. + // (this is not necessary, but a nice feature) + + // encode to send out to the signers + const psbtBaseText = psbt.toBase64() + + // each signer imports + const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText) + const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText) + + // Alice signs each input with the respective private keys + signer1.signInput(0, alice1.keys[0]) + signer2.signInput(1, alice2.keys[0]) + + // encode to send back to combiner (signer 1 and 2 are not near each other) + const s1text = signer1.toBase64() + const s2text = signer2.toBase64() + + const final1 = bitcoin.Psbt.fromBase64(s1text) + const final2 = bitcoin.Psbt.fromBase64(s2text) + + // final1.combine(final2) would give the exact same result + psbt.combine(final1, final2) + + // This step it new. Since we separate the signing operation and + // the creation of the scriptSig and witness stack, we are able to + psbt.finalizeAllInputs() + // it returns an array of the success of each input, also a result attribute + // which is true if all array items are true. + + // build and broadcast our RegTest network + await regtestUtils.broadcast(psbt.extractTransaction().toHex()) + // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 + }) + + it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => { + const alice1 = createPayment('p2pkh') + const inputData1 = await getInputData(2e5, alice1.payment, false, 'noredeem') + + const data = Buffer.from('bitcoinjs-lib', 'utf8') + const embed = bitcoin.payments.embed({ data: [data] }) + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData1) + .addOutput({ + script: embed.output, + value: 1000 + }) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 1e5 + }) + .signInput(0, alice1.keys[0]) + + psbt.finalizeAllInputs() + + // build and broadcast to the RegTest network + await regtestUtils.broadcast(psbt.extractTransaction().toHex()) + }) + + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => { + const multisig = createPayment('p2sh-p2ms(2 of 4)') + const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh') + { + const { + hash, + index, + nonWitnessUtxo, + redeemScript, // NEW: P2SH needs to give redeemScript when adding an input. + } = inputData1 + assert.deepStrictEqual({ hash, index, nonWitnessUtxo, redeemScript }, inputData1) + } + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData1) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 1e4 + }) + .signInput(0, multisig.keys[0]) + .signInput(0, multisig.keys[2]) + + 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: 1e4 + }) + }) + + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => { + const p2sh = createPayment('p2sh-p2wpkh') + const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh') + { + const { + hash, + index, + witnessUtxo, // NEW: this is an object of the output being spent { script: Buffer; value: Satoshis; } + redeemScript, + } = inputData + assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript }, inputData) + } + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4 + }) + .signInput(0, p2sh.keys[0]) + + 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 + }) + }) + + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { + + // the only thing that changes is you don't give a redeemscript for input data + + const p2wpkh = createPayment('p2wpkh') + const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem') + { + const { + hash, + index, + witnessUtxo, + } = inputData + assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData) + } + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4 + }) + .signInput(0, p2wpkh.keys[0]) + + 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 + }) + }) + + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => { + const p2wsh = createPayment('p2wsh-p2pk') + const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh') + { + const { + hash, + index, + witnessUtxo, + witnessScript, // NEW: A Buffer of the witnessScript + } = inputData + assert.deepStrictEqual({ hash, index, witnessUtxo, witnessScript }, inputData) + } + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4 + }) + .signInput(0, p2wsh.keys[0]) + + 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 + }) + }) + + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { + const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)') + const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh') + { + const { + hash, + index, + witnessUtxo, + redeemScript, + witnessScript, + } = inputData + assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript, witnessScript }, inputData) + } + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4 + }) + .signInput(0, p2sh.keys[0]) + .signInput(0, p2sh.keys[2]) + .signInput(0, p2sh.keys[3]) + + 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) { + const splitType = _type.split('-').reverse(); + const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; + const keys = []; + 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) { + keys.push(bitcoin.ECPair.makeRandom({ network: regtest })); + n-- + } + } + keys.push(bitcoin.ECPair.makeRandom({ network: regtest })); + + let payment; + splitType.forEach(type => { + if (type.slice(0, 4) === 'p2ms') { + payment = bitcoin.payments.p2ms({ + m, + pubkeys: keys.map(key => key.publicKey).sort(), + network: regtest, + }); + } else if (['p2sh', 'p2wsh'].indexOf(type) > -1) { + payment = bitcoin.payments[type]({ + redeem: payment, + network: regtest, + }); + } else { + payment = bitcoin.payments[type]({ + pubkey: keys[0].publicKey, + network: regtest, + }); + } + }); + + return { + payment, + keys, + }; +} + +function getWitnessUtxo(out) { + delete out.address; + out.script = Buffer.from(out.script, 'hex'); + return out; +} + +async function getInputData(amount, payment, isSegwit, redeemType) { + const unspent = await regtestUtils.faucetComplex(payment.output, amount); + const utx = await regtestUtils.fetch(unspent.txId); + // for non segwit inputs, you must pass the full transaction buffer + const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex'); + // for segwit inputs, you only need the output script and value as an object. + const witnessUtxo = getWitnessUtxo(utx.outs[unspent.vout]); + const mixin = isSegwit ? { witnessUtxo } : { nonWitnessUtxo }; + const mixin2 = {}; + switch (redeemType) { + case 'p2sh': + mixin2.redeemScript = payment.redeem.output; + break; + case 'p2wsh': + mixin2.witnessScript = payment.redeem.output; + break; + case 'p2sh-p2wsh': + mixin2.witnessScript = payment.redeem.redeem.output; + mixin2.redeemScript = payment.redeem.output; + break; + } + return { + hash: unspent.txId, + index: unspent.vout, + ...mixin, + ...mixin2, + }; +} From d0d94c7f06715b74b6447339889b5dbe3bc4d235 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 8 Jul 2019 15:46:06 +0900 Subject: [PATCH 379/568] Add signature verify method --- src/psbt.js | 40 +++++++++++- test/integration/transactions-psbt.js | 92 +++++++++++++++++++++++++-- ts_src/psbt.ts | 50 +++++++++++++-- types/psbt.d.ts | 1 + 4 files changed, 170 insertions(+), 13 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 529f023..3bb1b3f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -5,6 +5,7 @@ const utils_1 = require('bip174/src/lib/utils'); const address_1 = require('./address'); const bufferutils_1 = require('./bufferutils'); const crypto_1 = require('./crypto'); +const ecpair_1 = require('./ecpair'); const networks_1 = require('./networks'); const payments = require('./payments'); const bscript = require('./script'); @@ -304,6 +305,39 @@ class Psbt extends bip174_1.Psbt { this.clearFinalizedInput(inputIndex); return true; } + validateSignatures(inputIndex, pubkey) { + const input = this.inputs[inputIndex]; + const partialSig = (input || {}).partialSig; + if (!input || !partialSig || partialSig.length < 1) + throw new Error('No signatures to validate'); + const mySigs = pubkey + ? partialSig.filter(sig => sig.pubkey.equals(pubkey)) + : partialSig; + if (mySigs.length < 1) throw new Error('No signatures for this pubkey'); + const results = []; + let hashCache; + let scriptCache; + let sighashCache; + for (const pSig of mySigs) { + const sig = bscript.signature.decode(pSig.signature); + const { hash, script } = + sighashCache !== sig.hashType + ? getHashForSig( + inputIndex, + Object.assign({}, input, { sighashType: sig.hashType }), + this.__TX, + this.__CACHE, + ) + : { hash: hashCache, script: scriptCache }; + sighashCache = sig.hashType; + hashCache = hash; + scriptCache = script; + checkScriptForPubkey(pSig.pubkey, script, 'verify'); + const keypair = ecpair_1.fromPublicKey(pSig.pubkey); + results.push(keypair.verify(hash, sig.signature)); + } + return results.every(res => res === true); + } signInput(inputIndex, keyPair) { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); @@ -391,7 +425,7 @@ function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, cache) { unsignedTx, cache, ); - checkScriptForPubkey(pubkey, script); + checkScriptForPubkey(pubkey, script, 'sign'); return { hash, sighashType, @@ -494,7 +528,7 @@ function canFinalize(input, script, scriptType) { return false; } } -function checkScriptForPubkey(pubkey, script) { +function checkScriptForPubkey(pubkey, script, action) { const pubkeyHash = crypto_1.hash160(pubkey); const decompiled = bscript.decompile(script); if (decompiled === null) throw new Error('Unknown script error'); @@ -504,7 +538,7 @@ function checkScriptForPubkey(pubkey, script) { }); if (!hasKey) { throw new Error( - `Can not sign for this input with the key ${pubkey.toString('hex')}`, + `Can not ${action} for this input with the key ${pubkey.toString('hex')}`, ); } } diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index e48e335..9fb76f7 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -7,6 +7,65 @@ const regtest = regtestUtils.network // See bottom of file for some helper functions used to make the payment objects needed. describe('bitcoinjs-lib (transactions with psbt)', () => { + it('can create a 1-to-1 Transaction', () => { + const alice = bitcoin.ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr') + const psbt = new bitcoin.Psbt() + psbt.setVersion(2) // These are defaults. This line is not needed. + psbt.setLocktime(0) // These are defaults. This line is not needed. + psbt.addInput({ + // if hash is string, txid, if hash is Buffer, is reversed compared to txid + hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e', + index: 0, + sequence: 0xffffffff, // These are defaults. This line is not needed. + + // non-segwit inputs now require passing the whole previous tx as Buffer + nonWitnessUtxo: Buffer.from( + '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' + + '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + + 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + + '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + + '631e5e1e66009ce3710ceea5b1ad13ffffffff01' + + // value in satoshis (Int64LE) = 0x015f90 = 90000 + '905f010000000000' + + // scriptPubkey length + '19' + + // scriptPubkey + '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' + + // locktime + '00000000', + 'hex', + ), + + // // If this input was segwit, instead of nonWitnessUtxo, you would add + // // a witnessUtxo as follows. The scriptPubkey and the value only are needed. + // witnessUtxo: { + // script: Buffer.from( + // '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac', + // 'hex', + // ), + // value: 90000, + // }, + + // Not featured here: redeemScript. A Buffer of the redeemScript + }) + psbt.addOutput({ + address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', + value: 80000 + }) + psbt.signInput(0, alice) + psbt.validateSignatures(0) + psbt.finalizeAllInputs() + assert.strictEqual( + psbt.extractTransaction().toHex(), + '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + + 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + + 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + + '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + + 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + + '08a22724efa6f6a07b0ec4c79aa88ac00000000', + ) + }) + it('can create (and broadcast via 3PBP) a typical Transaction', async () => { // these are { payment: Payment; keys: ECPair[] } const alice1 = createPayment('p2pkh') @@ -64,6 +123,12 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // final1.combine(final2) would give the exact same result psbt.combine(final1, final2) + // Finalizer wants to check all signatures are valid before finalizing. + // If the finalizer wants to check for specific pubkeys, the second arg + // can be passed. See the first multisig example below. + assert.strictEqual(psbt.validateSignatures(0), true) + assert.strictEqual(psbt.validateSignatures(1), true) + // This step it new. Since we separate the signing operation and // the creation of the scriptSig and witness stack, we are able to psbt.finalizeAllInputs() @@ -94,6 +159,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, alice1.keys[0]) + assert.strictEqual(psbt.validateSignatures(0), true) psbt.finalizeAllInputs() // build and broadcast to the RegTest network @@ -122,6 +188,11 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .signInput(0, multisig.keys[0]) .signInput(0, multisig.keys[2]) + assert.strictEqual(psbt.validateSignatures(0), true) + assert.strictEqual(psbt.validateSignatures(0, multisig.keys[0].publicKey), true) + assert.throws(() => { + psbt.validateSignatures(0, multisig.keys[3].publicKey) + }, new RegExp('No signatures for this pubkey')) psbt.finalizeAllInputs() const tx = psbt.extractTransaction() @@ -158,6 +229,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, p2sh.keys[0]) + assert.strictEqual(psbt.validateSignatures(0), true) psbt.finalizeAllInputs() const tx = psbt.extractTransaction() @@ -196,6 +268,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, p2wpkh.keys[0]) + assert.strictEqual(psbt.validateSignatures(0), true) psbt.finalizeAllInputs() const tx = psbt.extractTransaction() @@ -232,6 +305,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, p2wsh.keys[0]) + assert.strictEqual(psbt.validateSignatures(0), true) psbt.finalizeAllInputs() const tx = psbt.extractTransaction() @@ -271,6 +345,11 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .signInput(0, p2sh.keys[2]) .signInput(0, p2sh.keys[3]) + assert.strictEqual(psbt.validateSignatures(0), true) + assert.strictEqual(psbt.validateSignatures(0, p2sh.keys[3].publicKey), true) + assert.throws(() => { + psbt.validateSignatures(0, p2sh.keys[1].publicKey) + }, new RegExp('No signatures for this pubkey')) psbt.finalizeAllInputs() const tx = psbt.extractTransaction() @@ -287,7 +366,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) }) -function createPayment(_type) { +function createPayment(_type, network) { + network = network || regtest const splitType = _type.split('-').reverse(); const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; const keys = []; @@ -297,11 +377,11 @@ function createPayment(_type) { m = parseInt(match[1]) let n = parseInt(match[2]) while (n > 1) { - keys.push(bitcoin.ECPair.makeRandom({ network: regtest })); + keys.push(bitcoin.ECPair.makeRandom({ network })); n-- } } - keys.push(bitcoin.ECPair.makeRandom({ network: regtest })); + keys.push(bitcoin.ECPair.makeRandom({ network })); let payment; splitType.forEach(type => { @@ -309,17 +389,17 @@ function createPayment(_type) { payment = bitcoin.payments.p2ms({ m, pubkeys: keys.map(key => key.publicKey).sort(), - network: regtest, + network, }); } else if (['p2sh', 'p2wsh'].indexOf(type) > -1) { payment = bitcoin.payments[type]({ redeem: payment, - network: regtest, + network, }); } else { payment = bitcoin.payments[type]({ pubkey: keys[0].publicKey, - network: regtest, + network, }); } }); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 9072033..6ccef08 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -10,7 +10,11 @@ import { checkForInput } from 'bip174/src/lib/utils'; import { toOutputScript } from './address'; import { reverseBuffer } from './bufferutils'; import { hash160 } from './crypto'; -import { Signer, SignerAsync } from './ecpair'; +import { + fromPublicKey as ecPairFromPublicKey, + Signer, + SignerAsync, +} from './ecpair'; import { bitcoin as btcNetwork, Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; @@ -367,6 +371,40 @@ export class Psbt extends PsbtBase { return true; } + validateSignatures(inputIndex: number, pubkey?: Buffer): boolean { + const input = this.inputs[inputIndex]; + const partialSig = (input || {}).partialSig; + if (!input || !partialSig || partialSig.length < 1) + throw new Error('No signatures to validate'); + const mySigs = pubkey + ? partialSig.filter(sig => sig.pubkey.equals(pubkey)) + : partialSig; + if (mySigs.length < 1) throw new Error('No signatures for this pubkey'); + const results: boolean[] = []; + let hashCache: Buffer; + let scriptCache: Buffer; + let sighashCache: number; + for (const pSig of mySigs) { + const sig = bscript.signature.decode(pSig.signature); + const { hash, script } = + sighashCache! !== sig.hashType + ? getHashForSig( + inputIndex, + Object.assign({}, input, { sighashType: sig.hashType }), + this.__TX, + this.__CACHE, + ) + : { hash: hashCache!, script: scriptCache! }; + sighashCache = sig.hashType; + hashCache = hash; + scriptCache = script; + checkScriptForPubkey(pSig.pubkey, script, 'verify'); + const keypair = ecPairFromPublicKey(pSig.pubkey); + results.push(keypair.verify(hash, sig.signature)); + } + return results.every(res => res === true); + } + signInput(inputIndex: number, keyPair: Signer): this { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); @@ -507,7 +545,7 @@ function getHashAndSighashType( unsignedTx, cache, ); - checkScriptForPubkey(pubkey, script); + checkScriptForPubkey(pubkey, script, 'sign'); return { hash, sighashType, @@ -628,7 +666,11 @@ function canFinalize( } } -function checkScriptForPubkey(pubkey: Buffer, script: Buffer): void { +function checkScriptForPubkey( + pubkey: Buffer, + script: Buffer, + action: string, +): void { const pubkeyHash = hash160(pubkey); const decompiled = bscript.decompile(script); @@ -641,7 +683,7 @@ function checkScriptForPubkey(pubkey: Buffer, script: Buffer): void { if (!hasKey) { throw new Error( - `Can not sign for this input with the key ${pubkey.toString('hex')}`, + `Can not ${action} for this input with the key ${pubkey.toString('hex')}`, ); } } diff --git a/types/psbt.d.ts b/types/psbt.d.ts index a708c21..1f9ee7b 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -29,6 +29,7 @@ export declare class Psbt extends PsbtBase { inputResults: boolean[]; }; finalizeInput(inputIndex: number): boolean; + validateSignatures(inputIndex: number, pubkey?: Buffer): boolean; signInput(inputIndex: number, keyPair: Signer): this; signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>; } From f66b568e4d86c86368b89cb470b96e650c56f499 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 8 Jul 2019 16:30:59 +0900 Subject: [PATCH 380/568] Add sign all inputs method --- src/psbt.js | 49 ++++++++++++++++++++++++ test/integration/transactions-psbt.js | 9 ++++- ts_src/psbt.ts | 55 +++++++++++++++++++++++++++ types/psbt.d.ts | 2 + 4 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 3bb1b3f..d89752b 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -338,6 +338,55 @@ class Psbt extends bip174_1.Psbt { } return results.every(res => res === true); } + sign(keyPair) { + if (!keyPair || !keyPair.publicKey) + throw new Error('Need Signer to sign input'); + // TODO: Add a pubkey/pubkeyhash cache to each input + // as input information is added, then eventually + // optimize this method. + const results = []; + for (const [i] of this.inputs.entries()) { + try { + this.signInput(i, keyPair); + results.push(true); + } catch (err) { + results.push(false); + } + } + if (results.every(v => v === false)) { + throw new Error('No inputs were signed'); + } + return this; + } + signAsync(keyPair) { + return new Promise((resolve, reject) => { + if (!keyPair || !keyPair.publicKey) + return reject(new Error('Need Signer to sign input')); + // TODO: Add a pubkey/pubkeyhash cache to each input + // as input information is added, then eventually + // optimize this method. + const results = []; + const promises = []; + for (const [i] of this.inputs.entries()) { + promises.push( + this.signInputAsync(i, keyPair).then( + () => { + results.push(true); + }, + () => { + results.push(false); + }, + ), + ); + } + return Promise.all(promises).then(() => { + if (results.every(v => v === false)) { + return reject(new Error('No inputs were signed')); + } + resolve(); + }); + }); + } signInput(inputIndex, keyPair) { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index 9fb76f7..37348c1 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -110,8 +110,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText) // Alice signs each input with the respective private keys - signer1.signInput(0, alice1.keys[0]) - signer2.signInput(1, alice2.keys[0]) + // signInput and signInputAsync are better + // (They take the input index explicitly as the first arg) + signer1.sign(alice1.keys[0]) + signer2.sign(alice2.keys[0]) + + // If your signer object's sign method returns a promise, use the following + // await signer2.signAsync(alice2.keys[0]) // encode to send back to combiner (signer 1 and 2 are not near each other) const s1text = signer1.toBase64() diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 6ccef08..3461b56 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -405,6 +405,61 @@ export class Psbt extends PsbtBase { return results.every(res => res === true); } + sign(keyPair: Signer): this { + if (!keyPair || !keyPair.publicKey) + throw new Error('Need Signer to sign input'); + + // TODO: Add a pubkey/pubkeyhash cache to each input + // as input information is added, then eventually + // optimize this method. + const results: boolean[] = []; + for (const [i] of this.inputs.entries()) { + try { + this.signInput(i, keyPair); + results.push(true); + } catch (err) { + results.push(false); + } + } + if (results.every(v => v === false)) { + throw new Error('No inputs were signed'); + } + return this; + } + + signAsync(keyPair: SignerAsync): Promise<void> { + return new Promise( + (resolve, reject): any => { + if (!keyPair || !keyPair.publicKey) + return reject(new Error('Need Signer to sign input')); + + // TODO: Add a pubkey/pubkeyhash cache to each input + // as input information is added, then eventually + // optimize this method. + const results: boolean[] = []; + const promises: Array<Promise<void>> = []; + for (const [i] of this.inputs.entries()) { + promises.push( + this.signInputAsync(i, keyPair).then( + () => { + results.push(true); + }, + () => { + results.push(false); + }, + ), + ); + } + return Promise.all(promises).then(() => { + if (results.every(v => v === false)) { + return reject(new Error('No inputs were signed')); + } + resolve(); + }); + }, + ); + } + signInput(inputIndex: number, keyPair: Signer): this { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 1f9ee7b..b2893b8 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -30,6 +30,8 @@ export declare class Psbt extends PsbtBase { }; finalizeInput(inputIndex: number): boolean; validateSignatures(inputIndex: number, pubkey?: Buffer): boolean; + sign(keyPair: Signer): this; + signAsync(keyPair: SignerAsync): Promise<void>; signInput(inputIndex: number, keyPair: Signer): this; signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>; } From e15b51536792659f1afb113379244514af0c0699 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 8 Jul 2019 17:40:21 +0900 Subject: [PATCH 381/568] Add tests --- test/psbt.js | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/test/psbt.js b/test/psbt.js index ecb78ab..cd45c1e 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -154,6 +154,20 @@ describe(`Psbt`, () => { psbt.getFeeRate() }, new RegExp('PSBT must be finalized to calculate fee rate')) + const pubkey = Buffer.from( + '029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f', + 'hex', + ) + assert.strictEqual(psbt.validateSignatures(0), true) + assert.strictEqual(psbt.validateSignatures(0, pubkey), true) + assert.throws(() => { + pubkey[32] = 42 + psbt.validateSignatures(0, pubkey) + }, new RegExp('No signatures for this pubkey')) + assert.throws(() => { + psbt.validateSignatures(42) + }, new RegExp('No signatures to validate')) + psbt.finalizeAllInputs() assert.strictEqual(psbt.toBase64(), f.result) @@ -246,6 +260,54 @@ describe(`Psbt`, () => { }) }) + describe('signAsync', () => { + fixtures.signInput.checks.forEach(f => { + if (f.description === 'checks the input exists') return + it(f.description, async () => { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotReject(async () => { + await psbtThatShouldsign.signAsync( + ECPair.fromWIF(f.shouldSign.WIF), + ) + }) + + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.rejects(async () => { + await psbtThatShouldThrow.signAsync( + ECPair.fromWIF(f.shouldThrow.WIF), + ) + }, new RegExp('No inputs were signed')) + assert.rejects(async () => { + await psbtThatShouldThrow.signAsync() + }, new RegExp('Need Signer to sign input')) + }) + }) + }) + + describe('sign', () => { + fixtures.signInput.checks.forEach(f => { + if (f.description === 'checks the input exists') return + it(f.description, () => { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotThrow(() => { + psbtThatShouldsign.sign( + ECPair.fromWIF(f.shouldSign.WIF), + ) + }) + + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.throws(() => { + psbtThatShouldThrow.sign( + ECPair.fromWIF(f.shouldThrow.WIF), + ) + }, new RegExp('No inputs were signed')) + assert.throws(() => { + psbtThatShouldThrow.sign() + }, new RegExp('Need Signer to sign input')) + }) + }) + }) + describe('fromTransaction', () => { fixtures.fromTransaction.forEach(f => { it('Creates the expected PSBT from a transaction buffer', () => { @@ -363,6 +425,44 @@ describe(`Psbt`, () => { }) }) + describe('create 1-to-1 transaction', () => { + const alice = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr') + const psbt = new Psbt() + psbt.addInput({ + hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e', + index: 0, + nonWitnessUtxo: Buffer.from( + '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' + + '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + + 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + + '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + + '631e5e1e66009ce3710ceea5b1ad13ffffffff01905f0100000000001976a9148bb' + + 'c95d2709c71607c60ee3f097c1217482f518d88ac00000000', + 'hex', + ), + sighashType: 1, + }) + psbt.addOutput({ + address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', + value: 80000 + }) + psbt.signInput(0, alice) + assert.throws(() => { + psbt.setVersion(3) + }, new RegExp('Can not modify transaction, signatures exist.')) + psbt.validateSignatures(0) + psbt.finalizeAllInputs() + assert.strictEqual( + psbt.extractTransaction().toHex(), + '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + + 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + + 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + + '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + + 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + + '08a22724efa6f6a07b0ec4c79aa88ac00000000', + ) + }) + describe('Method return types', () => { it('fromTransaction returns Psbt type (not base class)', () => { const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0])); From 09fcb1c6ee85d13a09c5c877020c67fc77b8b280 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 10:57:41 +0900 Subject: [PATCH 382/568] Use function keyword --- src/psbt.js | 60 ++++++++++++++++++++++--------------------- ts_src/psbt.ts | 70 ++++++++++++++++++++++++++------------------------ 2 files changed, 68 insertions(+), 62 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index d89752b..8ddf6ab 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -591,7 +591,7 @@ function checkScriptForPubkey(pubkey, script, action) { ); } } -const getHashForSig = (inputIndex, input, unsignedTx, cache) => { +function getHashForSig(inputIndex, input, unsignedTx, cache) { const sighashType = input.sighashType || transaction_1.Transaction.SIGHASH_ALL; let hash; @@ -672,45 +672,45 @@ const getHashForSig = (inputIndex, input, unsignedTx, cache) => { sighashType, hash, }; -}; -const scriptCheckerFactory = (payment, paymentScriptName) => ( - inputIndex, - scriptPubKey, - redeemScript, -) => { - const redeemScriptOutput = payment({ - redeem: { output: redeemScript }, - }).output; - if (!scriptPubKey.equals(redeemScriptOutput)) { - throw new Error( - `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, - ); - } -}; +} +function scriptCheckerFactory(payment, paymentScriptName) { + return (inputIndex, scriptPubKey, redeemScript) => { + const redeemScriptOutput = payment({ + redeem: { output: redeemScript }, + }).output; + if (!scriptPubKey.equals(redeemScriptOutput)) { + throw new Error( + `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } + }; +} const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script'); const checkWitnessScript = scriptCheckerFactory( payments.p2wsh, 'Witness script', ); -const isPaymentFactory = payment => script => { - try { - payment({ output: script }); - return true; - } catch (err) { - return false; - } -}; +function isPaymentFactory(payment) { + return script => { + try { + payment({ output: script }); + return true; + } catch (err) { + return false; + } + }; +} const isP2WPKH = isPaymentFactory(payments.p2wpkh); const isP2PKH = isPaymentFactory(payments.p2pkh); const isP2MS = isPaymentFactory(payments.p2ms); const isP2PK = isPaymentFactory(payments.p2pk); -const classifyScript = script => { +function classifyScript(script) { if (isP2WPKH(script)) return 'witnesspubkeyhash'; if (isP2PKH(script)) return 'pubkeyhash'; if (isP2MS(script)) return 'multisig'; if (isP2PK(script)) return 'pubkey'; return 'nonstandard'; -}; +} function getScriptFromInput(inputIndex, input, unsignedTx, cache) { const res = { script: null, @@ -748,11 +748,11 @@ function getScriptFromInput(inputIndex, input, unsignedTx, cache) { } return res; } -const hasSigs = (neededSigs, partialSig) => { +function hasSigs(neededSigs, partialSig) { if (!partialSig) return false; if (partialSig.length > neededSigs) throw new Error('Too many signatures'); return partialSig.length === neededSigs; -}; +} function witnessStackToScriptWitness(witness) { let buffer = Buffer.allocUnsafe(0); function writeSlice(slice) { @@ -797,7 +797,9 @@ function scriptWitnessToWitnessStack(buffer) { } return readVector(); } -const range = n => [...Array(n).keys()]; +function range(n) { + return [...Array(n).keys()]; +} function checkTxEmpty(tx) { const isEmpty = tx.ins.every( input => diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 3461b56..122bba8 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -749,12 +749,12 @@ interface HashForSigData { sighashType: number; } -const getHashForSig = ( +function getHashForSig( inputIndex: number, input: PsbtInput, unsignedTx: Transaction, cache: PsbtCache, -): HashForSigData => { +): HashForSigData { const sighashType = input.sighashType || Transaction.SIGHASH_ALL; let hash: Buffer; let script: Buffer; @@ -839,28 +839,30 @@ const getHashForSig = ( sighashType, hash, }; -}; +} type ScriptCheckerFunction = (idx: number, spk: Buffer, rs: Buffer) => void; -const scriptCheckerFactory = ( +function scriptCheckerFactory( payment: any, paymentScriptName: string, -): ScriptCheckerFunction => ( - inputIndex: number, - scriptPubKey: Buffer, - redeemScript: Buffer, -): void => { - const redeemScriptOutput = payment({ - redeem: { output: redeemScript }, - }).output as Buffer; +): ScriptCheckerFunction { + return ( + inputIndex: number, + scriptPubKey: Buffer, + redeemScript: Buffer, + ): void => { + const redeemScriptOutput = payment({ + redeem: { output: redeemScript }, + }).output as Buffer; - if (!scriptPubKey.equals(redeemScriptOutput)) { - throw new Error( - `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, - ); - } -}; + if (!scriptPubKey.equals(redeemScriptOutput)) { + throw new Error( + `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } + }; +} const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script'); const checkWitnessScript = scriptCheckerFactory( @@ -870,28 +872,28 @@ const checkWitnessScript = scriptCheckerFactory( type isPaymentFunction = (script: Buffer) => boolean; -const isPaymentFactory = (payment: any): isPaymentFunction => ( - script: Buffer, -): boolean => { - try { - payment({ output: script }); - return true; - } catch (err) { - return false; - } -}; +function isPaymentFactory(payment: any): isPaymentFunction { + return (script: Buffer): boolean => { + try { + payment({ output: script }); + return true; + } catch (err) { + return false; + } + }; +} const isP2WPKH = isPaymentFactory(payments.p2wpkh); const isP2PKH = isPaymentFactory(payments.p2pkh); const isP2MS = isPaymentFactory(payments.p2ms); const isP2PK = isPaymentFactory(payments.p2pk); -const classifyScript = (script: Buffer): string => { +function classifyScript(script: Buffer): string { if (isP2WPKH(script)) return 'witnesspubkeyhash'; if (isP2PKH(script)) return 'pubkeyhash'; if (isP2MS(script)) return 'multisig'; if (isP2PK(script)) return 'pubkey'; return 'nonstandard'; -}; +} interface GetScriptReturn { script: Buffer | null; @@ -942,11 +944,11 @@ function getScriptFromInput( return res; } -const hasSigs = (neededSigs: number, partialSig?: any[]): boolean => { +function hasSigs(neededSigs: number, partialSig?: any[]): boolean { if (!partialSig) return false; if (partialSig.length > neededSigs) throw new Error('Too many signatures'); return partialSig.length === neededSigs; -}; +} function witnessStackToScriptWitness(witness: Buffer[]): Buffer { let buffer = Buffer.allocUnsafe(0); @@ -1006,7 +1008,9 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] { return readVector(); } -const range = (n: number): number[] => [...Array(n).keys()]; +function range(n: number): number[] { + return [...Array(n).keys()]; +} function checkTxEmpty(tx: Transaction): void { const isEmpty = tx.ins.every( From 36a966cfcdafd9245d60e31d26c619981d0240fa Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 11:06:39 +0900 Subject: [PATCH 383/568] Check actual sighash flags instead of psbtInput one --- src/psbt.js | 43 +++++++++++++++++++------------------------ ts_src/psbt.ts | 42 ++++++++++++++++++------------------------ 2 files changed, 37 insertions(+), 48 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 8ddf6ab..c3a47ab 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -815,32 +815,27 @@ function checkTxEmpty(tx) { function checkInputsForPartialSig(inputs, action) { inputs.forEach(input => { let throws = false; - if ((input.partialSig || []).length > 0) { - if (input.sighashType !== undefined) { - const whitelist = []; - const isAnyoneCanPay = - input.sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY; - if (isAnyoneCanPay) whitelist.push('addInput'); - if (!isAnyoneCanPay && action === 'addInput') { - throws = true; - } - const hashType = input.sighashType & 0x1f; - switch (hashType) { - case transaction_1.Transaction.SIGHASH_ALL: - break; - case transaction_1.Transaction.SIGHASH_SINGLE: - case transaction_1.Transaction.SIGHASH_NONE: - whitelist.push('addOutput'); - whitelist.push('setSequence'); - break; - } - if (whitelist.indexOf(action) === -1) { - throws = true; - } - } else { + if ((input.partialSig || []).length === 0) return; + input.partialSig.forEach(pSig => { + const { hashType } = bscript.signature.decode(pSig.signature); + const whitelist = []; + const isAnyoneCanPay = + hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY; + if (isAnyoneCanPay) whitelist.push('addInput'); + const hashMod = hashType & 0x1f; + switch (hashMod) { + case transaction_1.Transaction.SIGHASH_ALL: + break; + case transaction_1.Transaction.SIGHASH_SINGLE: + case transaction_1.Transaction.SIGHASH_NONE: + whitelist.push('addOutput'); + whitelist.push('setSequence'); + break; + } + if (whitelist.indexOf(action) === -1) { throws = true; } - } + }); if (throws) { throw new Error('Can not modify transaction, signatures exist.'); } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 122bba8..9e45c83 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1028,32 +1028,26 @@ function checkTxEmpty(tx: Transaction): void { function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void { inputs.forEach(input => { let throws = false; - if ((input.partialSig || []).length > 0) { - if (input.sighashType !== undefined) { - const whitelist: string[] = []; - const isAnyoneCanPay = - input.sighashType & Transaction.SIGHASH_ANYONECANPAY; - if (isAnyoneCanPay) whitelist.push('addInput'); - if (!isAnyoneCanPay && action === 'addInput') { - throws = true; - } - const hashType = input.sighashType & 0x1f; - switch (hashType) { - case Transaction.SIGHASH_ALL: - break; - case Transaction.SIGHASH_SINGLE: - case Transaction.SIGHASH_NONE: - whitelist.push('addOutput'); - whitelist.push('setSequence'); - break; - } - if (whitelist.indexOf(action) === -1) { - throws = true; - } - } else { + if ((input.partialSig || []).length === 0) return; + input.partialSig!.forEach(pSig => { + const { hashType } = bscript.signature.decode(pSig.signature); + const whitelist: string[] = []; + const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY; + if (isAnyoneCanPay) whitelist.push('addInput'); + const hashMod = hashType & 0x1f; + switch (hashMod) { + case Transaction.SIGHASH_ALL: + break; + case Transaction.SIGHASH_SINGLE: + case Transaction.SIGHASH_NONE: + whitelist.push('addOutput'); + whitelist.push('setSequence'); + break; + } + if (whitelist.indexOf(action) === -1) { throws = true; } - } + }); if (throws) { throw new Error('Can not modify transaction, signatures exist.'); } From 88de1e7b0e140612a733c90cc2303c0e127d39be Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 11:29:20 +0900 Subject: [PATCH 384/568] Refactor: nonWitnessUtxo cache --- src/psbt.js | 44 +++++++++++++++++++++++++------------------- ts_src/psbt.ts | 49 ++++++++++++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index c3a47ab..fdb457f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -252,12 +252,9 @@ class Psbt extends bip174_1.Psbt { if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; } else if (input.nonWitnessUtxo) { - const cache = this.__CACHE; - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) { - addNonWitnessTxCache(this.__CACHE, input, idx); - } + const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx); const vout = this.__TX.ins[idx].index; - const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout]; + const out = nwTx.outs[vout]; inputAmount += out.value; } }); @@ -436,13 +433,14 @@ function addNonWitnessTxCache(cache, input, inputIndex) { Object.defineProperty(input, 'nonWitnessUtxo', { enumerable: true, get() { - if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) { - return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex]; + if (buf !== undefined) { + return buf; } else { - self.__NON_WITNESS_UTXO_BUF_CACHE[ - selfIndex - ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer(); - return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + const newBuf = txCache.toBuffer(); + self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf; + return newBuf; } }, set(data) { @@ -597,10 +595,11 @@ function getHashForSig(inputIndex, input, unsignedTx, cache) { let hash; let script; if (input.nonWitnessUtxo) { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(cache, input, inputIndex); - } - const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( + cache, + input, + inputIndex, + ); const prevoutHash = unsignedTx.ins[inputIndex].hash; const utxoHash = nonWitnessUtxoTx.getHash(); // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout @@ -723,10 +722,11 @@ function getScriptFromInput(inputIndex, input, unsignedTx, cache) { res.isP2SH = true; res.script = input.redeemScript; } else { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(cache, input, inputIndex); - } - const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( + cache, + input, + inputIndex, + ); const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; } @@ -841,6 +841,12 @@ function checkInputsForPartialSig(inputs, action) { } }); } +function nonWitnessUtxoTxFromCache(cache, input, inputIndex) { + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(cache, input, inputIndex); + } + return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; +} function check32Bit(num) { if ( typeof num !== 'number' || diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 9e45c83..272ac8e 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -305,12 +305,9 @@ export class Psbt extends PsbtBase { if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; } else if (input.nonWitnessUtxo) { - const cache = this.__CACHE; - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) { - addNonWitnessTxCache(this.__CACHE, input, idx); - } + const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx); const vout = this.__TX.ins[idx].index; - const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output; + const out = nwTx.outs[vout] as Output; inputAmount += out.value; } }); @@ -548,13 +545,14 @@ function addNonWitnessTxCache( Object.defineProperty(input, 'nonWitnessUtxo', { enumerable: true, get(): Buffer { - if (self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] !== undefined) { - return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex]; + if (buf !== undefined) { + return buf; } else { - self.__NON_WITNESS_UTXO_BUF_CACHE[ - selfIndex - ] = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex].toBuffer(); - return self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + const newBuf = txCache.toBuffer(); + self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf; + return newBuf; } }, set(data: Buffer): void { @@ -760,10 +758,11 @@ function getHashForSig( let script: Buffer; if (input.nonWitnessUtxo) { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(cache, input, inputIndex); - } - const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( + cache, + input, + inputIndex, + ); const prevoutHash = unsignedTx.ins[inputIndex].hash; const utxoHash = nonWitnessUtxoTx.getHash(); @@ -918,10 +917,11 @@ function getScriptFromInput( res.isP2SH = true; res.script = input.redeemScript; } else { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(cache, input, inputIndex); - } - const nonWitnessUtxoTx = cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( + cache, + input, + inputIndex, + ); const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; } @@ -1054,6 +1054,17 @@ function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void { }); } +function nonWitnessUtxoTxFromCache( + cache: PsbtCache, + input: PsbtInput, + inputIndex: number, +): Transaction { + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(cache, input, inputIndex); + } + return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; +} + function check32Bit(num: number): void { if ( typeof num !== 'number' || From e4e51113768a48061fd1ae2982f40ee3fffb05ef Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 11:51:28 +0900 Subject: [PATCH 385/568] Refactor: cache --- src/psbt.js | 149 ++++++++++++++++++++++-------------------- test/psbt.js | 12 ++-- ts_src/psbt.ts | 169 +++++++++++++++++++++++++----------------------- types/psbt.d.ts | 4 -- 4 files changed, 171 insertions(+), 163 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index fdb457f..c549d30 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -22,10 +22,13 @@ class Psbt extends bip174_1.Psbt { __NON_WITNESS_UTXO_TX_CACHE: [], __NON_WITNESS_UTXO_BUF_CACHE: [], __TX_IN_CACHE: {}, + __TX: new transaction_1.Transaction(), }; // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); - this.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); + this.__CACHE.__TX = transaction_1.Transaction.fromBuffer( + this.globalMap.unsignedTx, + ); this.setVersion(2); // set cache const self = this; @@ -33,15 +36,15 @@ class Psbt extends bip174_1.Psbt { Object.defineProperty(this.globalMap, 'unsignedTx', { enumerable: true, get() { - if (self.__TX_BUF_CACHE !== undefined) { - return self.__TX_BUF_CACHE; + if (self.__CACHE.__TX_BUF_CACHE !== undefined) { + return self.__CACHE.__TX_BUF_CACHE; } else { - self.__TX_BUF_CACHE = self.__TX.toBuffer(); - return self.__TX_BUF_CACHE; + self.__CACHE.__TX_BUF_CACHE = self.__CACHE.__TX.toBuffer(); + return self.__CACHE.__TX_BUF_CACHE; } }, set(data) { - self.__TX_BUF_CACHE = data; + self.__CACHE.__TX_BUF_CACHE = data; }, }); // Make data hidden when enumerating @@ -52,8 +55,6 @@ class Psbt extends bip174_1.Psbt { }); dpew(this, '__TX', false, true); dpew(this, '__EXTRACTED_TX', false, true); - dpew(this, '__FEE_RATE', false, true); - dpew(this, '__TX_BUF_CACHE', false, true); dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } @@ -61,7 +62,7 @@ class Psbt extends bip174_1.Psbt { const tx = transaction_1.Transaction.fromBuffer(txBuf); checkTxEmpty(tx); const psbt = new this(); - psbt.__TX = tx; + psbt.__CACHE.__TX = tx; checkTxForDupeIns(tx, psbt.__CACHE); let inputCount = tx.ins.length; let outputCount = tx.outs.length; @@ -90,7 +91,7 @@ class Psbt extends bip174_1.Psbt { }; }; const psbt = super.fromBuffer(buffer, txCountGetter); - psbt.__TX = tx; + psbt.__CACHE.__TX = tx; checkTxForDupeIns(tx, psbt.__CACHE); return psbt; } @@ -104,63 +105,39 @@ class Psbt extends bip174_1.Psbt { setVersion(version) { check32Bit(version); checkInputsForPartialSig(this.inputs, 'setVersion'); - this.__TX.version = version; - this.__TX_BUF_CACHE = undefined; - this.__EXTRACTED_TX = undefined; + const c = this.__CACHE; + c.__TX.version = version; + c.__TX_BUF_CACHE = undefined; + c.__EXTRACTED_TX = undefined; return this; } setLocktime(locktime) { check32Bit(locktime); checkInputsForPartialSig(this.inputs, 'setLocktime'); - this.__TX.locktime = locktime; - this.__TX_BUF_CACHE = undefined; - this.__EXTRACTED_TX = undefined; + const c = this.__CACHE; + c.__TX.locktime = locktime; + c.__TX_BUF_CACHE = undefined; + c.__EXTRACTED_TX = undefined; return this; } setSequence(inputIndex, sequence) { check32Bit(sequence); checkInputsForPartialSig(this.inputs, 'setSequence'); - if (this.__TX.ins.length <= inputIndex) { + const c = this.__CACHE; + if (c.__TX.ins.length <= inputIndex) { throw new Error('Input index too high'); } - this.__TX.ins[inputIndex].sequence = sequence; - this.__TX_BUF_CACHE = undefined; - this.__EXTRACTED_TX = undefined; + c.__TX.ins[inputIndex].sequence = sequence; + c.__TX_BUF_CACHE = undefined; + c.__EXTRACTED_TX = undefined; return this; } addInput(inputData) { checkInputsForPartialSig(this.inputs, 'addInput'); - const self = this; - const inputAdder = (_inputData, txBuf) => { - if ( - !txBuf || - _inputData.hash === undefined || - _inputData.index === undefined || - (!Buffer.isBuffer(_inputData.hash) && - typeof _inputData.hash !== 'string') || - typeof _inputData.index !== 'number' - ) { - throw new Error('Error adding input.'); - } - const prevHash = Buffer.isBuffer(_inputData.hash) - ? _inputData.hash - : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex')); - // Check if input already exists in cache. - const input = { hash: prevHash, index: _inputData.index }; - checkTxInputCache(self.__CACHE, input); - self.__TX.ins.push( - Object.assign({}, input, { - script: Buffer.alloc(0), - sequence: - _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE, - witness: [], - }), - ); - return self.__TX.toBuffer(); - }; + const inputAdder = getInputAdder(this.__CACHE); super.addInput(inputData, inputAdder); - this.__FEE_RATE = undefined; - this.__EXTRACTED_TX = undefined; + this.__CACHE.__FEE_RATE = undefined; + this.__CACHE.__EXTRACTED_TX = undefined; return this; } addOutput(outputData) { @@ -182,15 +159,15 @@ class Psbt extends bip174_1.Psbt { ) { throw new Error('Error adding output.'); } - self.__TX.outs.push({ + self.__CACHE.__TX.outs.push({ script: _outputData.script, value: _outputData.value, }); - return self.__TX.toBuffer(); + return self.__CACHE.__TX.toBuffer(); }; super.addOutput(outputData, true, outputAdder); - this.__FEE_RATE = undefined; - this.__EXTRACTED_TX = undefined; + this.__CACHE.__FEE_RATE = undefined; + this.__CACHE.__EXTRACTED_TX = undefined; return this; } addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) { @@ -202,8 +179,8 @@ class Psbt extends bip174_1.Psbt { extractTransaction(disableFeeCheck) { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); if (!disableFeeCheck) { - const feeRate = this.__FEE_RATE || this.getFeeRate(); - const vsize = this.__EXTRACTED_TX.virtualSize(); + const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate(); + const vsize = this.__CACHE.__EXTRACTED_TX.virtualSize(); const satoshis = feeRate * vsize; if (feeRate >= this.opts.maximumFeeRate) { throw new Error( @@ -215,8 +192,8 @@ class Psbt extends bip174_1.Psbt { ); } } - if (this.__EXTRACTED_TX) return this.__EXTRACTED_TX; - const tx = this.__TX.clone(); + if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX; + const tx = this.__CACHE.__TX.clone(); this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { @@ -225,21 +202,21 @@ class Psbt extends bip174_1.Psbt { ); } }); - this.__EXTRACTED_TX = tx; + this.__CACHE.__EXTRACTED_TX = tx; return tx; } getFeeRate() { if (!this.inputs.every(isFinalized)) throw new Error('PSBT must be finalized to calculate fee rate'); - if (this.__FEE_RATE) return this.__FEE_RATE; + if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE; let tx; let inputAmount = 0; let mustFinalize = true; - if (this.__EXTRACTED_TX) { - tx = this.__EXTRACTED_TX; + if (this.__CACHE.__EXTRACTED_TX) { + tx = this.__CACHE.__EXTRACTED_TX; mustFinalize = false; } else { - tx = this.__TX.clone(); + tx = this.__CACHE.__TX.clone(); } this.inputs.forEach((input, idx) => { if (mustFinalize && input.finalScriptSig) @@ -253,17 +230,17 @@ class Psbt extends bip174_1.Psbt { inputAmount += input.witnessUtxo.value; } else if (input.nonWitnessUtxo) { const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx); - const vout = this.__TX.ins[idx].index; + const vout = this.__CACHE.__TX.ins[idx].index; const out = nwTx.outs[vout]; inputAmount += out.value; } }); - this.__EXTRACTED_TX = tx; + this.__CACHE.__EXTRACTED_TX = tx; const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0); const fee = inputAmount - outputAmount; const bytes = tx.virtualSize(); - this.__FEE_RATE = Math.floor(fee / bytes); - return this.__FEE_RATE; + this.__CACHE.__FEE_RATE = Math.floor(fee / bytes); + return this.__CACHE.__FEE_RATE; } finalizeAllInputs() { const inputResults = range(this.inputs.length).map(idx => @@ -280,7 +257,7 @@ class Psbt extends bip174_1.Psbt { const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, - this.__TX, + this.__CACHE.__TX, this.__CACHE, ); if (!script) return false; @@ -322,7 +299,7 @@ class Psbt extends bip174_1.Psbt { ? getHashForSig( inputIndex, Object.assign({}, input, { sighashType: sig.hashType }), - this.__TX, + this.__CACHE.__TX, this.__CACHE, ) : { hash: hashCache, script: scriptCache }; @@ -391,7 +368,7 @@ class Psbt extends bip174_1.Psbt { this.inputs, inputIndex, keyPair.publicKey, - this.__TX, + this.__CACHE.__TX, this.__CACHE, ); const partialSig = { @@ -408,7 +385,7 @@ class Psbt extends bip174_1.Psbt { this.inputs, inputIndex, keyPair.publicKey, - this.__TX, + this.__CACHE.__TX, this.__CACHE, ); Promise.resolve(keyPair.sign(hash)).then(signature => { @@ -847,6 +824,36 @@ function nonWitnessUtxoTxFromCache(cache, input, inputIndex) { } return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; } +function getInputAdder(cache) { + const selfCache = cache; + return (_inputData, txBuf) => { + if ( + !txBuf || + _inputData.hash === undefined || + _inputData.index === undefined || + (!Buffer.isBuffer(_inputData.hash) && + typeof _inputData.hash !== 'string') || + typeof _inputData.index !== 'number' + ) { + throw new Error('Error adding input.'); + } + const prevHash = Buffer.isBuffer(_inputData.hash) + ? _inputData.hash + : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex')); + // Check if input already exists in cache. + const input = { hash: prevHash, index: _inputData.index }; + checkTxInputCache(selfCache, input); + selfCache.__TX.ins.push( + Object.assign({}, input, { + script: Buffer.alloc(0), + sequence: + _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE, + witness: [], + }), + ); + return selfCache.__TX.toBuffer(); + }; +} function check32Bit(num) { if ( typeof num !== 'number' || diff --git a/test/psbt.js b/test/psbt.js index cd45c1e..657a67a 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -397,9 +397,9 @@ describe(`Psbt`, () => { }); assert.strictEqual(psbt.inputCount, 1) - assert.strictEqual(psbt.__TX.ins[0].sequence, 0xffffffff) + assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0xffffffff) psbt.setSequence(0, 0) - assert.strictEqual(psbt.__TX.ins[0].sequence, 0) + assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0) }) it('throws if input index is too high', () => { @@ -467,24 +467,24 @@ describe(`Psbt`, () => { it('fromTransaction returns Psbt type (not base class)', () => { const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0])); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__TX); + assert.ok(psbt.__CACHE.__TX); }) it('fromBuffer returns Psbt type (not base class)', () => { const psbt = Psbt.fromBuffer(Buffer.from( '70736274ff01000a01000000000000000000000000', 'hex' //cHNidP8BAAoBAAAAAAAAAAAAAAAA )); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__TX); + assert.ok(psbt.__CACHE.__TX); }) it('fromBase64 returns Psbt type (not base class)', () => { const psbt = Psbt.fromBase64('cHNidP8BAAoBAAAAAAAAAAAAAAAA'); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__TX); + assert.ok(psbt.__CACHE.__TX); }) it('fromHex returns Psbt type (not base class)', () => { const psbt = Psbt.fromHex('70736274ff01000a01000000000000000000000000'); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__TX); + assert.ok(psbt.__CACHE.__TX); }) }) }) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 272ac8e..0174b3b 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -34,7 +34,7 @@ export class Psbt extends PsbtBase { const tx = Transaction.fromBuffer(txBuf); checkTxEmpty(tx); const psbt = new this() as Psbt; - psbt.__TX = tx; + psbt.__CACHE.__TX = tx; checkTxForDupeIns(tx, psbt.__CACHE); let inputCount = tx.ins.length; let outputCount = tx.outs.length; @@ -71,25 +71,22 @@ export class Psbt extends PsbtBase { }; }; const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt; - psbt.__TX = tx!; + psbt.__CACHE.__TX = tx!; checkTxForDupeIns(tx!, psbt.__CACHE); return psbt as InstanceType<T>; } - private __CACHE = { - __NON_WITNESS_UTXO_TX_CACHE: [] as Transaction[], - __NON_WITNESS_UTXO_BUF_CACHE: [] as Buffer[], - __TX_IN_CACHE: {} as { [index: string]: number }, + private __CACHE: PsbtCache = { + __NON_WITNESS_UTXO_TX_CACHE: [], + __NON_WITNESS_UTXO_BUF_CACHE: [], + __TX_IN_CACHE: {}, + __TX: new Transaction(), }; - private __TX: Transaction; - private __TX_BUF_CACHE?: Buffer; - private __FEE_RATE?: number; - private __EXTRACTED_TX?: Transaction; private opts: PsbtOpts; constructor(opts: PsbtOptsOptional = {}) { super(); // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); - this.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); + this.__CACHE.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); this.setVersion(2); // set cache @@ -98,15 +95,15 @@ export class Psbt extends PsbtBase { Object.defineProperty(this.globalMap, 'unsignedTx', { enumerable: true, get(): Buffer { - if (self.__TX_BUF_CACHE !== undefined) { - return self.__TX_BUF_CACHE; + if (self.__CACHE.__TX_BUF_CACHE !== undefined) { + return self.__CACHE.__TX_BUF_CACHE; } else { - self.__TX_BUF_CACHE = self.__TX.toBuffer(); - return self.__TX_BUF_CACHE; + self.__CACHE.__TX_BUF_CACHE = self.__CACHE.__TX.toBuffer(); + return self.__CACHE.__TX_BUF_CACHE; } }, set(data: Buffer): void { - self.__TX_BUF_CACHE = data; + self.__CACHE.__TX_BUF_CACHE = data; }, }); @@ -123,8 +120,6 @@ export class Psbt extends PsbtBase { }); dpew(this, '__TX', false, true); dpew(this, '__EXTRACTED_TX', false, true); - dpew(this, '__FEE_RATE', false, true); - dpew(this, '__TX_BUF_CACHE', false, true); dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } @@ -141,69 +136,42 @@ export class Psbt extends PsbtBase { setVersion(version: number): this { check32Bit(version); checkInputsForPartialSig(this.inputs, 'setVersion'); - this.__TX.version = version; - this.__TX_BUF_CACHE = undefined; - this.__EXTRACTED_TX = undefined; + const c = this.__CACHE; + c.__TX.version = version; + c.__TX_BUF_CACHE = undefined; + c.__EXTRACTED_TX = undefined; return this; } setLocktime(locktime: number): this { check32Bit(locktime); checkInputsForPartialSig(this.inputs, 'setLocktime'); - this.__TX.locktime = locktime; - this.__TX_BUF_CACHE = undefined; - this.__EXTRACTED_TX = undefined; + const c = this.__CACHE; + c.__TX.locktime = locktime; + c.__TX_BUF_CACHE = undefined; + c.__EXTRACTED_TX = undefined; return this; } setSequence(inputIndex: number, sequence: number): this { check32Bit(sequence); checkInputsForPartialSig(this.inputs, 'setSequence'); - if (this.__TX.ins.length <= inputIndex) { + const c = this.__CACHE; + if (c.__TX.ins.length <= inputIndex) { throw new Error('Input index too high'); } - this.__TX.ins[inputIndex].sequence = sequence; - this.__TX_BUF_CACHE = undefined; - this.__EXTRACTED_TX = undefined; + c.__TX.ins[inputIndex].sequence = sequence; + c.__TX_BUF_CACHE = undefined; + c.__EXTRACTED_TX = undefined; return this; } addInput(inputData: TransactionInput): this { checkInputsForPartialSig(this.inputs, 'addInput'); - const self = this; - const inputAdder = ( - _inputData: TransactionInput, - txBuf: Buffer, - ): Buffer => { - if ( - !txBuf || - (_inputData as any).hash === undefined || - (_inputData as any).index === undefined || - (!Buffer.isBuffer((_inputData as any).hash) && - typeof (_inputData as any).hash !== 'string') || - typeof (_inputData as any).index !== 'number' - ) { - throw new Error('Error adding input.'); - } - const prevHash = Buffer.isBuffer(_inputData.hash) - ? _inputData.hash - : reverseBuffer(Buffer.from(_inputData.hash, 'hex')); - - // Check if input already exists in cache. - const input = { hash: prevHash, index: _inputData.index }; - checkTxInputCache(self.__CACHE, input); - - self.__TX.ins.push({ - ...input, - script: Buffer.alloc(0), - sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE, - witness: [], - }); - return self.__TX.toBuffer(); - }; + const inputAdder = getInputAdder(this.__CACHE); super.addInput(inputData, inputAdder); - this.__FEE_RATE = undefined; - this.__EXTRACTED_TX = undefined; + this.__CACHE.__FEE_RATE = undefined; + this.__CACHE.__EXTRACTED_TX = undefined; return this; } @@ -229,15 +197,15 @@ export class Psbt extends PsbtBase { ) { throw new Error('Error adding output.'); } - self.__TX.outs.push({ + self.__CACHE.__TX.outs.push({ script: (_outputData as any).script!, value: _outputData.value, }); - return self.__TX.toBuffer(); + return self.__CACHE.__TX.toBuffer(); }; super.addOutput(outputData, true, outputAdder); - this.__FEE_RATE = undefined; - this.__EXTRACTED_TX = undefined; + this.__CACHE.__FEE_RATE = undefined; + this.__CACHE.__EXTRACTED_TX = undefined; return this; } @@ -254,8 +222,8 @@ export class Psbt extends PsbtBase { extractTransaction(disableFeeCheck?: boolean): Transaction { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); if (!disableFeeCheck) { - const feeRate = this.__FEE_RATE || this.getFeeRate(); - const vsize = this.__EXTRACTED_TX!.virtualSize(); + const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate(); + const vsize = this.__CACHE.__EXTRACTED_TX!.virtualSize(); const satoshis = feeRate * vsize; if (feeRate >= this.opts.maximumFeeRate) { throw new Error( @@ -267,8 +235,8 @@ export class Psbt extends PsbtBase { ); } } - if (this.__EXTRACTED_TX) return this.__EXTRACTED_TX; - const tx = this.__TX.clone(); + if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX; + const tx = this.__CACHE.__TX.clone(); this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { @@ -277,22 +245,22 @@ export class Psbt extends PsbtBase { ); } }); - this.__EXTRACTED_TX = tx; + this.__CACHE.__EXTRACTED_TX = tx; return tx; } getFeeRate(): number { if (!this.inputs.every(isFinalized)) throw new Error('PSBT must be finalized to calculate fee rate'); - if (this.__FEE_RATE) return this.__FEE_RATE; + if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE; let tx: Transaction; let inputAmount = 0; let mustFinalize = true; - if (this.__EXTRACTED_TX) { - tx = this.__EXTRACTED_TX; + if (this.__CACHE.__EXTRACTED_TX) { + tx = this.__CACHE.__EXTRACTED_TX; mustFinalize = false; } else { - tx = this.__TX.clone(); + tx = this.__CACHE.__TX.clone(); } this.inputs.forEach((input, idx) => { if (mustFinalize && input.finalScriptSig) @@ -306,20 +274,20 @@ export class Psbt extends PsbtBase { inputAmount += input.witnessUtxo.value; } else if (input.nonWitnessUtxo) { const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx); - const vout = this.__TX.ins[idx].index; + const vout = this.__CACHE.__TX.ins[idx].index; const out = nwTx.outs[vout] as Output; inputAmount += out.value; } }); - this.__EXTRACTED_TX = tx; + this.__CACHE.__EXTRACTED_TX = tx; const outputAmount = (tx.outs as Output[]).reduce( (total, o) => total + o.value, 0, ); const fee = inputAmount - outputAmount; const bytes = tx.virtualSize(); - this.__FEE_RATE = Math.floor(fee / bytes); - return this.__FEE_RATE; + this.__CACHE.__FEE_RATE = Math.floor(fee / bytes); + return this.__CACHE.__FEE_RATE; } finalizeAllInputs(): { @@ -341,7 +309,7 @@ export class Psbt extends PsbtBase { const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, - this.__TX, + this.__CACHE.__TX, this.__CACHE, ); if (!script) return false; @@ -388,7 +356,7 @@ export class Psbt extends PsbtBase { ? getHashForSig( inputIndex, Object.assign({}, input, { sighashType: sig.hashType }), - this.__TX, + this.__CACHE.__TX, this.__CACHE, ) : { hash: hashCache!, script: scriptCache! }; @@ -464,7 +432,7 @@ export class Psbt extends PsbtBase { this.inputs, inputIndex, keyPair.publicKey, - this.__TX, + this.__CACHE.__TX, this.__CACHE, ); @@ -485,7 +453,7 @@ export class Psbt extends PsbtBase { this.inputs, inputIndex, keyPair.publicKey, - this.__TX, + this.__CACHE.__TX, this.__CACHE, ); @@ -517,6 +485,10 @@ interface PsbtCache { __NON_WITNESS_UTXO_TX_CACHE: Transaction[]; __NON_WITNESS_UTXO_BUF_CACHE: Buffer[]; __TX_IN_CACHE: { [index: string]: number }; + __TX: Transaction; + __TX_BUF_CACHE?: Buffer; + __FEE_RATE?: number; + __EXTRACTED_TX?: Transaction; } interface PsbtOptsOptional { @@ -1065,6 +1037,39 @@ function nonWitnessUtxoTxFromCache( return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; } +function getInputAdder( + cache: PsbtCache, +): (_inputData: TransactionInput, txBuf: Buffer) => Buffer { + const selfCache = cache; + return (_inputData: TransactionInput, txBuf: Buffer): Buffer => { + if ( + !txBuf || + (_inputData as any).hash === undefined || + (_inputData as any).index === undefined || + (!Buffer.isBuffer((_inputData as any).hash) && + typeof (_inputData as any).hash !== 'string') || + typeof (_inputData as any).index !== 'number' + ) { + throw new Error('Error adding input.'); + } + const prevHash = Buffer.isBuffer(_inputData.hash) + ? _inputData.hash + : reverseBuffer(Buffer.from(_inputData.hash, 'hex')); + + // Check if input already exists in cache. + const input = { hash: prevHash, index: _inputData.index }; + checkTxInputCache(selfCache, input); + + selfCache.__TX.ins.push({ + ...input, + script: Buffer.alloc(0), + sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE, + witness: [], + }); + return selfCache.__TX.toBuffer(); + }; +} + function check32Bit(num: number): void { if ( typeof num !== 'number' || diff --git a/types/psbt.d.ts b/types/psbt.d.ts index b2893b8..775f3b0 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -8,10 +8,6 @@ export declare class Psbt extends PsbtBase { static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>; static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>; private __CACHE; - private __TX; - private __TX_BUF_CACHE?; - private __FEE_RATE?; - private __EXTRACTED_TX?; private opts; constructor(opts?: PsbtOptsOptional); readonly inputCount: number; From 497d048ebf3b269ee3782423da10478fe822f194 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 11:57:50 +0900 Subject: [PATCH 386/568] Refactor: externalize outputAdder --- src/psbt.js | 49 ++++++++++++++++++++++++-------------------- ts_src/psbt.ts | 55 +++++++++++++++++++++++++++----------------------- 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index c549d30..eff98ae 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -134,10 +134,11 @@ class Psbt extends bip174_1.Psbt { } addInput(inputData) { checkInputsForPartialSig(this.inputs, 'addInput'); - const inputAdder = getInputAdder(this.__CACHE); + const c = this.__CACHE; + const inputAdder = getInputAdder(c); super.addInput(inputData, inputAdder); - this.__CACHE.__FEE_RATE = undefined; - this.__CACHE.__EXTRACTED_TX = undefined; + c.__FEE_RATE = undefined; + c.__EXTRACTED_TX = undefined; return this; } addOutput(outputData) { @@ -148,26 +149,11 @@ class Psbt extends bip174_1.Psbt { const script = address_1.toOutputScript(address, network); outputData = Object.assign(outputData, { script }); } - const self = this; - const outputAdder = (_outputData, txBuf) => { - if ( - !txBuf || - _outputData.script === undefined || - _outputData.value === undefined || - !Buffer.isBuffer(_outputData.script) || - typeof _outputData.value !== 'number' - ) { - throw new Error('Error adding output.'); - } - self.__CACHE.__TX.outs.push({ - script: _outputData.script, - value: _outputData.value, - }); - return self.__CACHE.__TX.toBuffer(); - }; + const c = this.__CACHE; + const outputAdder = getOutputAdder(c); super.addOutput(outputData, true, outputAdder); - this.__CACHE.__FEE_RATE = undefined; - this.__CACHE.__EXTRACTED_TX = undefined; + c.__FEE_RATE = undefined; + c.__EXTRACTED_TX = undefined; return this; } addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) { @@ -854,6 +840,25 @@ function getInputAdder(cache) { return selfCache.__TX.toBuffer(); }; } +function getOutputAdder(cache) { + const selfCache = cache; + return (_outputData, txBuf) => { + if ( + !txBuf || + _outputData.script === undefined || + _outputData.value === undefined || + !Buffer.isBuffer(_outputData.script) || + typeof _outputData.value !== 'number' + ) { + throw new Error('Error adding output.'); + } + selfCache.__TX.outs.push({ + script: _outputData.script, + value: _outputData.value, + }); + return selfCache.__TX.toBuffer(); + }; +} function check32Bit(num) { if ( typeof num !== 'number' || diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 0174b3b..370b481 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -168,10 +168,11 @@ export class Psbt extends PsbtBase { addInput(inputData: TransactionInput): this { checkInputsForPartialSig(this.inputs, 'addInput'); - const inputAdder = getInputAdder(this.__CACHE); + const c = this.__CACHE; + const inputAdder = getInputAdder(c); super.addInput(inputData, inputAdder); - this.__CACHE.__FEE_RATE = undefined; - this.__CACHE.__EXTRACTED_TX = undefined; + c.__FEE_RATE = undefined; + c.__EXTRACTED_TX = undefined; return this; } @@ -183,29 +184,11 @@ export class Psbt extends PsbtBase { const script = toOutputScript(address, network); outputData = Object.assign(outputData, { script }); } - const self = this; - const outputAdder = ( - _outputData: TransactionOutput, - txBuf: Buffer, - ): Buffer => { - if ( - !txBuf || - (_outputData as any).script === undefined || - (_outputData as any).value === undefined || - !Buffer.isBuffer((_outputData as any).script) || - typeof (_outputData as any).value !== 'number' - ) { - throw new Error('Error adding output.'); - } - self.__CACHE.__TX.outs.push({ - script: (_outputData as any).script!, - value: _outputData.value, - }); - return self.__CACHE.__TX.toBuffer(); - }; + const c = this.__CACHE; + const outputAdder = getOutputAdder(c); super.addOutput(outputData, true, outputAdder); - this.__CACHE.__FEE_RATE = undefined; - this.__CACHE.__EXTRACTED_TX = undefined; + c.__FEE_RATE = undefined; + c.__EXTRACTED_TX = undefined; return this; } @@ -1070,6 +1053,28 @@ function getInputAdder( }; } +function getOutputAdder( + cache: PsbtCache, +): (_outputData: TransactionOutput, txBuf: Buffer) => Buffer { + const selfCache = cache; + return (_outputData: TransactionOutput, txBuf: Buffer): Buffer => { + if ( + !txBuf || + (_outputData as any).script === undefined || + (_outputData as any).value === undefined || + !Buffer.isBuffer((_outputData as any).script) || + typeof (_outputData as any).value !== 'number' + ) { + throw new Error('Error adding output.'); + } + selfCache.__TX.outs.push({ + script: (_outputData as any).script!, + value: _outputData.value, + }); + return selfCache.__TX.toBuffer(); + }; +} + function check32Bit(num: number): void { if ( typeof num !== 'number' || From 9749a216b8cc68fff6e00771cf93af23ce47c52a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 12:15:20 +0900 Subject: [PATCH 387/568] Refactor: input finalize and get fee shared logic --- src/psbt.js | 104 ++++++++++++++++++++++++--------------------- ts_src/psbt.ts | 112 ++++++++++++++++++++++++++++--------------------- 2 files changed, 120 insertions(+), 96 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index eff98ae..c2f9bd3 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -164,69 +164,42 @@ class Psbt extends bip174_1.Psbt { } extractTransaction(disableFeeCheck) { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + const c = this.__CACHE; if (!disableFeeCheck) { - const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate(); - const vsize = this.__CACHE.__EXTRACTED_TX.virtualSize(); - const satoshis = feeRate * vsize; - if (feeRate >= this.opts.maximumFeeRate) { - throw new Error( - `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + - `fees, which is ${feeRate} satoshi per byte for a transaction ` + - `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + - `byte). Use setMaximumFeeRate method to raise your threshold, or ` + - `pass true to the first arg of extractTransaction.`, - ); - } + checkFees(this, c, this.opts); } - if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX; - const tx = this.__CACHE.__TX.clone(); - this.inputs.forEach((input, idx) => { - if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; - if (input.finalScriptWitness) { - tx.ins[idx].witness = scriptWitnessToWitnessStack( - input.finalScriptWitness, - ); - } - }); - this.__CACHE.__EXTRACTED_TX = tx; + if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; + const tx = c.__TX.clone(); + inputFinalizeGetAmts(this.inputs, tx, c, true, false); + c.__EXTRACTED_TX = tx; return tx; } getFeeRate() { if (!this.inputs.every(isFinalized)) throw new Error('PSBT must be finalized to calculate fee rate'); - if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE; + const c = this.__CACHE; + if (c.__FEE_RATE) return c.__FEE_RATE; let tx; - let inputAmount = 0; let mustFinalize = true; - if (this.__CACHE.__EXTRACTED_TX) { - tx = this.__CACHE.__EXTRACTED_TX; + if (c.__EXTRACTED_TX) { + tx = c.__EXTRACTED_TX; mustFinalize = false; } else { - tx = this.__CACHE.__TX.clone(); + tx = c.__TX.clone(); } - this.inputs.forEach((input, idx) => { - if (mustFinalize && input.finalScriptSig) - tx.ins[idx].script = input.finalScriptSig; - if (mustFinalize && input.finalScriptWitness) { - tx.ins[idx].witness = scriptWitnessToWitnessStack( - input.finalScriptWitness, - ); - } - if (input.witnessUtxo) { - inputAmount += input.witnessUtxo.value; - } else if (input.nonWitnessUtxo) { - const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx); - const vout = this.__CACHE.__TX.ins[idx].index; - const out = nwTx.outs[vout]; - inputAmount += out.value; - } - }); - this.__CACHE.__EXTRACTED_TX = tx; + const inputAmount = inputFinalizeGetAmts( + this.inputs, + tx, + c, + mustFinalize, + true, + ); + c.__EXTRACTED_TX = tx; const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0); const fee = inputAmount - outputAmount; const bytes = tx.virtualSize(); - this.__CACHE.__FEE_RATE = Math.floor(fee / bytes); - return this.__CACHE.__FEE_RATE; + c.__FEE_RATE = Math.floor(fee / bytes); + return c.__FEE_RATE; } finalizeAllInputs() { const inputResults = range(this.inputs.length).map(idx => @@ -859,6 +832,41 @@ function getOutputAdder(cache) { return selfCache.__TX.toBuffer(); }; } +function checkFees(psbt, cache, opts) { + const feeRate = cache.__FEE_RATE || psbt.getFeeRate(); + const vsize = cache.__EXTRACTED_TX.virtualSize(); + const satoshis = feeRate * vsize; + if (feeRate >= opts.maximumFeeRate) { + throw new Error( + `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + + `fees, which is ${feeRate} satoshi per byte for a transaction ` + + `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + + `byte). Use setMaximumFeeRate method to raise your threshold, or ` + + `pass true to the first arg of extractTransaction.`, + ); + } +} +function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) { + let inputAmount = 0; + inputs.forEach((input, idx) => { + if (mustFinalize && input.finalScriptSig) + tx.ins[idx].script = input.finalScriptSig; + if (mustFinalize && input.finalScriptWitness) { + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); + } + if (getAmounts && input.witnessUtxo) { + inputAmount += input.witnessUtxo.value; + } else if (getAmounts && input.nonWitnessUtxo) { + const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx); + const vout = tx.ins[idx].index; + const out = nwTx.outs[vout]; + inputAmount += out.value; + } + }); + return inputAmount; +} function check32Bit(num) { if ( typeof num !== 'number' || diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 370b481..0771436 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -204,73 +204,46 @@ export class Psbt extends PsbtBase { extractTransaction(disableFeeCheck?: boolean): Transaction { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + const c = this.__CACHE; if (!disableFeeCheck) { - const feeRate = this.__CACHE.__FEE_RATE || this.getFeeRate(); - const vsize = this.__CACHE.__EXTRACTED_TX!.virtualSize(); - const satoshis = feeRate * vsize; - if (feeRate >= this.opts.maximumFeeRate) { - throw new Error( - `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + - `fees, which is ${feeRate} satoshi per byte for a transaction ` + - `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + - `byte). Use setMaximumFeeRate method to raise your threshold, or ` + - `pass true to the first arg of extractTransaction.`, - ); - } + checkFees(this, c, this.opts); } - if (this.__CACHE.__EXTRACTED_TX) return this.__CACHE.__EXTRACTED_TX; - const tx = this.__CACHE.__TX.clone(); - this.inputs.forEach((input, idx) => { - if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; - if (input.finalScriptWitness) { - tx.ins[idx].witness = scriptWitnessToWitnessStack( - input.finalScriptWitness, - ); - } - }); - this.__CACHE.__EXTRACTED_TX = tx; + if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; + const tx = c.__TX.clone(); + inputFinalizeGetAmts(this.inputs, tx, c, true, false); + c.__EXTRACTED_TX = tx; return tx; } getFeeRate(): number { if (!this.inputs.every(isFinalized)) throw new Error('PSBT must be finalized to calculate fee rate'); - if (this.__CACHE.__FEE_RATE) return this.__CACHE.__FEE_RATE; + const c = this.__CACHE; + if (c.__FEE_RATE) return c.__FEE_RATE; let tx: Transaction; - let inputAmount = 0; let mustFinalize = true; - if (this.__CACHE.__EXTRACTED_TX) { - tx = this.__CACHE.__EXTRACTED_TX; + if (c.__EXTRACTED_TX) { + tx = c.__EXTRACTED_TX; mustFinalize = false; } else { - tx = this.__CACHE.__TX.clone(); + tx = c.__TX.clone(); } - this.inputs.forEach((input, idx) => { - if (mustFinalize && input.finalScriptSig) - tx.ins[idx].script = input.finalScriptSig; - if (mustFinalize && input.finalScriptWitness) { - tx.ins[idx].witness = scriptWitnessToWitnessStack( - input.finalScriptWitness, - ); - } - if (input.witnessUtxo) { - inputAmount += input.witnessUtxo.value; - } else if (input.nonWitnessUtxo) { - const nwTx = nonWitnessUtxoTxFromCache(this.__CACHE, input, idx); - const vout = this.__CACHE.__TX.ins[idx].index; - const out = nwTx.outs[vout] as Output; - inputAmount += out.value; - } - }); - this.__CACHE.__EXTRACTED_TX = tx; + const inputAmount = inputFinalizeGetAmts( + this.inputs, + tx, + c, + mustFinalize, + true, + ); + c.__EXTRACTED_TX = tx; const outputAmount = (tx.outs as Output[]).reduce( (total, o) => total + o.value, 0, ); const fee = inputAmount - outputAmount; const bytes = tx.virtualSize(); - this.__CACHE.__FEE_RATE = Math.floor(fee / bytes); - return this.__CACHE.__FEE_RATE; + c.__FEE_RATE = Math.floor(fee / bytes); + return c.__FEE_RATE; } finalizeAllInputs(): { @@ -1075,6 +1048,49 @@ function getOutputAdder( }; } +function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void { + const feeRate = cache.__FEE_RATE || psbt.getFeeRate(); + const vsize = cache.__EXTRACTED_TX!.virtualSize(); + const satoshis = feeRate * vsize; + if (feeRate >= opts.maximumFeeRate) { + throw new Error( + `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + + `fees, which is ${feeRate} satoshi per byte for a transaction ` + + `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + + `byte). Use setMaximumFeeRate method to raise your threshold, or ` + + `pass true to the first arg of extractTransaction.`, + ); + } +} + +function inputFinalizeGetAmts( + inputs: PsbtInput[], + tx: Transaction, + cache: PsbtCache, + mustFinalize: boolean, + getAmounts: boolean, +): number { + let inputAmount = 0; + inputs.forEach((input, idx) => { + if (mustFinalize && input.finalScriptSig) + tx.ins[idx].script = input.finalScriptSig; + if (mustFinalize && input.finalScriptWitness) { + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); + } + if (getAmounts && input.witnessUtxo) { + inputAmount += input.witnessUtxo.value; + } else if (getAmounts && input.nonWitnessUtxo) { + const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx); + const vout = tx.ins[idx].index; + const out = nwTx.outs[vout] as Output; + inputAmount += out.value; + } + }); + return inputAmount; +} + function check32Bit(num: number): void { if ( typeof num !== 'number' || From 2fd4b9dc54eeffabd35474a732d02a60f4c0a434 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 12:30:51 +0900 Subject: [PATCH 388/568] Refactor: pass only cache to certain functions --- src/psbt.js | 38 ++++++++++++++------------------------ ts_src/psbt.ts | 36 ++++++++++++++---------------------- 2 files changed, 28 insertions(+), 46 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index c2f9bd3..d7cc03a 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -26,25 +26,24 @@ class Psbt extends bip174_1.Psbt { }; // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); - this.__CACHE.__TX = transaction_1.Transaction.fromBuffer( - this.globalMap.unsignedTx, - ); + const c = this.__CACHE; + c.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); this.setVersion(2); // set cache - const self = this; delete this.globalMap.unsignedTx; Object.defineProperty(this.globalMap, 'unsignedTx', { enumerable: true, get() { - if (self.__CACHE.__TX_BUF_CACHE !== undefined) { - return self.__CACHE.__TX_BUF_CACHE; + const buf = c.__TX_BUF_CACHE; + if (buf !== undefined) { + return buf; } else { - self.__CACHE.__TX_BUF_CACHE = self.__CACHE.__TX.toBuffer(); - return self.__CACHE.__TX_BUF_CACHE; + c.__TX_BUF_CACHE = c.__TX.toBuffer(); + return c.__TX_BUF_CACHE; } }, set(data) { - self.__CACHE.__TX_BUF_CACHE = data; + c.__TX_BUF_CACHE = data; }, }); // Make data hidden when enumerating @@ -53,8 +52,6 @@ class Psbt extends bip174_1.Psbt { enumerable, writable, }); - dpew(this, '__TX', false, true); - dpew(this, '__EXTRACTED_TX', false, true); dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } @@ -216,7 +213,6 @@ class Psbt extends bip174_1.Psbt { const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, - this.__CACHE.__TX, this.__CACHE, ); if (!script) return false; @@ -258,7 +254,6 @@ class Psbt extends bip174_1.Psbt { ? getHashForSig( inputIndex, Object.assign({}, input, { sighashType: sig.hashType }), - this.__CACHE.__TX, this.__CACHE, ) : { hash: hashCache, script: scriptCache }; @@ -327,7 +322,6 @@ class Psbt extends bip174_1.Psbt { this.inputs, inputIndex, keyPair.publicKey, - this.__CACHE.__TX, this.__CACHE, ); const partialSig = { @@ -344,7 +338,6 @@ class Psbt extends bip174_1.Psbt { this.inputs, inputIndex, keyPair.publicKey, - this.__CACHE.__TX, this.__CACHE, ); Promise.resolve(keyPair.sign(hash)).then(signature => { @@ -400,14 +393,9 @@ function checkTxInputCache(cache, input) { function isFinalized(input) { return !!input.finalScriptSig || !!input.finalScriptWitness; } -function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx, cache) { +function getHashAndSighashType(inputs, inputIndex, pubkey, cache) { const input = utils_1.checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig( - inputIndex, - input, - unsignedTx, - cache, - ); + const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache); checkScriptForPubkey(pubkey, script, 'sign'); return { hash, @@ -525,7 +513,8 @@ function checkScriptForPubkey(pubkey, script, action) { ); } } -function getHashForSig(inputIndex, input, unsignedTx, cache) { +function getHashForSig(inputIndex, input, cache) { + const unsignedTx = cache.__TX; const sighashType = input.sighashType || transaction_1.Transaction.SIGHASH_ALL; let hash; @@ -646,7 +635,8 @@ function classifyScript(script) { if (isP2PK(script)) return 'pubkey'; return 'nonstandard'; } -function getScriptFromInput(inputIndex, input, unsignedTx, cache) { +function getScriptFromInput(inputIndex, input, cache) { + const unsignedTx = cache.__TX; const res = { script: null, isSegwit: false, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 0771436..ffe417f 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -52,6 +52,7 @@ export class Psbt extends PsbtBase { } return psbt as InstanceType<T>; } + static fromBuffer<T extends typeof PsbtBase>( this: T, buffer: Buffer, @@ -75,6 +76,7 @@ export class Psbt extends PsbtBase { checkTxForDupeIns(tx!, psbt.__CACHE); return psbt as InstanceType<T>; } + private __CACHE: PsbtCache = { __NON_WITNESS_UTXO_TX_CACHE: [], __NON_WITNESS_UTXO_BUF_CACHE: [], @@ -82,28 +84,30 @@ export class Psbt extends PsbtBase { __TX: new Transaction(), }; private opts: PsbtOpts; + constructor(opts: PsbtOptsOptional = {}) { super(); // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); - this.__CACHE.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); + const c = this.__CACHE; + c.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); this.setVersion(2); // set cache - const self = this; delete this.globalMap.unsignedTx; Object.defineProperty(this.globalMap, 'unsignedTx', { enumerable: true, get(): Buffer { - if (self.__CACHE.__TX_BUF_CACHE !== undefined) { - return self.__CACHE.__TX_BUF_CACHE; + const buf = c.__TX_BUF_CACHE; + if (buf !== undefined) { + return buf; } else { - self.__CACHE.__TX_BUF_CACHE = self.__CACHE.__TX.toBuffer(); - return self.__CACHE.__TX_BUF_CACHE; + c.__TX_BUF_CACHE = c.__TX.toBuffer(); + return c.__TX_BUF_CACHE; } }, set(data: Buffer): void { - self.__CACHE.__TX_BUF_CACHE = data; + c.__TX_BUF_CACHE = data; }, }); @@ -118,8 +122,6 @@ export class Psbt extends PsbtBase { enumerable, writable, }); - dpew(this, '__TX', false, true); - dpew(this, '__EXTRACTED_TX', false, true); dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } @@ -265,7 +267,6 @@ export class Psbt extends PsbtBase { const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, - this.__CACHE.__TX, this.__CACHE, ); if (!script) return false; @@ -312,7 +313,6 @@ export class Psbt extends PsbtBase { ? getHashForSig( inputIndex, Object.assign({}, input, { sighashType: sig.hashType }), - this.__CACHE.__TX, this.__CACHE, ) : { hash: hashCache!, script: scriptCache! }; @@ -388,7 +388,6 @@ export class Psbt extends PsbtBase { this.inputs, inputIndex, keyPair.publicKey, - this.__CACHE.__TX, this.__CACHE, ); @@ -409,7 +408,6 @@ export class Psbt extends PsbtBase { this.inputs, inputIndex, keyPair.publicKey, - this.__CACHE.__TX, this.__CACHE, ); @@ -513,19 +511,13 @@ function getHashAndSighashType( inputs: PsbtInput[], inputIndex: number, pubkey: Buffer, - unsignedTx: Transaction, cache: PsbtCache, ): { hash: Buffer; sighashType: number; } { const input = checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig( - inputIndex, - input, - unsignedTx, - cache, - ); + const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache); checkScriptForPubkey(pubkey, script, 'sign'); return { hash, @@ -678,9 +670,9 @@ interface HashForSigData { function getHashForSig( inputIndex: number, input: PsbtInput, - unsignedTx: Transaction, cache: PsbtCache, ): HashForSigData { + const unsignedTx = cache.__TX; const sighashType = input.sighashType || Transaction.SIGHASH_ALL; let hash: Buffer; let script: Buffer; @@ -831,9 +823,9 @@ interface GetScriptReturn { function getScriptFromInput( inputIndex: number, input: PsbtInput, - unsignedTx: Transaction, cache: PsbtCache, ): GetScriptReturn { + const unsignedTx = cache.__TX; const res: GetScriptReturn = { script: null, isSegwit: false, From 479c56bbb4af63f11ac0df072add047f35fb8b81 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 12:58:03 +0900 Subject: [PATCH 389/568] Refactor: Re-order helper functions based on like-kind --- src/psbt.js | 606 +++++++++++++++++++------------------- ts_src/psbt.ts | 773 ++++++++++++++++++++++++------------------------- 2 files changed, 681 insertions(+), 698 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index d7cc03a..8e6f1f2 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -352,31 +352,120 @@ class Psbt extends bip174_1.Psbt { } } exports.Psbt = Psbt; -function addNonWitnessTxCache(cache, input, inputIndex) { - cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo; - const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo); - cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; - const self = cache; - const selfIndex = inputIndex; - delete input.nonWitnessUtxo; - Object.defineProperty(input, 'nonWitnessUtxo', { - enumerable: true, - get() { - const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; - const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex]; - if (buf !== undefined) { - return buf; - } else { - const newBuf = txCache.toBuffer(); - self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf; - return newBuf; +function canFinalize(input, script, scriptType) { + switch (scriptType) { + case 'pubkey': + case 'pubkeyhash': + case 'witnesspubkeyhash': + return hasSigs(1, input.partialSig); + case 'multisig': + const p2ms = payments.p2ms({ output: script }); + return hasSigs(p2ms.m, input.partialSig); + default: + return false; + } +} +function hasSigs(neededSigs, partialSig) { + if (!partialSig) return false; + if (partialSig.length > neededSigs) throw new Error('Too many signatures'); + return partialSig.length === neededSigs; +} +function isFinalized(input) { + return !!input.finalScriptSig || !!input.finalScriptWitness; +} +function isPaymentFactory(payment) { + return script => { + try { + payment({ output: script }); + return true; + } catch (err) { + return false; + } + }; +} +const isP2MS = isPaymentFactory(payments.p2ms); +const isP2PK = isPaymentFactory(payments.p2pk); +const isP2PKH = isPaymentFactory(payments.p2pkh); +const isP2WPKH = isPaymentFactory(payments.p2wpkh); +function check32Bit(num) { + if ( + typeof num !== 'number' || + num !== Math.floor(num) || + num > 0xffffffff || + num < 0 + ) { + throw new Error('Invalid 32 bit integer'); + } +} +function checkFees(psbt, cache, opts) { + const feeRate = cache.__FEE_RATE || psbt.getFeeRate(); + const vsize = cache.__EXTRACTED_TX.virtualSize(); + const satoshis = feeRate * vsize; + if (feeRate >= opts.maximumFeeRate) { + throw new Error( + `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + + `fees, which is ${feeRate} satoshi per byte for a transaction ` + + `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + + `byte). Use setMaximumFeeRate method to raise your threshold, or ` + + `pass true to the first arg of extractTransaction.`, + ); + } +} +function checkInputsForPartialSig(inputs, action) { + inputs.forEach(input => { + let throws = false; + if ((input.partialSig || []).length === 0) return; + input.partialSig.forEach(pSig => { + const { hashType } = bscript.signature.decode(pSig.signature); + const whitelist = []; + const isAnyoneCanPay = + hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY; + if (isAnyoneCanPay) whitelist.push('addInput'); + const hashMod = hashType & 0x1f; + switch (hashMod) { + case transaction_1.Transaction.SIGHASH_ALL: + break; + case transaction_1.Transaction.SIGHASH_SINGLE: + case transaction_1.Transaction.SIGHASH_NONE: + whitelist.push('addOutput'); + whitelist.push('setSequence'); + break; } - }, - set(data) { - self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data; - }, + if (whitelist.indexOf(action) === -1) { + throws = true; + } + }); + if (throws) { + throw new Error('Can not modify transaction, signatures exist.'); + } }); } +function checkScriptForPubkey(pubkey, script, action) { + const pubkeyHash = crypto_1.hash160(pubkey); + const decompiled = bscript.decompile(script); + if (decompiled === null) throw new Error('Unknown script error'); + const hasKey = decompiled.some(element => { + if (typeof element === 'number') return false; + return element.equals(pubkey) || element.equals(pubkeyHash); + }); + if (!hasKey) { + throw new Error( + `Can not ${action} for this input with the key ${pubkey.toString('hex')}`, + ); + } +} +function checkTxEmpty(tx) { + const isEmpty = tx.ins.every( + input => + input.script && + input.script.length === 0 && + input.witness && + input.witness.length === 0, + ); + if (!isEmpty) { + throw new Error('Format Error: Transaction ScriptSigs are not empty'); + } +} function checkTxForDupeIns(tx, cache) { tx.ins.forEach(input => { checkTxInputCache(cache, input); @@ -390,18 +479,23 @@ function checkTxInputCache(cache, input) { if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.'); cache.__TX_IN_CACHE[key] = 1; } -function isFinalized(input) { - return !!input.finalScriptSig || !!input.finalScriptWitness; -} -function getHashAndSighashType(inputs, inputIndex, pubkey, cache) { - const input = utils_1.checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache); - checkScriptForPubkey(pubkey, script, 'sign'); - return { - hash, - sighashType, +function scriptCheckerFactory(payment, paymentScriptName) { + return (inputIndex, scriptPubKey, redeemScript) => { + const redeemScriptOutput = payment({ + redeem: { output: redeemScript }, + }).output; + if (!scriptPubKey.equals(redeemScriptOutput)) { + throw new Error( + `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } }; } +const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script'); +const checkWitnessScript = scriptCheckerFactory( + payments.p2wsh, + 'Witness script', +); function getFinalScripts( script, scriptType, @@ -437,81 +531,14 @@ function getFinalScripts( finalScriptWitness, }; } -function getSortedSigs(script, partialSig) { - const p2ms = payments.p2ms({ output: script }); - // for each pubkey in order of p2ms script - return p2ms.pubkeys - .map(pk => { - // filter partialSig array by pubkey being equal - return ( - partialSig.filter(ps => { - return ps.pubkey.equals(pk); - })[0] || {} - ).signature; - // Any pubkey without a match will return undefined - // this last filter removes all the undefined items in the array. - }) - .filter(v => !!v); -} -function getPayment(script, scriptType, partialSig) { - let payment; - switch (scriptType) { - case 'multisig': - const sigs = getSortedSigs(script, partialSig); - payment = payments.p2ms({ - output: script, - signatures: sigs, - }); - break; - case 'pubkey': - payment = payments.p2pk({ - output: script, - signature: partialSig[0].signature, - }); - break; - case 'pubkeyhash': - payment = payments.p2pkh({ - output: script, - pubkey: partialSig[0].pubkey, - signature: partialSig[0].signature, - }); - break; - case 'witnesspubkeyhash': - payment = payments.p2wpkh({ - output: script, - pubkey: partialSig[0].pubkey, - signature: partialSig[0].signature, - }); - break; - } - return payment; -} -function canFinalize(input, script, scriptType) { - switch (scriptType) { - case 'pubkey': - case 'pubkeyhash': - case 'witnesspubkeyhash': - return hasSigs(1, input.partialSig); - case 'multisig': - const p2ms = payments.p2ms({ output: script }); - return hasSigs(p2ms.m, input.partialSig); - default: - return false; - } -} -function checkScriptForPubkey(pubkey, script, action) { - const pubkeyHash = crypto_1.hash160(pubkey); - const decompiled = bscript.decompile(script); - if (decompiled === null) throw new Error('Unknown script error'); - const hasKey = decompiled.some(element => { - if (typeof element === 'number') return false; - return element.equals(pubkey) || element.equals(pubkeyHash); - }); - if (!hasKey) { - throw new Error( - `Can not ${action} for this input with the key ${pubkey.toString('hex')}`, - ); - } +function getHashAndSighashType(inputs, inputIndex, pubkey, cache) { + const input = utils_1.checkForInput(inputs, inputIndex); + const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache); + checkScriptForPubkey(pubkey, script, 'sign'); + return { + hash, + sighashType, + }; } function getHashForSig(inputIndex, input, cache) { const unsignedTx = cache.__TX; @@ -597,182 +624,6 @@ function getHashForSig(inputIndex, input, cache) { hash, }; } -function scriptCheckerFactory(payment, paymentScriptName) { - return (inputIndex, scriptPubKey, redeemScript) => { - const redeemScriptOutput = payment({ - redeem: { output: redeemScript }, - }).output; - if (!scriptPubKey.equals(redeemScriptOutput)) { - throw new Error( - `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, - ); - } - }; -} -const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script'); -const checkWitnessScript = scriptCheckerFactory( - payments.p2wsh, - 'Witness script', -); -function isPaymentFactory(payment) { - return script => { - try { - payment({ output: script }); - return true; - } catch (err) { - return false; - } - }; -} -const isP2WPKH = isPaymentFactory(payments.p2wpkh); -const isP2PKH = isPaymentFactory(payments.p2pkh); -const isP2MS = isPaymentFactory(payments.p2ms); -const isP2PK = isPaymentFactory(payments.p2pk); -function classifyScript(script) { - if (isP2WPKH(script)) return 'witnesspubkeyhash'; - if (isP2PKH(script)) return 'pubkeyhash'; - if (isP2MS(script)) return 'multisig'; - if (isP2PK(script)) return 'pubkey'; - return 'nonstandard'; -} -function getScriptFromInput(inputIndex, input, cache) { - const unsignedTx = cache.__TX; - const res = { - script: null, - isSegwit: false, - isP2SH: false, - isP2WSH: false, - }; - if (input.nonWitnessUtxo) { - if (input.redeemScript) { - res.isP2SH = true; - res.script = input.redeemScript; - } else { - const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( - cache, - input, - inputIndex, - ); - const prevoutIndex = unsignedTx.ins[inputIndex].index; - res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; - } - } else if (input.witnessUtxo) { - res.isSegwit = true; - res.isP2SH = !!input.redeemScript; - res.isP2WSH = !!input.witnessScript; - if (input.witnessScript) { - res.script = input.witnessScript; - } else if (input.redeemScript) { - res.script = payments.p2wpkh({ - hash: input.redeemScript.slice(2), - }).output; - } else { - res.script = payments.p2wpkh({ - hash: input.witnessUtxo.script.slice(2), - }).output; - } - } - return res; -} -function hasSigs(neededSigs, partialSig) { - if (!partialSig) return false; - if (partialSig.length > neededSigs) throw new Error('Too many signatures'); - return partialSig.length === neededSigs; -} -function witnessStackToScriptWitness(witness) { - let buffer = Buffer.allocUnsafe(0); - function writeSlice(slice) { - buffer = Buffer.concat([buffer, Buffer.from(slice)]); - } - function writeVarInt(i) { - const currentLen = buffer.length; - const varintLen = varuint.encodingLength(i); - buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); - varuint.encode(i, buffer, currentLen); - } - function writeVarSlice(slice) { - writeVarInt(slice.length); - writeSlice(slice); - } - function writeVector(vector) { - writeVarInt(vector.length); - vector.forEach(writeVarSlice); - } - writeVector(witness); - return buffer; -} -function scriptWitnessToWitnessStack(buffer) { - let offset = 0; - function readSlice(n) { - offset += n; - return buffer.slice(offset - n, offset); - } - function readVarInt() { - const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; - return vi; - } - function readVarSlice() { - return readSlice(readVarInt()); - } - function readVector() { - const count = readVarInt(); - const vector = []; - for (let i = 0; i < count; i++) vector.push(readVarSlice()); - return vector; - } - return readVector(); -} -function range(n) { - return [...Array(n).keys()]; -} -function checkTxEmpty(tx) { - const isEmpty = tx.ins.every( - input => - input.script && - input.script.length === 0 && - input.witness && - input.witness.length === 0, - ); - if (!isEmpty) { - throw new Error('Format Error: Transaction ScriptSigs are not empty'); - } -} -function checkInputsForPartialSig(inputs, action) { - inputs.forEach(input => { - let throws = false; - if ((input.partialSig || []).length === 0) return; - input.partialSig.forEach(pSig => { - const { hashType } = bscript.signature.decode(pSig.signature); - const whitelist = []; - const isAnyoneCanPay = - hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY; - if (isAnyoneCanPay) whitelist.push('addInput'); - const hashMod = hashType & 0x1f; - switch (hashMod) { - case transaction_1.Transaction.SIGHASH_ALL: - break; - case transaction_1.Transaction.SIGHASH_SINGLE: - case transaction_1.Transaction.SIGHASH_NONE: - whitelist.push('addOutput'); - whitelist.push('setSequence'); - break; - } - if (whitelist.indexOf(action) === -1) { - throws = true; - } - }); - if (throws) { - throw new Error('Can not modify transaction, signatures exist.'); - } - }); -} -function nonWitnessUtxoTxFromCache(cache, input, inputIndex) { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(cache, input, inputIndex); - } - return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; -} function getInputAdder(cache) { const selfCache = cache; return (_inputData, txBuf) => { @@ -822,19 +673,162 @@ function getOutputAdder(cache) { return selfCache.__TX.toBuffer(); }; } -function checkFees(psbt, cache, opts) { - const feeRate = cache.__FEE_RATE || psbt.getFeeRate(); - const vsize = cache.__EXTRACTED_TX.virtualSize(); - const satoshis = feeRate * vsize; - if (feeRate >= opts.maximumFeeRate) { - throw new Error( - `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + - `fees, which is ${feeRate} satoshi per byte for a transaction ` + - `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + - `byte). Use setMaximumFeeRate method to raise your threshold, or ` + - `pass true to the first arg of extractTransaction.`, - ); +function getPayment(script, scriptType, partialSig) { + let payment; + switch (scriptType) { + case 'multisig': + const sigs = getSortedSigs(script, partialSig); + payment = payments.p2ms({ + output: script, + signatures: sigs, + }); + break; + case 'pubkey': + payment = payments.p2pk({ + output: script, + signature: partialSig[0].signature, + }); + break; + case 'pubkeyhash': + payment = payments.p2pkh({ + output: script, + pubkey: partialSig[0].pubkey, + signature: partialSig[0].signature, + }); + break; + case 'witnesspubkeyhash': + payment = payments.p2wpkh({ + output: script, + pubkey: partialSig[0].pubkey, + signature: partialSig[0].signature, + }); + break; } + return payment; +} +function getScriptFromInput(inputIndex, input, cache) { + const unsignedTx = cache.__TX; + const res = { + script: null, + isSegwit: false, + isP2SH: false, + isP2WSH: false, + }; + if (input.nonWitnessUtxo) { + if (input.redeemScript) { + res.isP2SH = true; + res.script = input.redeemScript; + } else { + const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( + cache, + input, + inputIndex, + ); + const prevoutIndex = unsignedTx.ins[inputIndex].index; + res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; + } + } else if (input.witnessUtxo) { + res.isSegwit = true; + res.isP2SH = !!input.redeemScript; + res.isP2WSH = !!input.witnessScript; + if (input.witnessScript) { + res.script = input.witnessScript; + } else if (input.redeemScript) { + res.script = payments.p2wpkh({ + hash: input.redeemScript.slice(2), + }).output; + } else { + res.script = payments.p2wpkh({ + hash: input.witnessUtxo.script.slice(2), + }).output; + } + } + return res; +} +function getSortedSigs(script, partialSig) { + const p2ms = payments.p2ms({ output: script }); + // for each pubkey in order of p2ms script + return p2ms.pubkeys + .map(pk => { + // filter partialSig array by pubkey being equal + return ( + partialSig.filter(ps => { + return ps.pubkey.equals(pk); + })[0] || {} + ).signature; + // Any pubkey without a match will return undefined + // this last filter removes all the undefined items in the array. + }) + .filter(v => !!v); +} +function scriptWitnessToWitnessStack(buffer) { + let offset = 0; + function readSlice(n) { + offset += n; + return buffer.slice(offset - n, offset); + } + function readVarInt() { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + } + function readVarSlice() { + return readSlice(readVarInt()); + } + function readVector() { + const count = readVarInt(); + const vector = []; + for (let i = 0; i < count; i++) vector.push(readVarSlice()); + return vector; + } + return readVector(); +} +function witnessStackToScriptWitness(witness) { + let buffer = Buffer.allocUnsafe(0); + function writeSlice(slice) { + buffer = Buffer.concat([buffer, Buffer.from(slice)]); + } + function writeVarInt(i) { + const currentLen = buffer.length; + const varintLen = varuint.encodingLength(i); + buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); + varuint.encode(i, buffer, currentLen); + } + function writeVarSlice(slice) { + writeVarInt(slice.length); + writeSlice(slice); + } + function writeVector(vector) { + writeVarInt(vector.length); + vector.forEach(writeVarSlice); + } + writeVector(witness); + return buffer; +} +function addNonWitnessTxCache(cache, input, inputIndex) { + cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo; + const tx = transaction_1.Transaction.fromBuffer(input.nonWitnessUtxo); + cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; + const self = cache; + const selfIndex = inputIndex; + delete input.nonWitnessUtxo; + Object.defineProperty(input, 'nonWitnessUtxo', { + enumerable: true, + get() { + const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex]; + if (buf !== undefined) { + return buf; + } else { + const newBuf = txCache.toBuffer(); + self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf; + return newBuf; + } + }, + set(data) { + self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data; + }, + }); } function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) { let inputAmount = 0; @@ -857,13 +851,19 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) { }); return inputAmount; } -function check32Bit(num) { - if ( - typeof num !== 'number' || - num !== Math.floor(num) || - num > 0xffffffff || - num < 0 - ) { - throw new Error('Invalid 32 bit integer'); +function nonWitnessUtxoTxFromCache(cache, input, inputIndex) { + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(cache, input, inputIndex); } + return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; +} +function classifyScript(script) { + if (isP2WPKH(script)) return 'witnesspubkeyhash'; + if (isP2PKH(script)) return 'pubkeyhash'; + if (isP2MS(script)) return 'multisig'; + if (isP2PK(script)) return 'pubkey'; + return 'nonstandard'; +} +function range(n) { + return [...Array(n).keys()]; } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index ffe417f..617cde9 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -425,16 +425,6 @@ export class Psbt extends PsbtBase { } } -// -// -// -// -// Helper functions -// -// -// -// - interface PsbtCache { __NON_WITNESS_UTXO_TX_CACHE: Transaction[]; __NON_WITNESS_UTXO_BUF_CACHE: Buffer[]; @@ -455,38 +445,139 @@ interface PsbtOpts { maximumFeeRate: number; } -function addNonWitnessTxCache( - cache: PsbtCache, +function canFinalize( input: PsbtInput, - inputIndex: number, -): void { - cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!; + script: Buffer, + scriptType: string, +): boolean { + switch (scriptType) { + case 'pubkey': + case 'pubkeyhash': + case 'witnesspubkeyhash': + return hasSigs(1, input.partialSig); + case 'multisig': + const p2ms = payments.p2ms({ output: script }); + return hasSigs(p2ms.m!, input.partialSig); + default: + return false; + } +} - const tx = Transaction.fromBuffer(input.nonWitnessUtxo!); - cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; +function hasSigs(neededSigs: number, partialSig?: any[]): boolean { + if (!partialSig) return false; + if (partialSig.length > neededSigs) throw new Error('Too many signatures'); + return partialSig.length === neededSigs; +} - const self = cache; - const selfIndex = inputIndex; - delete input.nonWitnessUtxo; - Object.defineProperty(input, 'nonWitnessUtxo', { - enumerable: true, - get(): Buffer { - const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; - const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex]; - if (buf !== undefined) { - return buf; - } else { - const newBuf = txCache.toBuffer(); - self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf; - return newBuf; +function isFinalized(input: PsbtInput): boolean { + return !!input.finalScriptSig || !!input.finalScriptWitness; +} + +function isPaymentFactory(payment: any): (script: Buffer) => boolean { + return (script: Buffer): boolean => { + try { + payment({ output: script }); + return true; + } catch (err) { + return false; + } + }; +} +const isP2MS = isPaymentFactory(payments.p2ms); +const isP2PK = isPaymentFactory(payments.p2pk); +const isP2PKH = isPaymentFactory(payments.p2pkh); +const isP2WPKH = isPaymentFactory(payments.p2wpkh); + +function check32Bit(num: number): void { + if ( + typeof num !== 'number' || + num !== Math.floor(num) || + num > 0xffffffff || + num < 0 + ) { + throw new Error('Invalid 32 bit integer'); + } +} + +function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void { + const feeRate = cache.__FEE_RATE || psbt.getFeeRate(); + const vsize = cache.__EXTRACTED_TX!.virtualSize(); + const satoshis = feeRate * vsize; + if (feeRate >= opts.maximumFeeRate) { + throw new Error( + `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + + `fees, which is ${feeRate} satoshi per byte for a transaction ` + + `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + + `byte). Use setMaximumFeeRate method to raise your threshold, or ` + + `pass true to the first arg of extractTransaction.`, + ); + } +} + +function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void { + inputs.forEach(input => { + let throws = false; + if ((input.partialSig || []).length === 0) return; + input.partialSig!.forEach(pSig => { + const { hashType } = bscript.signature.decode(pSig.signature); + const whitelist: string[] = []; + const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY; + if (isAnyoneCanPay) whitelist.push('addInput'); + const hashMod = hashType & 0x1f; + switch (hashMod) { + case Transaction.SIGHASH_ALL: + break; + case Transaction.SIGHASH_SINGLE: + case Transaction.SIGHASH_NONE: + whitelist.push('addOutput'); + whitelist.push('setSequence'); + break; } - }, - set(data: Buffer): void { - self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data; - }, + if (whitelist.indexOf(action) === -1) { + throws = true; + } + }); + if (throws) { + throw new Error('Can not modify transaction, signatures exist.'); + } }); } +function checkScriptForPubkey( + pubkey: Buffer, + script: Buffer, + action: string, +): void { + const pubkeyHash = hash160(pubkey); + + const decompiled = bscript.decompile(script); + if (decompiled === null) throw new Error('Unknown script error'); + + const hasKey = decompiled.some(element => { + if (typeof element === 'number') return false; + return element.equals(pubkey) || element.equals(pubkeyHash); + }); + + if (!hasKey) { + throw new Error( + `Can not ${action} for this input with the key ${pubkey.toString('hex')}`, + ); + } +} + +function checkTxEmpty(tx: Transaction): void { + const isEmpty = tx.ins.every( + input => + input.script && + input.script.length === 0 && + input.witness && + input.witness.length === 0, + ); + if (!isEmpty) { + throw new Error('Format Error: Transaction ScriptSigs are not empty'); + } +} + function checkTxForDupeIns(tx: Transaction, cache: PsbtCache): void { tx.ins.forEach(input => { checkTxInputCache(cache, input); @@ -503,27 +594,31 @@ function checkTxInputCache( cache.__TX_IN_CACHE[key] = 1; } -function isFinalized(input: PsbtInput): boolean { - return !!input.finalScriptSig || !!input.finalScriptWitness; -} +function scriptCheckerFactory( + payment: any, + paymentScriptName: string, +): (idx: number, spk: Buffer, rs: Buffer) => void { + return ( + inputIndex: number, + scriptPubKey: Buffer, + redeemScript: Buffer, + ): void => { + const redeemScriptOutput = payment({ + redeem: { output: redeemScript }, + }).output as Buffer; -function getHashAndSighashType( - inputs: PsbtInput[], - inputIndex: number, - pubkey: Buffer, - cache: PsbtCache, -): { - hash: Buffer; - sighashType: number; -} { - const input = checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache); - checkScriptForPubkey(pubkey, script, 'sign'); - return { - hash, - sighashType, + if (!scriptPubKey.equals(redeemScriptOutput)) { + throw new Error( + `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + ); + } }; } +const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script'); +const checkWitnessScript = scriptCheckerFactory( + payments.p2wsh, + 'Witness script', +); function getFinalScripts( script: Buffer, @@ -566,112 +661,33 @@ function getFinalScripts( }; } -function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] { - const p2ms = payments.p2ms({ output: script }); - // for each pubkey in order of p2ms script - return p2ms - .pubkeys!.map(pk => { - // filter partialSig array by pubkey being equal - return ( - partialSig.filter(ps => { - return ps.pubkey.equals(pk); - })[0] || {} - ).signature; - // Any pubkey without a match will return undefined - // this last filter removes all the undefined items in the array. - }) - .filter(v => !!v); -} - -function getPayment( - script: Buffer, - scriptType: string, - partialSig: PartialSig[], -): payments.Payment { - let payment: payments.Payment; - switch (scriptType) { - case 'multisig': - const sigs = getSortedSigs(script, partialSig); - payment = payments.p2ms({ - output: script, - signatures: sigs, - }); - break; - case 'pubkey': - payment = payments.p2pk({ - output: script, - signature: partialSig[0].signature, - }); - break; - case 'pubkeyhash': - payment = payments.p2pkh({ - output: script, - pubkey: partialSig[0].pubkey, - signature: partialSig[0].signature, - }); - break; - case 'witnesspubkeyhash': - payment = payments.p2wpkh({ - output: script, - pubkey: partialSig[0].pubkey, - signature: partialSig[0].signature, - }); - break; - } - return payment!; -} - -function canFinalize( - input: PsbtInput, - script: Buffer, - scriptType: string, -): boolean { - switch (scriptType) { - case 'pubkey': - case 'pubkeyhash': - case 'witnesspubkeyhash': - return hasSigs(1, input.partialSig); - case 'multisig': - const p2ms = payments.p2ms({ output: script }); - return hasSigs(p2ms.m!, input.partialSig); - default: - return false; - } -} - -function checkScriptForPubkey( +function getHashAndSighashType( + inputs: PsbtInput[], + inputIndex: number, pubkey: Buffer, - script: Buffer, - action: string, -): void { - const pubkeyHash = hash160(pubkey); - - const decompiled = bscript.decompile(script); - if (decompiled === null) throw new Error('Unknown script error'); - - const hasKey = decompiled.some(element => { - if (typeof element === 'number') return false; - return element.equals(pubkey) || element.equals(pubkeyHash); - }); - - if (!hasKey) { - throw new Error( - `Can not ${action} for this input with the key ${pubkey.toString('hex')}`, - ); - } -} - -interface HashForSigData { - script: Buffer; + cache: PsbtCache, +): { hash: Buffer; sighashType: number; +} { + const input = checkForInput(inputs, inputIndex); + const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache); + checkScriptForPubkey(pubkey, script, 'sign'); + return { + hash, + sighashType, + }; } function getHashForSig( inputIndex: number, input: PsbtInput, cache: PsbtCache, -): HashForSigData { +): { + script: Buffer; + hash: Buffer; + sighashType: number; +} { const unsignedTx = cache.__TX; const sighashType = input.sighashType || Transaction.SIGHASH_ALL; let hash: Buffer; @@ -760,231 +776,6 @@ function getHashForSig( }; } -type ScriptCheckerFunction = (idx: number, spk: Buffer, rs: Buffer) => void; - -function scriptCheckerFactory( - payment: any, - paymentScriptName: string, -): ScriptCheckerFunction { - return ( - inputIndex: number, - scriptPubKey: Buffer, - redeemScript: Buffer, - ): void => { - const redeemScriptOutput = payment({ - redeem: { output: redeemScript }, - }).output as Buffer; - - if (!scriptPubKey.equals(redeemScriptOutput)) { - throw new Error( - `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, - ); - } - }; -} - -const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script'); -const checkWitnessScript = scriptCheckerFactory( - payments.p2wsh, - 'Witness script', -); - -type isPaymentFunction = (script: Buffer) => boolean; - -function isPaymentFactory(payment: any): isPaymentFunction { - return (script: Buffer): boolean => { - try { - payment({ output: script }); - return true; - } catch (err) { - return false; - } - }; -} -const isP2WPKH = isPaymentFactory(payments.p2wpkh); -const isP2PKH = isPaymentFactory(payments.p2pkh); -const isP2MS = isPaymentFactory(payments.p2ms); -const isP2PK = isPaymentFactory(payments.p2pk); - -function classifyScript(script: Buffer): string { - if (isP2WPKH(script)) return 'witnesspubkeyhash'; - if (isP2PKH(script)) return 'pubkeyhash'; - if (isP2MS(script)) return 'multisig'; - if (isP2PK(script)) return 'pubkey'; - return 'nonstandard'; -} - -interface GetScriptReturn { - script: Buffer | null; - isSegwit: boolean; - isP2SH: boolean; - isP2WSH: boolean; -} -function getScriptFromInput( - inputIndex: number, - input: PsbtInput, - cache: PsbtCache, -): GetScriptReturn { - const unsignedTx = cache.__TX; - const res: GetScriptReturn = { - script: null, - isSegwit: false, - isP2SH: false, - isP2WSH: false, - }; - if (input.nonWitnessUtxo) { - if (input.redeemScript) { - res.isP2SH = true; - res.script = input.redeemScript; - } else { - const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( - cache, - input, - inputIndex, - ); - const prevoutIndex = unsignedTx.ins[inputIndex].index; - res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; - } - } else if (input.witnessUtxo) { - res.isSegwit = true; - res.isP2SH = !!input.redeemScript; - res.isP2WSH = !!input.witnessScript; - if (input.witnessScript) { - res.script = input.witnessScript; - } else if (input.redeemScript) { - res.script = payments.p2wpkh({ - hash: input.redeemScript.slice(2), - }).output!; - } else { - res.script = payments.p2wpkh({ - hash: input.witnessUtxo.script.slice(2), - }).output!; - } - } - return res; -} - -function hasSigs(neededSigs: number, partialSig?: any[]): boolean { - if (!partialSig) return false; - if (partialSig.length > neededSigs) throw new Error('Too many signatures'); - return partialSig.length === neededSigs; -} - -function witnessStackToScriptWitness(witness: Buffer[]): Buffer { - let buffer = Buffer.allocUnsafe(0); - - function writeSlice(slice: Buffer): void { - buffer = Buffer.concat([buffer, Buffer.from(slice)]); - } - - function writeVarInt(i: number): void { - const currentLen = buffer.length; - const varintLen = varuint.encodingLength(i); - - buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); - varuint.encode(i, buffer, currentLen); - } - - function writeVarSlice(slice: Buffer): void { - writeVarInt(slice.length); - writeSlice(slice); - } - - function writeVector(vector: Buffer[]): void { - writeVarInt(vector.length); - vector.forEach(writeVarSlice); - } - - writeVector(witness); - - return buffer; -} - -function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] { - let offset = 0; - - function readSlice(n: number): Buffer { - offset += n; - return buffer.slice(offset - n, offset); - } - - function readVarInt(): number { - const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; - return vi; - } - - function readVarSlice(): Buffer { - return readSlice(readVarInt()); - } - - function readVector(): Buffer[] { - const count = readVarInt(); - const vector: Buffer[] = []; - for (let i = 0; i < count; i++) vector.push(readVarSlice()); - return vector; - } - - return readVector(); -} - -function range(n: number): number[] { - return [...Array(n).keys()]; -} - -function checkTxEmpty(tx: Transaction): void { - const isEmpty = tx.ins.every( - input => - input.script && - input.script.length === 0 && - input.witness && - input.witness.length === 0, - ); - if (!isEmpty) { - throw new Error('Format Error: Transaction ScriptSigs are not empty'); - } -} - -function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void { - inputs.forEach(input => { - let throws = false; - if ((input.partialSig || []).length === 0) return; - input.partialSig!.forEach(pSig => { - const { hashType } = bscript.signature.decode(pSig.signature); - const whitelist: string[] = []; - const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY; - if (isAnyoneCanPay) whitelist.push('addInput'); - const hashMod = hashType & 0x1f; - switch (hashMod) { - case Transaction.SIGHASH_ALL: - break; - case Transaction.SIGHASH_SINGLE: - case Transaction.SIGHASH_NONE: - whitelist.push('addOutput'); - whitelist.push('setSequence'); - break; - } - if (whitelist.indexOf(action) === -1) { - throws = true; - } - }); - if (throws) { - throw new Error('Can not modify transaction, signatures exist.'); - } - }); -} - -function nonWitnessUtxoTxFromCache( - cache: PsbtCache, - input: PsbtInput, - inputIndex: number, -): Transaction { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { - addNonWitnessTxCache(cache, input, inputIndex); - } - return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; -} - function getInputAdder( cache: PsbtCache, ): (_inputData: TransactionInput, txBuf: Buffer) => Buffer { @@ -1040,19 +831,199 @@ function getOutputAdder( }; } -function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void { - const feeRate = cache.__FEE_RATE || psbt.getFeeRate(); - const vsize = cache.__EXTRACTED_TX!.virtualSize(); - const satoshis = feeRate * vsize; - if (feeRate >= opts.maximumFeeRate) { - throw new Error( - `Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` + - `fees, which is ${feeRate} satoshi per byte for a transaction ` + - `with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` + - `byte). Use setMaximumFeeRate method to raise your threshold, or ` + - `pass true to the first arg of extractTransaction.`, - ); +function getPayment( + script: Buffer, + scriptType: string, + partialSig: PartialSig[], +): payments.Payment { + let payment: payments.Payment; + switch (scriptType) { + case 'multisig': + const sigs = getSortedSigs(script, partialSig); + payment = payments.p2ms({ + output: script, + signatures: sigs, + }); + break; + case 'pubkey': + payment = payments.p2pk({ + output: script, + signature: partialSig[0].signature, + }); + break; + case 'pubkeyhash': + payment = payments.p2pkh({ + output: script, + pubkey: partialSig[0].pubkey, + signature: partialSig[0].signature, + }); + break; + case 'witnesspubkeyhash': + payment = payments.p2wpkh({ + output: script, + pubkey: partialSig[0].pubkey, + signature: partialSig[0].signature, + }); + break; } + return payment!; +} + +interface GetScriptReturn { + script: Buffer | null; + isSegwit: boolean; + isP2SH: boolean; + isP2WSH: boolean; +} +function getScriptFromInput( + inputIndex: number, + input: PsbtInput, + cache: PsbtCache, +): GetScriptReturn { + const unsignedTx = cache.__TX; + const res: GetScriptReturn = { + script: null, + isSegwit: false, + isP2SH: false, + isP2WSH: false, + }; + if (input.nonWitnessUtxo) { + if (input.redeemScript) { + res.isP2SH = true; + res.script = input.redeemScript; + } else { + const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( + cache, + input, + inputIndex, + ); + const prevoutIndex = unsignedTx.ins[inputIndex].index; + res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; + } + } else if (input.witnessUtxo) { + res.isSegwit = true; + res.isP2SH = !!input.redeemScript; + res.isP2WSH = !!input.witnessScript; + if (input.witnessScript) { + res.script = input.witnessScript; + } else if (input.redeemScript) { + res.script = payments.p2wpkh({ + hash: input.redeemScript.slice(2), + }).output!; + } else { + res.script = payments.p2wpkh({ + hash: input.witnessUtxo.script.slice(2), + }).output!; + } + } + return res; +} + +function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] { + const p2ms = payments.p2ms({ output: script }); + // for each pubkey in order of p2ms script + return p2ms + .pubkeys!.map(pk => { + // filter partialSig array by pubkey being equal + return ( + partialSig.filter(ps => { + return ps.pubkey.equals(pk); + })[0] || {} + ).signature; + // Any pubkey without a match will return undefined + // this last filter removes all the undefined items in the array. + }) + .filter(v => !!v); +} + +function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] { + let offset = 0; + + function readSlice(n: number): Buffer { + offset += n; + return buffer.slice(offset - n, offset); + } + + function readVarInt(): number { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + } + + function readVarSlice(): Buffer { + return readSlice(readVarInt()); + } + + function readVector(): Buffer[] { + const count = readVarInt(); + const vector: Buffer[] = []; + for (let i = 0; i < count; i++) vector.push(readVarSlice()); + return vector; + } + + return readVector(); +} + +function witnessStackToScriptWitness(witness: Buffer[]): Buffer { + let buffer = Buffer.allocUnsafe(0); + + function writeSlice(slice: Buffer): void { + buffer = Buffer.concat([buffer, Buffer.from(slice)]); + } + + function writeVarInt(i: number): void { + const currentLen = buffer.length; + const varintLen = varuint.encodingLength(i); + + buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); + varuint.encode(i, buffer, currentLen); + } + + function writeVarSlice(slice: Buffer): void { + writeVarInt(slice.length); + writeSlice(slice); + } + + function writeVector(vector: Buffer[]): void { + writeVarInt(vector.length); + vector.forEach(writeVarSlice); + } + + writeVector(witness); + + return buffer; +} + +function addNonWitnessTxCache( + cache: PsbtCache, + input: PsbtInput, + inputIndex: number, +): void { + cache.__NON_WITNESS_UTXO_BUF_CACHE[inputIndex] = input.nonWitnessUtxo!; + + const tx = Transaction.fromBuffer(input.nonWitnessUtxo!); + cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex] = tx; + + const self = cache; + const selfIndex = inputIndex; + delete input.nonWitnessUtxo; + Object.defineProperty(input, 'nonWitnessUtxo', { + enumerable: true, + get(): Buffer { + const buf = self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex]; + const txCache = self.__NON_WITNESS_UTXO_TX_CACHE[selfIndex]; + if (buf !== undefined) { + return buf; + } else { + const newBuf = txCache.toBuffer(); + self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = newBuf; + return newBuf; + } + }, + set(data: Buffer): void { + self.__NON_WITNESS_UTXO_BUF_CACHE[selfIndex] = data; + }, + }); } function inputFinalizeGetAmts( @@ -1083,13 +1054,25 @@ function inputFinalizeGetAmts( return inputAmount; } -function check32Bit(num: number): void { - if ( - typeof num !== 'number' || - num !== Math.floor(num) || - num > 0xffffffff || - num < 0 - ) { - throw new Error('Invalid 32 bit integer'); +function nonWitnessUtxoTxFromCache( + cache: PsbtCache, + input: PsbtInput, + inputIndex: number, +): Transaction { + if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + addNonWitnessTxCache(cache, input, inputIndex); } + return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; +} + +function classifyScript(script: Buffer): string { + if (isP2WPKH(script)) return 'witnesspubkeyhash'; + if (isP2PKH(script)) return 'pubkeyhash'; + if (isP2MS(script)) return 'multisig'; + if (isP2PK(script)) return 'pubkey'; + return 'nonstandard'; +} + +function range(n: number): number[] { + return [...Array(n).keys()]; } From 0f76aa935ad1effdb2904f36e6bb849128df1dd9 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 13:02:34 +0900 Subject: [PATCH 390/568] Refactor: Use varint from BIP174 --- src/psbt.js | 2 +- ts_src/psbt.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 8e6f1f2..aa73949 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -1,6 +1,7 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); +const varuint = require('bip174/src/lib/converter/varint'); const utils_1 = require('bip174/src/lib/utils'); const address_1 = require('./address'); const bufferutils_1 = require('./bufferutils'); @@ -10,7 +11,6 @@ const networks_1 = require('./networks'); const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); -const varuint = require('varuint-bitcoin'); const DEFAULT_OPTS = { network: networks_1.bitcoin, maximumFeeRate: 5000, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 617cde9..a78b37b 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,4 +1,5 @@ import { Psbt as PsbtBase } from 'bip174'; +import * as varuint from 'bip174/src/lib/converter/varint'; import { NonWitnessUtxo, PartialSig, @@ -19,7 +20,6 @@ import { bitcoin as btcNetwork, Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; import { Output, Transaction } from './transaction'; -const varuint = require('varuint-bitcoin'); const DEFAULT_OPTS: PsbtOpts = { network: btcNetwork, @@ -946,7 +946,7 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] { function readVarInt(): number { const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; + offset += (varuint.decode as any).bytes; return vi; } From ba33f0317f3d102be69ad269eb584a12ff7d5bcc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 13:55:02 +0900 Subject: [PATCH 391/568] Add check for spending more than you have --- src/psbt.js | 36 ++++++++++++++++-------------------- ts_src/psbt.ts | 45 ++++++++++++++++++++------------------------- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index aa73949..4c80a31 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -167,8 +167,7 @@ class Psbt extends bip174_1.Psbt { } if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; const tx = c.__TX.clone(); - inputFinalizeGetAmts(this.inputs, tx, c, true, false); - c.__EXTRACTED_TX = tx; + inputFinalizeGetAmts(this.inputs, tx, c, true); return tx; } getFeeRate() { @@ -184,18 +183,7 @@ class Psbt extends bip174_1.Psbt { } else { tx = c.__TX.clone(); } - const inputAmount = inputFinalizeGetAmts( - this.inputs, - tx, - c, - mustFinalize, - true, - ); - c.__EXTRACTED_TX = tx; - const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0); - const fee = inputAmount - outputAmount; - const bytes = tx.virtualSize(); - c.__FEE_RATE = Math.floor(fee / bytes); + inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize); return c.__FEE_RATE; } finalizeAllInputs() { @@ -830,7 +818,7 @@ function addNonWitnessTxCache(cache, input, inputIndex) { }, }); } -function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) { +function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) { let inputAmount = 0; inputs.forEach((input, idx) => { if (mustFinalize && input.finalScriptSig) @@ -840,22 +828,30 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, getAmounts) { input.finalScriptWitness, ); } - if (getAmounts && input.witnessUtxo) { + if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; - } else if (getAmounts && input.nonWitnessUtxo) { + } else if (input.nonWitnessUtxo) { const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx); const vout = tx.ins[idx].index; const out = nwTx.outs[vout]; inputAmount += out.value; } }); - return inputAmount; + const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0); + const fee = inputAmount - outputAmount; + if (fee < 0) { + throw new Error('Outputs are spending more than Inputs'); + } + const bytes = tx.virtualSize(); + cache.__EXTRACTED_TX = tx; + cache.__FEE_RATE = Math.floor(fee / bytes); } function nonWitnessUtxoTxFromCache(cache, input, inputIndex) { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + const c = cache.__NON_WITNESS_UTXO_TX_CACHE; + if (!c[inputIndex]) { addNonWitnessTxCache(cache, input, inputIndex); } - return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + return c[inputIndex]; } function classifyScript(script) { if (isP2WPKH(script)) return 'witnesspubkeyhash'; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index a78b37b..2e4c826 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -212,8 +212,7 @@ export class Psbt extends PsbtBase { } if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; const tx = c.__TX.clone(); - inputFinalizeGetAmts(this.inputs, tx, c, true, false); - c.__EXTRACTED_TX = tx; + inputFinalizeGetAmts(this.inputs, tx, c, true); return tx; } @@ -230,22 +229,8 @@ export class Psbt extends PsbtBase { } else { tx = c.__TX.clone(); } - const inputAmount = inputFinalizeGetAmts( - this.inputs, - tx, - c, - mustFinalize, - true, - ); - c.__EXTRACTED_TX = tx; - const outputAmount = (tx.outs as Output[]).reduce( - (total, o) => total + o.value, - 0, - ); - const fee = inputAmount - outputAmount; - const bytes = tx.virtualSize(); - c.__FEE_RATE = Math.floor(fee / bytes); - return c.__FEE_RATE; + inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize); + return c.__FEE_RATE!; } finalizeAllInputs(): { @@ -1031,8 +1016,7 @@ function inputFinalizeGetAmts( tx: Transaction, cache: PsbtCache, mustFinalize: boolean, - getAmounts: boolean, -): number { +): void { let inputAmount = 0; inputs.forEach((input, idx) => { if (mustFinalize && input.finalScriptSig) @@ -1042,16 +1026,26 @@ function inputFinalizeGetAmts( input.finalScriptWitness, ); } - if (getAmounts && input.witnessUtxo) { + if (input.witnessUtxo) { inputAmount += input.witnessUtxo.value; - } else if (getAmounts && input.nonWitnessUtxo) { + } else if (input.nonWitnessUtxo) { const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx); const vout = tx.ins[idx].index; const out = nwTx.outs[vout] as Output; inputAmount += out.value; } }); - return inputAmount; + const outputAmount = (tx.outs as Output[]).reduce( + (total, o) => total + o.value, + 0, + ); + const fee = inputAmount - outputAmount; + if (fee < 0) { + throw new Error('Outputs are spending more than Inputs'); + } + const bytes = tx.virtualSize(); + cache.__EXTRACTED_TX = tx; + cache.__FEE_RATE = Math.floor(fee / bytes); } function nonWitnessUtxoTxFromCache( @@ -1059,10 +1053,11 @@ function nonWitnessUtxoTxFromCache( input: PsbtInput, inputIndex: number, ): Transaction { - if (!cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]) { + const c = cache.__NON_WITNESS_UTXO_TX_CACHE; + if (!c[inputIndex]) { addNonWitnessTxCache(cache, input, inputIndex); } - return cache.__NON_WITNESS_UTXO_TX_CACHE[inputIndex]; + return c[inputIndex]; } function classifyScript(script: Buffer): string { From b8c341dea0be5a8cd3feb313a71b697b31a99b4a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 15:45:56 +0900 Subject: [PATCH 392/568] Finalize should chain this as well. --- src/psbt.js | 30 ++++++++++++--------- test/integration/transactions-psbt.js | 23 ++++++++-------- ts_src/psbt.ts | 38 ++++++++++++++------------- types/psbt.d.ts | 8 +++--- 4 files changed, 51 insertions(+), 48 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 4c80a31..58a56dd 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -187,14 +187,9 @@ class Psbt extends bip174_1.Psbt { return c.__FEE_RATE; } finalizeAllInputs() { - const inputResults = range(this.inputs.length).map(idx => - this.finalizeInput(idx), - ); - const result = inputResults.every(val => val === true); - return { - result, - inputResults, - }; + utils_1.checkForInput(this.inputs, 0); // making sure we have at least one + range(this.inputs.length).forEach(idx => this.finalizeInput(idx)); + return this; } finalizeInput(inputIndex) { const input = utils_1.checkForInput(this.inputs, inputIndex); @@ -203,9 +198,10 @@ class Psbt extends bip174_1.Psbt { input, this.__CACHE, ); - if (!script) return false; + if (!script) throw new Error(`No script found for input #${inputIndex}`); const scriptType = classifyScript(script); - if (!canFinalize(input, script, scriptType)) return false; + if (!canFinalize(input, script, scriptType)) + throw new Error(`Can not finalize input #${inputIndex}`); const { finalScriptSig, finalScriptWitness } = getFinalScripts( script, scriptType, @@ -218,9 +214,17 @@ class Psbt extends bip174_1.Psbt { this.addFinalScriptSigToInput(inputIndex, finalScriptSig); if (finalScriptWitness) this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); - if (!finalScriptSig && !finalScriptWitness) return false; + if (!finalScriptSig && !finalScriptWitness) + throw new Error(`Unknown error finalizing input #${inputIndex}`); this.clearFinalizedInput(inputIndex); - return true; + return this; + } + validateAllSignatures() { + utils_1.checkForInput(this.inputs, 0); // making sure we have at least one + const results = range(this.inputs.length).map(idx => + this.validateSignatures(idx), + ); + return results.reduce((final, res) => res === true && final, true); } validateSignatures(inputIndex, pubkey) { const input = this.inputs[inputIndex]; @@ -261,7 +265,7 @@ class Psbt extends bip174_1.Psbt { // as input information is added, then eventually // optimize this method. const results = []; - for (const [i] of this.inputs.entries()) { + for (const i of range(this.inputs.length)) { try { this.signInput(i, keyPair); results.push(true); diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index 37348c1..a7398a8 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -225,26 +225,25 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { } = inputData assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript }, inputData) } + const keyPair = p2sh.keys[0] + const outputData = { + script: p2sh.payment.output, // sending to myself for fun + value: 2e4 + } - const psbt = new bitcoin.Psbt({ network: regtest }) + const tx = new bitcoin.Psbt() .addInput(inputData) - .addOutput({ - address: regtestUtils.RANDOM_ADDRESS, - value: 2e4 - }) - .signInput(0, p2sh.keys[0]) - - assert.strictEqual(psbt.validateSignatures(0), true) - psbt.finalizeAllInputs() - - const tx = psbt.extractTransaction() + .addOutput(outputData) + .sign(keyPair) + .finalizeAllInputs() + .extractTransaction() // build and broadcast to the Bitcoin RegTest network await regtestUtils.broadcast(tx.toHex()) await regtestUtils.verify({ txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, + address: p2sh.payment.address, vout: 0, value: 2e4 }) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 2e4c826..61ed15e 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -233,31 +233,24 @@ export class Psbt extends PsbtBase { return c.__FEE_RATE!; } - finalizeAllInputs(): { - result: boolean; - inputResults: boolean[]; - } { - const inputResults = range(this.inputs.length).map(idx => - this.finalizeInput(idx), - ); - const result = inputResults.every(val => val === true); - return { - result, - inputResults, - }; + finalizeAllInputs(): this { + checkForInput(this.inputs, 0); // making sure we have at least one + range(this.inputs.length).forEach(idx => this.finalizeInput(idx)); + return this; } - finalizeInput(inputIndex: number): boolean { + finalizeInput(inputIndex: number): this { const input = checkForInput(this.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, this.__CACHE, ); - if (!script) return false; + if (!script) throw new Error(`No script found for input #${inputIndex}`); const scriptType = classifyScript(script); - if (!canFinalize(input, script, scriptType)) return false; + if (!canFinalize(input, script, scriptType)) + throw new Error(`Can not finalize input #${inputIndex}`); const { finalScriptSig, finalScriptWitness } = getFinalScripts( script, @@ -272,10 +265,19 @@ export class Psbt extends PsbtBase { this.addFinalScriptSigToInput(inputIndex, finalScriptSig); if (finalScriptWitness) this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); - if (!finalScriptSig && !finalScriptWitness) return false; + if (!finalScriptSig && !finalScriptWitness) + throw new Error(`Unknown error finalizing input #${inputIndex}`); this.clearFinalizedInput(inputIndex); - return true; + return this; + } + + validateAllSignatures(): boolean { + checkForInput(this.inputs, 0); // making sure we have at least one + const results = range(this.inputs.length).map(idx => + this.validateSignatures(idx), + ); + return results.reduce((final, res) => res === true && final, true); } validateSignatures(inputIndex: number, pubkey?: Buffer): boolean { @@ -319,7 +321,7 @@ export class Psbt extends PsbtBase { // as input information is added, then eventually // optimize this method. const results: boolean[] = []; - for (const [i] of this.inputs.entries()) { + for (const i of range(this.inputs.length)) { try { this.signInput(i, keyPair); results.push(true); diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 775f3b0..40571fa 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -20,11 +20,9 @@ export declare class Psbt extends PsbtBase { addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this; extractTransaction(disableFeeCheck?: boolean): Transaction; getFeeRate(): number; - finalizeAllInputs(): { - result: boolean; - inputResults: boolean[]; - }; - finalizeInput(inputIndex: number): boolean; + finalizeAllInputs(): this; + finalizeInput(inputIndex: number): this; + validateAllSignatures(): boolean; validateSignatures(inputIndex: number, pubkey?: Buffer): boolean; sign(keyPair: Signer): this; signAsync(keyPair: SignerAsync): Promise<void>; From 01c7ac39b611e63cfff38d5292b295fe15e65dd3 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 9 Jul 2019 18:03:15 +0900 Subject: [PATCH 393/568] Add clone, addInputs, addOutputs --- src/psbt.js | 14 ++ test/integration/transactions-psbt.js | 339 ++++++++++++++------------ ts_src/psbt.ts | 17 ++ types/psbt.d.ts | 3 + 4 files changed, 221 insertions(+), 152 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 58a56dd..33a32a3 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -95,6 +95,12 @@ class Psbt extends bip174_1.Psbt { get inputCount() { return this.inputs.length; } + clone() { + // TODO: more efficient cloning + const res = Psbt.fromBuffer(this.toBuffer()); + res.opts = JSON.parse(JSON.stringify(this.opts)); + return res; + } setMaximumFeeRate(satoshiPerByte) { check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw this.opts.maximumFeeRate = satoshiPerByte; @@ -129,6 +135,10 @@ class Psbt extends bip174_1.Psbt { c.__EXTRACTED_TX = undefined; return this; } + addInputs(inputDatas) { + inputDatas.forEach(inputData => this.addInput(inputData)); + return this; + } addInput(inputData) { checkInputsForPartialSig(this.inputs, 'addInput'); const c = this.__CACHE; @@ -138,6 +148,10 @@ class Psbt extends bip174_1.Psbt { c.__EXTRACTED_TX = undefined; return this; } + addOutputs(outputDatas) { + outputDatas.forEach(outputData => this.addOutput(outputData)); + return this; + } addOutput(outputData) { checkInputsForPartialSig(this.inputs, 'addOutput'); const { address } = outputData; diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index a7398a8..96ae073 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -1,17 +1,19 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const regtestUtils = require('./_regtest') -const regtest = regtestUtils.network +const { describe, it } = require('mocha'); +const assert = require('assert'); +const bitcoin = require('../../'); +const regtestUtils = require('./_regtest'); +const regtest = regtestUtils.network; // See bottom of file for some helper functions used to make the payment objects needed. describe('bitcoinjs-lib (transactions with psbt)', () => { it('can create a 1-to-1 Transaction', () => { - const alice = bitcoin.ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr') - const psbt = new bitcoin.Psbt() - psbt.setVersion(2) // These are defaults. This line is not needed. - psbt.setLocktime(0) // These are defaults. This line is not needed. + const alice = bitcoin.ECPair.fromWIF( + 'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr', + ); + const psbt = new bitcoin.Psbt(); + psbt.setVersion(2); // These are defaults. This line is not needed. + psbt.setLocktime(0); // These are defaults. This line is not needed. psbt.addInput({ // if hash is string, txid, if hash is Buffer, is reversed compared to txid hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e', @@ -21,18 +23,18 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // non-segwit inputs now require passing the whole previous tx as Buffer nonWitnessUtxo: Buffer.from( '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' + - '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + - 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + - '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + - '631e5e1e66009ce3710ceea5b1ad13ffffffff01' + - // value in satoshis (Int64LE) = 0x015f90 = 90000 - '905f010000000000' + - // scriptPubkey length - '19' + - // scriptPubkey - '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' + - // locktime - '00000000', + '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + + 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + + '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + + '631e5e1e66009ce3710ceea5b1ad13ffffffff01' + + // value in satoshis (Int64LE) = 0x015f90 = 90000 + '905f010000000000' + + // scriptPubkey length + '19' + + // scriptPubkey + '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' + + // locktime + '00000000', 'hex', ), @@ -47,40 +49,50 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // }, // Not featured here: redeemScript. A Buffer of the redeemScript - }) + }); psbt.addOutput({ address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', - value: 80000 - }) - psbt.signInput(0, alice) - psbt.validateSignatures(0) - psbt.finalizeAllInputs() + value: 80000, + }); + psbt.signInput(0, alice); + psbt.validateSignatures(0); + psbt.finalizeAllInputs(); assert.strictEqual( psbt.extractTransaction().toHex(), '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + - 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + - 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + - '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + - 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + - '08a22724efa6f6a07b0ec4c79aa88ac00000000', - ) - }) + 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + + 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + + '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + + 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + + '08a22724efa6f6a07b0ec4c79aa88ac00000000', + ); + }); it('can create (and broadcast via 3PBP) a typical Transaction', async () => { // these are { payment: Payment; keys: ECPair[] } - const alice1 = createPayment('p2pkh') - const alice2 = createPayment('p2pkh') + const alice1 = createPayment('p2pkh'); + const alice2 = createPayment('p2pkh'); // give Alice 2 unspent outputs - const inputData1 = await getInputData(5e4, alice1.payment, false, 'noredeem') - const inputData2 = await getInputData(7e4, alice2.payment, false, 'noredeem') + const inputData1 = await getInputData( + 5e4, + alice1.payment, + false, + 'noredeem', + ); + const inputData2 = await getInputData( + 7e4, + alice2.payment, + false, + 'noredeem', + ); { const { hash, // string of txid or Buffer of tx hash. (txid and hash are reverse order) index, // the output index of the txo you are spending nonWitnessUtxo, // the full previous transaction as a Buffer - } = inputData1 - assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1) + } = inputData1; + assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1); } // network is only needed if you pass an address to addOutput @@ -90,12 +102,12 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .addInput(inputData2) // alice2 unspent .addOutput({ address: 'mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', - value: 8e4 + value: 8e4, }) // the actual "spend" .addOutput({ address: alice2.payment.address, // OR script, which is a Buffer. - value: 1e4 - }) // Alice's change + value: 1e4, + }); // Alice's change // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee // Let's show a new feature with PSBT. @@ -103,231 +115,248 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // (this is not necessary, but a nice feature) // encode to send out to the signers - const psbtBaseText = psbt.toBase64() + const psbtBaseText = psbt.toBase64(); // each signer imports - const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText) - const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText) + const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText); + const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText); // Alice signs each input with the respective private keys // signInput and signInputAsync are better // (They take the input index explicitly as the first arg) - signer1.sign(alice1.keys[0]) - signer2.sign(alice2.keys[0]) + signer1.sign(alice1.keys[0]); + signer2.sign(alice2.keys[0]); // If your signer object's sign method returns a promise, use the following // await signer2.signAsync(alice2.keys[0]) // encode to send back to combiner (signer 1 and 2 are not near each other) - const s1text = signer1.toBase64() - const s2text = signer2.toBase64() + const s1text = signer1.toBase64(); + const s2text = signer2.toBase64(); - const final1 = bitcoin.Psbt.fromBase64(s1text) - const final2 = bitcoin.Psbt.fromBase64(s2text) + const final1 = bitcoin.Psbt.fromBase64(s1text); + const final2 = bitcoin.Psbt.fromBase64(s2text); // final1.combine(final2) would give the exact same result - psbt.combine(final1, final2) + psbt.combine(final1, final2); // Finalizer wants to check all signatures are valid before finalizing. // If the finalizer wants to check for specific pubkeys, the second arg // can be passed. See the first multisig example below. - assert.strictEqual(psbt.validateSignatures(0), true) - assert.strictEqual(psbt.validateSignatures(1), true) + assert.strictEqual(psbt.validateSignatures(0), true); + assert.strictEqual(psbt.validateSignatures(1), true); // This step it new. Since we separate the signing operation and // the creation of the scriptSig and witness stack, we are able to - psbt.finalizeAllInputs() + psbt.finalizeAllInputs(); // it returns an array of the success of each input, also a result attribute // which is true if all array items are true. // build and broadcast our RegTest network - await regtestUtils.broadcast(psbt.extractTransaction().toHex()) + await regtestUtils.broadcast(psbt.extractTransaction().toHex()); // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 - }) + }); it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => { - const alice1 = createPayment('p2pkh') - const inputData1 = await getInputData(2e5, alice1.payment, false, 'noredeem') + const alice1 = createPayment('p2pkh'); + const inputData1 = await getInputData( + 2e5, + alice1.payment, + false, + 'noredeem', + ); - const data = Buffer.from('bitcoinjs-lib', 'utf8') - const embed = bitcoin.payments.embed({ data: [data] }) + const data = Buffer.from('bitcoinjs-lib', 'utf8'); + const embed = bitcoin.payments.embed({ data: [data] }); const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData1) .addOutput({ script: embed.output, - value: 1000 + value: 1000, }) .addOutput({ address: regtestUtils.RANDOM_ADDRESS, - value: 1e5 + value: 1e5, }) - .signInput(0, alice1.keys[0]) + .signInput(0, alice1.keys[0]); - assert.strictEqual(psbt.validateSignatures(0), true) - psbt.finalizeAllInputs() + assert.strictEqual(psbt.validateSignatures(0), true); + psbt.finalizeAllInputs(); // build and broadcast to the RegTest network - await regtestUtils.broadcast(psbt.extractTransaction().toHex()) - }) + await regtestUtils.broadcast(psbt.extractTransaction().toHex()); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => { - const multisig = createPayment('p2sh-p2ms(2 of 4)') - const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh') + const multisig = createPayment('p2sh-p2ms(2 of 4)'); + const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh'); { const { hash, index, nonWitnessUtxo, redeemScript, // NEW: P2SH needs to give redeemScript when adding an input. - } = inputData1 - assert.deepStrictEqual({ hash, index, nonWitnessUtxo, redeemScript }, inputData1) + } = inputData1; + assert.deepStrictEqual( + { hash, index, nonWitnessUtxo, redeemScript }, + inputData1, + ); } const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData1) .addOutput({ address: regtestUtils.RANDOM_ADDRESS, - value: 1e4 + value: 1e4, }) .signInput(0, multisig.keys[0]) - .signInput(0, multisig.keys[2]) + .signInput(0, multisig.keys[2]); - assert.strictEqual(psbt.validateSignatures(0), true) - assert.strictEqual(psbt.validateSignatures(0, multisig.keys[0].publicKey), true) + assert.strictEqual(psbt.validateSignatures(0), true); + assert.strictEqual( + psbt.validateSignatures(0, multisig.keys[0].publicKey), + true, + ); assert.throws(() => { - psbt.validateSignatures(0, multisig.keys[3].publicKey) - }, new RegExp('No signatures for this pubkey')) - psbt.finalizeAllInputs() + psbt.validateSignatures(0, multisig.keys[3].publicKey); + }, new RegExp('No signatures for this pubkey')); + psbt.finalizeAllInputs(); - const tx = psbt.extractTransaction() + const tx = psbt.extractTransaction(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 1e4 - }) - }) + value: 1e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => { - const p2sh = createPayment('p2sh-p2wpkh') - const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh') + const p2sh = createPayment('p2sh-p2wpkh'); + const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh'); + const inputData2 = await getInputData(5e4, p2sh.payment, true, 'p2sh'); { const { hash, index, witnessUtxo, // NEW: this is an object of the output being spent { script: Buffer; value: Satoshis; } redeemScript, - } = inputData - assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript }, inputData) + } = inputData; + assert.deepStrictEqual( + { hash, index, witnessUtxo, redeemScript }, + inputData, + ); } - const keyPair = p2sh.keys[0] + const keyPair = p2sh.keys[0]; const outputData = { script: p2sh.payment.output, // sending to myself for fun - value: 2e4 - } + value: 2e4, + }; + const outputData2 = { + script: p2sh.payment.output, // sending to myself for fun + value: 7e4, + }; const tx = new bitcoin.Psbt() - .addInput(inputData) - .addOutput(outputData) + .addInputs([inputData, inputData2]) + .addOutputs([outputData, outputData2]) .sign(keyPair) .finalizeAllInputs() - .extractTransaction() + .extractTransaction(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: p2sh.payment.address, vout: 0, - value: 2e4 - }) - }) + value: 2e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { - // the only thing that changes is you don't give a redeemscript for input data - const p2wpkh = createPayment('p2wpkh') - const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem') + const p2wpkh = createPayment('p2wpkh'); + const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem'); { - const { - hash, - index, - witnessUtxo, - } = inputData - assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData) + const { hash, index, witnessUtxo } = inputData; + assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData); } const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ address: regtestUtils.RANDOM_ADDRESS, - value: 2e4 + value: 2e4, }) - .signInput(0, p2wpkh.keys[0]) + .signInput(0, p2wpkh.keys[0]); - assert.strictEqual(psbt.validateSignatures(0), true) - psbt.finalizeAllInputs() + assert.strictEqual(psbt.validateSignatures(0), true); + psbt.finalizeAllInputs(); - const tx = psbt.extractTransaction() + const tx = psbt.extractTransaction(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 2e4 - }) - }) + value: 2e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => { - const p2wsh = createPayment('p2wsh-p2pk') - const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh') + const p2wsh = createPayment('p2wsh-p2pk'); + const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh'); { const { hash, index, witnessUtxo, witnessScript, // NEW: A Buffer of the witnessScript - } = inputData - assert.deepStrictEqual({ hash, index, witnessUtxo, witnessScript }, inputData) + } = inputData; + assert.deepStrictEqual( + { hash, index, witnessUtxo, witnessScript }, + inputData, + ); } const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ address: regtestUtils.RANDOM_ADDRESS, - value: 2e4 + value: 2e4, }) - .signInput(0, p2wsh.keys[0]) + .signInput(0, p2wsh.keys[0]); - assert.strictEqual(psbt.validateSignatures(0), true) - psbt.finalizeAllInputs() + assert.strictEqual(psbt.validateSignatures(0), true); + psbt.finalizeAllInputs(); - const tx = psbt.extractTransaction() + const tx = psbt.extractTransaction(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 2e4 - }) - }) + value: 2e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { - const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)') - const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh') + const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); + const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh'); { const { hash, @@ -335,54 +364,60 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { witnessUtxo, redeemScript, witnessScript, - } = inputData - assert.deepStrictEqual({ hash, index, witnessUtxo, redeemScript, witnessScript }, inputData) + } = inputData; + assert.deepStrictEqual( + { hash, index, witnessUtxo, redeemScript, witnessScript }, + inputData, + ); } const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ address: regtestUtils.RANDOM_ADDRESS, - value: 2e4 + value: 2e4, }) .signInput(0, p2sh.keys[0]) .signInput(0, p2sh.keys[2]) - .signInput(0, p2sh.keys[3]) + .signInput(0, p2sh.keys[3]); - assert.strictEqual(psbt.validateSignatures(0), true) - assert.strictEqual(psbt.validateSignatures(0, p2sh.keys[3].publicKey), true) + assert.strictEqual(psbt.validateSignatures(0), true); + assert.strictEqual( + psbt.validateSignatures(0, p2sh.keys[3].publicKey), + true, + ); assert.throws(() => { - psbt.validateSignatures(0, p2sh.keys[1].publicKey) - }, new RegExp('No signatures for this pubkey')) - psbt.finalizeAllInputs() + psbt.validateSignatures(0, p2sh.keys[1].publicKey); + }, new RegExp('No signatures for this pubkey')); + psbt.finalizeAllInputs(); - const tx = psbt.extractTransaction() + const tx = psbt.extractTransaction(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 2e4 - }) - }) -}) + value: 2e4, + }); + }); +}); function createPayment(_type, network) { - network = network || regtest + network = network || regtest; const splitType = _type.split('-').reverse(); const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; const keys = []; let m; if (isMultisig) { - const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/) - m = parseInt(match[1]) - let n = parseInt(match[2]) + const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/); + m = parseInt(match[1]); + let n = parseInt(match[2]); while (n > 1) { keys.push(bitcoin.ECPair.makeRandom({ network })); - n-- + n--; } } keys.push(bitcoin.ECPair.makeRandom({ network })); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 61ed15e..bfe340c 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -130,6 +130,13 @@ export class Psbt extends PsbtBase { return this.inputs.length; } + clone(): Psbt { + // TODO: more efficient cloning + const res = Psbt.fromBuffer(this.toBuffer()); + res.opts = JSON.parse(JSON.stringify(this.opts)); + return res; + } + setMaximumFeeRate(satoshiPerByte: number): void { check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw this.opts.maximumFeeRate = satoshiPerByte; @@ -168,6 +175,11 @@ export class Psbt extends PsbtBase { return this; } + addInputs(inputDatas: TransactionInput[]): this { + inputDatas.forEach(inputData => this.addInput(inputData)); + return this; + } + addInput(inputData: TransactionInput): this { checkInputsForPartialSig(this.inputs, 'addInput'); const c = this.__CACHE; @@ -178,6 +190,11 @@ export class Psbt extends PsbtBase { return this; } + addOutputs(outputDatas: TransactionOutput[]): this { + outputDatas.forEach(outputData => this.addOutput(outputData)); + return this; + } + addOutput(outputData: TransactionOutput): this { checkInputsForPartialSig(this.inputs, 'addOutput'); const { address } = outputData as any; diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 40571fa..10617c2 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -11,11 +11,14 @@ export declare class Psbt extends PsbtBase { private opts; constructor(opts?: PsbtOptsOptional); readonly inputCount: number; + clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void; setVersion(version: number): this; setLocktime(locktime: number): this; setSequence(inputIndex: number, sequence: number): this; + addInputs(inputDatas: TransactionInput[]): this; addInput(inputData: TransactionInput): this; + addOutputs(outputDatas: TransactionOutput[]): this; addOutput(outputData: TransactionOutput): this; addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this; extractTransaction(disableFeeCheck?: boolean): Transaction; From 75f5e8f03c5871c6d80a8f4c4e09ea00da8875d3 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 9 Jul 2019 14:35:40 +0700 Subject: [PATCH 394/568] Use Buffer notation in JSON --- test/fixtures/psbt.json | 36 ++++++++++++++++---------------- test/psbt.js | 46 ++++++++++++++--------------------------- 2 files changed, 34 insertions(+), 48 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 59a74cd..0525bf6 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -174,37 +174,37 @@ "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=", "inputData": [ { - "nonWitnessUtxo": "0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000", - "redeemScript": "5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae", + "nonWitnessUtxo": "Buffer.from('0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000', 'hex')", + "redeemScript": "Buffer.from('5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae', 'hex')", "bip32Derivation": [ { - "masterFingerprint": "d90c6a4f", - "pubkey": "029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f", + "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')", + "pubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f', 'hex')", "path": "m/0'/0'/0'" }, { - "masterFingerprint": "d90c6a4f", - "pubkey": "02dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7", + "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')", + "pubkey": "Buffer.from('02dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7', 'hex')", "path": "m/0'/0'/1'" } ] }, { "witnessUtxo": { - "script": "a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887", + "script": "Buffer.from('a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887', 'hex')", "value": 200000000 }, - "redeemScript": "00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903", - "witnessScript": "522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae", + "redeemScript": "Buffer.from('00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903', 'hex')", + "witnessScript": "Buffer.from('522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae', 'hex')", "bip32Derivation": [ { - "masterFingerprint": "d90c6a4f", - "pubkey": "023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73", + "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')", + "pubkey": "Buffer.from('023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73', 'hex')", "path": "m/0'/0'/3'" }, { - "masterFingerprint": "d90c6a4f", - "pubkey": "03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc", + "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')", + "pubkey": "Buffer.from('03089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc', 'hex')", "path": "m/0'/0'/2'" } ] @@ -214,8 +214,8 @@ { "bip32Derivation": [ { - "masterFingerprint": "d90c6a4f", - "pubkey": "03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58771", + "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')", + "pubkey": "Buffer.from('03a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58771', 'hex')", "path": "m/0'/0'/4'" } ] @@ -223,8 +223,8 @@ { "bip32Derivation": [ { - "masterFingerprint": "d90c6a4f", - "pubkey": "027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b50051096", + "masterFingerprint": "Buffer.from('d90c6a4f', 'hex')", + "pubkey": "Buffer.from('027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b50051096', 'hex')", "path": "m/0'/0'/5'" } ] @@ -316,7 +316,7 @@ { "description": "checks for hash and index", "inputData": { - "hash": "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", + "hash": "Buffer.from('000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f', 'hex')", "index": 2 }, "equals": "cHNidP8BADMCAAAAAQABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4PAgAAAAD/////AAAAAAAAAAA=" diff --git a/test/psbt.js b/test/psbt.js index 657a67a..25bbfa9 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -5,37 +5,23 @@ const ECPair = require('../src/ecpair') const Psbt = require('..').Psbt const NETWORKS = require('../src/networks') -const fixtures = require('./fixtures/psbt') +const initBuffers = object => JSON.parse(JSON.stringify(object), (key, value) => { + const regex = new RegExp(/^Buffer.from\(['"](.*)['"], ['"](.*)['"]\)$/) + const result = regex.exec(value) + if (!result) return value + + const data = result[1] + const encoding = result[2] + + return Buffer.from(data, encoding) +}) + +const fixtures = initBuffers(require('./fixtures/psbt')) const upperCaseFirstLetter = str => str.replace(/^./, s => s.toUpperCase()) const b = hex => Buffer.from(hex, 'hex'); -const initBuffers = (attr, data) => { - if ([ - 'nonWitnessUtxo', - 'redeemScript', - 'witnessScript' - ].includes(attr)) { - data = b(data) - } else if (attr === 'bip32Derivation') { - data.masterFingerprint = b(data.masterFingerprint) - data.pubkey = b(data.pubkey) - } else if (attr === 'witnessUtxo') { - data.script = b(data.script) - } else if (attr === 'hash') { - if ( - typeof data === 'string' && - data.match(/^[0-9a-f]*$/i) && - data.length % 2 === 0 - ) { - data = b(data) - } - } - - return data -}; - describe(`Psbt`, () => { describe('BIP174 Test Vectors', () => { fixtures.bip174.invalid.forEach(f => { @@ -94,9 +80,9 @@ describe(`Psbt`, () => { adder = adder.bind(psbt) const arg = data[attr] if (Array.isArray(arg)) { - arg.forEach(a => adder(i, initBuffers(attr, a))) + arg.forEach(a => adder(i, a)) } else { - adder(i, initBuffers(attr, arg)) + adder(i, arg) if (attr === 'nonWitnessUtxo') { const first = psbt.inputs[i].nonWitnessUtxo psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined @@ -320,7 +306,7 @@ describe(`Psbt`, () => { describe('addInput', () => { fixtures.addInput.checks.forEach(f => { for (const attr of Object.keys(f.inputData)) { - f.inputData[attr] = initBuffers(attr, f.inputData[attr]) + f.inputData[attr] = f.inputData[attr] } it(f.description, () => { const psbt = new Psbt() @@ -349,7 +335,7 @@ describe(`Psbt`, () => { describe('addOutput', () => { fixtures.addOutput.checks.forEach(f => { for (const attr of Object.keys(f.outputData)) { - f.outputData[attr] = initBuffers(attr, f.outputData[attr]) + f.outputData[attr] = f.outputData[attr] } it(f.description, () => { const psbt = new Psbt() From 0d9fa87943e23013aa9d76162afc36ed4a676435 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 9 Jul 2019 16:21:00 +0700 Subject: [PATCH 395/568] Move nonWitnessUtxo cache tests out into own test --- test/fixtures/psbt.json | 9 ++++++++- test/psbt.js | 33 +++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 0525bf6..9e85b63 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -428,5 +428,12 @@ "transaction": "020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000", "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=" } - ] + ], + "cache": { + "nonWitnessUtxo": { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuIgYClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8Q2QxqTwAAAIAAAACAAAAAgCIGAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXENkMak8AAACAAAAAgAEAAIAAAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", + "nonWitnessUtxo": "Buffer.from('0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000', 'hex')", + "inputIndex": 0 + } + } } diff --git a/test/psbt.js b/test/psbt.js index 25bbfa9..ce92dc6 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -83,16 +83,6 @@ describe(`Psbt`, () => { arg.forEach(a => adder(i, a)) } else { adder(i, arg) - if (attr === 'nonWitnessUtxo') { - const first = psbt.inputs[i].nonWitnessUtxo - psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined - const second = psbt.inputs[i].nonWitnessUtxo - psbt.inputs[i].nonWitnessUtxo = Buffer.from([1,2,3]) - psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[i] = undefined - const third = psbt.inputs[i].nonWitnessUtxo - assert.ok(first.equals(second)) - assert.ok(first.equals(third)) - } } } } @@ -473,4 +463,27 @@ describe(`Psbt`, () => { assert.ok(psbt.__CACHE.__TX); }) }) + + describe('Cache', () => { + it('non-witness UTXOs are cached', () => { + const f = fixtures.cache.nonWitnessUtxo; + const psbt = Psbt.fromBase64(f.psbt) + const index = f.inputIndex; + + // Cache is empty + assert.strictEqual(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index], undefined) + + // Cache is populated + psbt.addNonWitnessUtxoToInput(index, f.nonWitnessUtxo) + const value = psbt.inputs[index].nonWitnessUtxo + assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value)) + assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo)) + + // Cache is rebuilt from internal transaction object when cleared + psbt.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3]) + psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined + assert.ok(psbt.inputs[index].nonWitnessUtxo.equals(value)) + }) + }) }) + From fa897cf78e48942aebb8cfcc308e3af0be22490d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 10 Jul 2019 10:19:26 +0900 Subject: [PATCH 396/568] Check signatures for sighash type before finalize --- src/psbt.js | 11 +++++++++++ ts_src/psbt.ts | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index 33a32a3..f352a8b 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -216,6 +216,7 @@ class Psbt extends bip174_1.Psbt { const scriptType = classifyScript(script); if (!canFinalize(input, script, scriptType)) throw new Error(`Can not finalize input #${inputIndex}`); + checkPartialSigSighashes(input); const { finalScriptSig, finalScriptWitness } = getFinalScripts( script, scriptType, @@ -446,6 +447,16 @@ function checkInputsForPartialSig(inputs, action) { } }); } +function checkPartialSigSighashes(input) { + if (!input.sighashType || !input.partialSig) return; + const { partialSig, sighashType } = input; + partialSig.forEach(pSig => { + const { hashType } = bscript.signature.decode(pSig.signature); + if (sighashType !== hashType) { + throw new Error('Signature sighash does not match input sighash type'); + } + }); +} function checkScriptForPubkey(pubkey, script, action) { const pubkeyHash = crypto_1.hash160(pubkey); const decompiled = bscript.decompile(script); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index bfe340c..acb35f6 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -269,6 +269,8 @@ export class Psbt extends PsbtBase { if (!canFinalize(input, script, scriptType)) throw new Error(`Can not finalize input #${inputIndex}`); + checkPartialSigSighashes(input); + const { finalScriptSig, finalScriptWitness } = getFinalScripts( script, scriptType, @@ -547,6 +549,17 @@ function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void { }); } +function checkPartialSigSighashes(input: PsbtInput): void { + if (!input.sighashType || !input.partialSig) return; + const { partialSig, sighashType } = input; + partialSig.forEach(pSig => { + const { hashType } = bscript.signature.decode(pSig.signature); + if (sighashType !== hashType) { + throw new Error('Signature sighash does not match input sighash type'); + } + }); +} + function checkScriptForPubkey( pubkey: Buffer, script: Buffer, From ccab2652f92329dd6a01ffed4ccbd96e5152acf1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 10 Jul 2019 11:15:12 +0900 Subject: [PATCH 397/568] Add sighash checks for signer --- src/psbt.js | 65 ++++++++++++++++++++++++++++++++++++++++++------- ts_src/psbt.ts | 64 ++++++++++++++++++++++++++++++++++++++++++------ types/psbt.d.ts | 8 +++--- 3 files changed, 117 insertions(+), 20 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index f352a8b..c7e8b55 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -273,7 +273,7 @@ class Psbt extends bip174_1.Psbt { } return results.every(res => res === true); } - sign(keyPair) { + sign(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); // TODO: Add a pubkey/pubkeyhash cache to each input @@ -282,7 +282,7 @@ class Psbt extends bip174_1.Psbt { const results = []; for (const i of range(this.inputs.length)) { try { - this.signInput(i, keyPair); + this.signInput(i, keyPair, sighashTypes); results.push(true); } catch (err) { results.push(false); @@ -293,7 +293,7 @@ class Psbt extends bip174_1.Psbt { } return this; } - signAsync(keyPair) { + signAsync(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) { return new Promise((resolve, reject) => { if (!keyPair || !keyPair.publicKey) return reject(new Error('Need Signer to sign input')); @@ -304,7 +304,7 @@ class Psbt extends bip174_1.Psbt { const promises = []; for (const [i] of this.inputs.entries()) { promises.push( - this.signInputAsync(i, keyPair).then( + this.signInputAsync(i, keyPair, sighashTypes).then( () => { results.push(true); }, @@ -322,7 +322,11 @@ class Psbt extends bip174_1.Psbt { }); }); } - signInput(inputIndex, keyPair) { + signInput( + inputIndex, + keyPair, + sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], + ) { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); const { hash, sighashType } = getHashAndSighashType( @@ -330,6 +334,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, keyPair.publicKey, this.__CACHE, + sighashTypes, ); const partialSig = { pubkey: keyPair.publicKey, @@ -337,7 +342,11 @@ class Psbt extends bip174_1.Psbt { }; return this.addPartialSigToInput(inputIndex, partialSig); } - signInputAsync(inputIndex, keyPair) { + signInputAsync( + inputIndex, + keyPair, + sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], + ) { return new Promise((resolve, reject) => { if (!keyPair || !keyPair.publicKey) return reject(new Error('Need Signer to sign input')); @@ -346,6 +355,7 @@ class Psbt extends bip174_1.Psbt { inputIndex, keyPair.publicKey, this.__CACHE, + sighashTypes, ); Promise.resolve(keyPair.sign(hash)).then(signature => { const partialSig = { @@ -548,19 +558,37 @@ function getFinalScripts( finalScriptWitness, }; } -function getHashAndSighashType(inputs, inputIndex, pubkey, cache) { +function getHashAndSighashType( + inputs, + inputIndex, + pubkey, + cache, + sighashTypes, +) { const input = utils_1.checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache); + const { hash, sighashType, script } = getHashForSig( + inputIndex, + input, + cache, + sighashTypes, + ); checkScriptForPubkey(pubkey, script, 'sign'); return { hash, sighashType, }; } -function getHashForSig(inputIndex, input, cache) { +function getHashForSig(inputIndex, input, cache, sighashTypes) { const unsignedTx = cache.__TX; const sighashType = input.sighashType || transaction_1.Transaction.SIGHASH_ALL; + if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) { + const str = sighashTypeToString(sighashType); + throw new Error( + `Sighash type is not allowed. Retry the sign method passing the ` + + `sighashTypes array of whitelisted types. Sighash type: ${str}`, + ); + } let hash; let script; if (input.nonWitnessUtxo) { @@ -800,6 +828,25 @@ function scriptWitnessToWitnessStack(buffer) { } return readVector(); } +function sighashTypeToString(sighashType) { + let text = + sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY + ? 'SIGHASH_ANYONECANPAY | ' + : ''; + const sigMod = sighashType & 0x1f; + switch (sigMod) { + case transaction_1.Transaction.SIGHASH_ALL: + text += 'SIGHASH_ALL'; + break; + case transaction_1.Transaction.SIGHASH_SINGLE: + text += 'SIGHASH_SINGLE'; + break; + case transaction_1.Transaction.SIGHASH_NONE: + text += 'SIGHASH_NONE'; + break; + } + return text; +} function witnessStackToScriptWitness(witness) { let buffer = Buffer.allocUnsafe(0); function writeSlice(slice) { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index acb35f6..8fb1e97 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -332,7 +332,10 @@ export class Psbt extends PsbtBase { return results.every(res => res === true); } - sign(keyPair: Signer): this { + sign( + keyPair: Signer, + sighashTypes: number[] = [Transaction.SIGHASH_ALL], + ): this { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); @@ -342,7 +345,7 @@ export class Psbt extends PsbtBase { const results: boolean[] = []; for (const i of range(this.inputs.length)) { try { - this.signInput(i, keyPair); + this.signInput(i, keyPair, sighashTypes); results.push(true); } catch (err) { results.push(false); @@ -354,7 +357,10 @@ export class Psbt extends PsbtBase { return this; } - signAsync(keyPair: SignerAsync): Promise<void> { + signAsync( + keyPair: SignerAsync, + sighashTypes: number[] = [Transaction.SIGHASH_ALL], + ): Promise<void> { return new Promise( (resolve, reject): any => { if (!keyPair || !keyPair.publicKey) @@ -367,7 +373,7 @@ export class Psbt extends PsbtBase { const promises: Array<Promise<void>> = []; for (const [i] of this.inputs.entries()) { promises.push( - this.signInputAsync(i, keyPair).then( + this.signInputAsync(i, keyPair, sighashTypes).then( () => { results.push(true); }, @@ -387,7 +393,11 @@ export class Psbt extends PsbtBase { ); } - signInput(inputIndex: number, keyPair: Signer): this { + signInput( + inputIndex: number, + keyPair: Signer, + sighashTypes: number[] = [Transaction.SIGHASH_ALL], + ): this { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); const { hash, sighashType } = getHashAndSighashType( @@ -395,6 +405,7 @@ export class Psbt extends PsbtBase { inputIndex, keyPair.publicKey, this.__CACHE, + sighashTypes, ); const partialSig = { @@ -405,7 +416,11 @@ export class Psbt extends PsbtBase { return this.addPartialSigToInput(inputIndex, partialSig); } - signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void> { + signInputAsync( + inputIndex: number, + keyPair: SignerAsync, + sighashTypes: number[] = [Transaction.SIGHASH_ALL], + ): Promise<void> { return new Promise( (resolve, reject): void => { if (!keyPair || !keyPair.publicKey) @@ -415,6 +430,7 @@ export class Psbt extends PsbtBase { inputIndex, keyPair.publicKey, this.__CACHE, + sighashTypes, ); Promise.resolve(keyPair.sign(hash)).then(signature => { @@ -683,12 +699,18 @@ function getHashAndSighashType( inputIndex: number, pubkey: Buffer, cache: PsbtCache, + sighashTypes: number[], ): { hash: Buffer; sighashType: number; } { const input = checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache); + const { hash, sighashType, script } = getHashForSig( + inputIndex, + input, + cache, + sighashTypes, + ); checkScriptForPubkey(pubkey, script, 'sign'); return { hash, @@ -700,6 +722,7 @@ function getHashForSig( inputIndex: number, input: PsbtInput, cache: PsbtCache, + sighashTypes?: number[], ): { script: Buffer; hash: Buffer; @@ -707,6 +730,13 @@ function getHashForSig( } { const unsignedTx = cache.__TX; const sighashType = input.sighashType || Transaction.SIGHASH_ALL; + if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) { + const str = sighashTypeToString(sighashType); + throw new Error( + `Sighash type is not allowed. Retry the sign method passing the ` + + `sighashTypes array of whitelisted types. Sighash type: ${str}`, + ); + } let hash: Buffer; let script: Buffer; @@ -981,6 +1011,26 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] { return readVector(); } +function sighashTypeToString(sighashType: number): string { + let text = + sighashType & Transaction.SIGHASH_ANYONECANPAY + ? 'SIGHASH_ANYONECANPAY | ' + : ''; + const sigMod = sighashType & 0x1f; + switch (sigMod) { + case Transaction.SIGHASH_ALL: + text += 'SIGHASH_ALL'; + break; + case Transaction.SIGHASH_SINGLE: + text += 'SIGHASH_SINGLE'; + break; + case Transaction.SIGHASH_NONE: + text += 'SIGHASH_NONE'; + break; + } + return text; +} + function witnessStackToScriptWitness(witness: Buffer[]): Buffer { let buffer = Buffer.allocUnsafe(0); diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 10617c2..95f6624 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -27,10 +27,10 @@ export declare class Psbt extends PsbtBase { finalizeInput(inputIndex: number): this; validateAllSignatures(): boolean; validateSignatures(inputIndex: number, pubkey?: Buffer): boolean; - sign(keyPair: Signer): this; - signAsync(keyPair: SignerAsync): Promise<void>; - signInput(inputIndex: number, keyPair: Signer): this; - signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>; + sign(keyPair: Signer, sighashTypes?: number[]): this; + signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>; + signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this; + signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>; } interface PsbtOptsOptional { network?: Network; From a50ec330334d214c7bc1099912dd0d58715d5032 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 10 Jul 2019 16:43:51 +0900 Subject: [PATCH 398/568] Update dep --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 768c79f..fce68c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.13.tgz", - "integrity": "sha512-jWP7Lb27Nmbk6gaZKhJZOyk5LqRWs9z+R2xzgu3W8/iZXIIP2kcR6fh5lNg7GGOiWUaqanWC9rjrDVrBVbXKww==" + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.14.tgz", + "integrity": "sha512-v9cre0W4ZpAJS1v18WUJLE9yKdSZyenGpZBg7CXiZ5n35JPXganH92d4Yk8WXpRfbFZ4SMXTqKLEgpLPX1TmcA==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index 1fadfcb..03e6d5f 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.13", + "bip174": "0.0.14", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", From da5adcf88f90a00981a5ce0b0a54963173081731 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 10 Jul 2019 17:59:11 +0700 Subject: [PATCH 399/568] Refactor and cleanup validateSignatures tests --- test/fixtures/psbt.json | 7 +++++++ test/psbt.js | 34 ++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 9e85b63..c6eabd9 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -429,6 +429,13 @@ "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=" } ], + "validateSignatures": { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", + "index": 0, + "pubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f', 'hex')", + "incorrectPubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e02a', 'hex')", + "nonExistantIndex": 42 + }, "cache": { "nonWitnessUtxo": { "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuIgYClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8Q2QxqTwAAAIAAAACAAAAAgCIGAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXENkMak8AAACAAAAAgAEAAIAAAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", diff --git a/test/psbt.js b/test/psbt.js index ce92dc6..94136af 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -130,20 +130,6 @@ describe(`Psbt`, () => { psbt.getFeeRate() }, new RegExp('PSBT must be finalized to calculate fee rate')) - const pubkey = Buffer.from( - '029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f', - 'hex', - ) - assert.strictEqual(psbt.validateSignatures(0), true) - assert.strictEqual(psbt.validateSignatures(0, pubkey), true) - assert.throws(() => { - pubkey[32] = 42 - psbt.validateSignatures(0, pubkey) - }, new RegExp('No signatures for this pubkey')) - assert.throws(() => { - psbt.validateSignatures(42) - }, new RegExp('No signatures to validate')) - psbt.finalizeAllInputs() assert.strictEqual(psbt.toBase64(), f.result) @@ -401,6 +387,26 @@ describe(`Psbt`, () => { }) }) + describe('validateSignatures', () => { + const f = fixtures.validateSignatures + it('Correctly validates a signature', () => { + const psbt = Psbt.fromBase64(f.psbt) + + assert.strictEqual(psbt.validateSignatures(f.index), true) + assert.throws(() => { + psbt.validateSignatures(f.nonExistantIndex) + }, new RegExp('No signatures to validate')) + }) + + it('Correctly validates a signature against a pubkey', () => { + const psbt = Psbt.fromBase64(f.psbt) + assert.strictEqual(psbt.validateSignatures(f.index, f.pubkey), true) + assert.throws(() => { + psbt.validateSignatures(f.index, f.incorrectPubkey) + }, new RegExp('No signatures for this pubkey')) + }) + }) + describe('create 1-to-1 transaction', () => { const alice = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr') const psbt = new Psbt() From 47b42e72f4e8bc2f20807ffba4b14c6c1f3d2040 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 10 Jul 2019 18:05:30 +0700 Subject: [PATCH 400/568] Refactor and cleanup getFeeRate tests --- test/fixtures/psbt.json | 3 +++ test/psbt.js | 21 +++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index c6eabd9..13f4389 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -436,6 +436,9 @@ "incorrectPubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e02a', 'hex')", "nonExistantIndex": 42 }, + "getFeeRate": { + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" + }, "cache": { "nonWitnessUtxo": { "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEER1IhApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/IQLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU211KuIgYClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8Q2QxqTwAAAIAAAACAAAAAgCIGAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXENkMak8AAACAAAAAgAEAAIAAAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", diff --git a/test/psbt.js b/test/psbt.js index 94136af..cc72e55 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -126,10 +126,6 @@ describe(`Psbt`, () => { it('Finalizes inputs and gives the expected PSBT', () => { const psbt = Psbt.fromBase64(f.psbt) - assert.throws(() => { - psbt.getFeeRate() - }, new RegExp('PSBT must be finalized to calculate fee rate')) - psbt.finalizeAllInputs() assert.strictEqual(psbt.toBase64(), f.result) @@ -407,6 +403,23 @@ describe(`Psbt`, () => { }) }) + describe('getFeeRate', () => { + it('Throws error if called before inputs are finalized', () => { + const f = fixtures.getFeeRate + const psbt = Psbt.fromBase64(f.psbt) + + assert.throws(() => { + psbt.getFeeRate() + }, new RegExp('PSBT must be finalized to calculate fee rate')) + + psbt.finalizeAllInputs() + + assert.doesNotThrow(() => { + psbt.getFeeRate() + }) + }) + }) + describe('create 1-to-1 transaction', () => { const alice = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr') const psbt = new Psbt() From d05144627588fd98ca7562864e2d891fc7927706 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 10 Jul 2019 18:12:56 +0700 Subject: [PATCH 401/568] Add P2MS test case to finalizer tests --- test/fixtures/psbt.json | 6 ++++++ test/psbt.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 13f4389..2333d65 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -293,8 +293,14 @@ ], "finalizer": [ { + "description": "BIP174 test case", "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==" + }, + { + "description": "P2MS input", + "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=", + "result": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAABB/0gAQBIMEUCIQDqqpZ1MJnPj6imVcAADCzzQirJB4VYJNaV0fXe55gekgIgNAtuInVXbYJffrCAUfAN2WuVnLMDSd4vD1kzFkddAuEBSDBFAiEA2cMFRBaNLyENM+J1Fn3msZkrctYIshkHU1jH3754FPwCIBK6ktH4o1rVJ7t6gGRZBwOfL4FoF8xD6nVFMIBd3v8YAUyLUiECxZrqO4BvDo4JmjuQB7gv9Trw2q0Ldeke1RClPHZEUQghAuF++G/r0Rd1y4Ml8hno5hOVQdX90WPyEXlIIMjR4HS2IQPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuURCEDveDmb+sjw+JBGUmQarmAOxT7AAWWelALXlbhMO31cc5UrgAA" } ], "extractor": [ diff --git a/test/psbt.js b/test/psbt.js index cc72e55..e1840ce 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -123,7 +123,7 @@ describe(`Psbt`, () => { }) fixtures.bip174.finalizer.forEach(f => { - it('Finalizes inputs and gives the expected PSBT', () => { + it(`Finalizes inputs and gives the expected PSBT: ${f.description}`, () => { const psbt = Psbt.fromBase64(f.psbt) psbt.finalizeAllInputs() From f6ab5b796f8e74353b85dfa8ae0d6cffa90b947d Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 10 Jul 2019 18:54:05 +0700 Subject: [PATCH 402/568] Move all BIP174 test cases into BIP14 describe block --- test/psbt.js | 129 +++++++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 65 deletions(-) diff --git a/test/psbt.js b/test/psbt.js index e1840ce..9f7cc1d 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -93,74 +93,74 @@ describe(`Psbt`, () => { assert.strictEqual(psbt.toBase64(), f.result) }) }) - }) - fixtures.bip174.signer.forEach(f => { - it('Signs PSBT to the expected result', () => { - const psbt = Psbt.fromBase64(f.psbt) - - f.keys.forEach(({inputToSign, WIF}) => { - const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet); - psbt.signInput(inputToSign, keyPair); + fixtures.bip174.signer.forEach(f => { + it('Signs PSBT to the expected result', () => { + const psbt = Psbt.fromBase64(f.psbt) + + f.keys.forEach(({inputToSign, WIF}) => { + const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet); + psbt.signInput(inputToSign, keyPair); + }) + + assert.strictEqual(psbt.toBase64(), f.result) }) - - assert.strictEqual(psbt.toBase64(), f.result) }) - }) - - fixtures.bip174.combiner.forEach(f => { - it('Combines two PSBTs to the expected result', () => { - const psbts = f.psbts.map(psbt => Psbt.fromBase64(psbt)) - - psbts[0].combine(psbts[1]) - - // Produces a different Base64 string due to implemetation specific key-value ordering. - // That means this test will fail: - // assert.strictEqual(psbts[0].toBase64(), f.result) - // However, if we compare the actual PSBT properties we can see they are logically identical: - assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result)) + + fixtures.bip174.combiner.forEach(f => { + it('Combines two PSBTs to the expected result', () => { + const psbts = f.psbts.map(psbt => Psbt.fromBase64(psbt)) + + psbts[0].combine(psbts[1]) + + // Produces a different Base64 string due to implemetation specific key-value ordering. + // That means this test will fail: + // assert.strictEqual(psbts[0].toBase64(), f.result) + // However, if we compare the actual PSBT properties we can see they are logically identical: + assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result)) + }) }) - }) - - fixtures.bip174.finalizer.forEach(f => { - it(`Finalizes inputs and gives the expected PSBT: ${f.description}`, () => { - const psbt = Psbt.fromBase64(f.psbt) - - psbt.finalizeAllInputs() - - assert.strictEqual(psbt.toBase64(), f.result) + + fixtures.bip174.finalizer.forEach(f => { + it(`Finalizes inputs and gives the expected PSBT: ${f.description}`, () => { + const psbt = Psbt.fromBase64(f.psbt) + + psbt.finalizeAllInputs() + + assert.strictEqual(psbt.toBase64(), f.result) + }) }) - }) - - fixtures.bip174.extractor.forEach(f => { - it('Extracts the expected transaction from a PSBT', () => { - const psbt1 = Psbt.fromBase64(f.psbt) - const transaction1 = psbt1.extractTransaction(true).toHex() - - const psbt2 = Psbt.fromBase64(f.psbt) - const transaction2 = psbt2.extractTransaction().toHex() - - assert.strictEqual(transaction1, transaction2) - assert.strictEqual(transaction1, f.transaction) - - const psbt3 = Psbt.fromBase64(f.psbt) - delete psbt3.inputs[0].finalScriptSig - delete psbt3.inputs[0].finalScriptWitness - assert.throws(() => { - psbt3.extractTransaction() - }, new RegExp('Not finalized')) - - const psbt4 = Psbt.fromBase64(f.psbt) - psbt4.setMaximumFeeRate(1) - assert.throws(() => { - psbt4.extractTransaction() - }, new RegExp('Warning: You are paying around [\\d.]+ in fees')) - - const psbt5 = Psbt.fromBase64(f.psbt) - psbt5.extractTransaction(true) - const fr1 = psbt5.getFeeRate() - const fr2 = psbt5.getFeeRate() - assert.strictEqual(fr1, fr2) + + fixtures.bip174.extractor.forEach(f => { + it('Extracts the expected transaction from a PSBT', () => { + const psbt1 = Psbt.fromBase64(f.psbt) + const transaction1 = psbt1.extractTransaction(true).toHex() + + const psbt2 = Psbt.fromBase64(f.psbt) + const transaction2 = psbt2.extractTransaction().toHex() + + assert.strictEqual(transaction1, transaction2) + assert.strictEqual(transaction1, f.transaction) + + const psbt3 = Psbt.fromBase64(f.psbt) + delete psbt3.inputs[0].finalScriptSig + delete psbt3.inputs[0].finalScriptWitness + assert.throws(() => { + psbt3.extractTransaction() + }, new RegExp('Not finalized')) + + const psbt4 = Psbt.fromBase64(f.psbt) + psbt4.setMaximumFeeRate(1) + assert.throws(() => { + psbt4.extractTransaction() + }, new RegExp('Warning: You are paying around [\\d.]+ in fees')) + + const psbt5 = Psbt.fromBase64(f.psbt) + psbt5.extractTransaction(true) + const fr1 = psbt5.getFeeRate() + const fr2 = psbt5.getFeeRate() + assert.strictEqual(fr1, fr2) + }) }) }) @@ -504,5 +504,4 @@ describe(`Psbt`, () => { assert.ok(psbt.inputs[index].nonWitnessUtxo.equals(value)) }) }) -}) - +}) \ No newline at end of file From ec2c14b81f79ee4dfcae004535b5a35d966c3406 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 10 Jul 2019 19:20:10 +0700 Subject: [PATCH 403/568] Extract finalizeAllInputs test out of BIP174 test cases --- test/fixtures/psbt.json | 13 +++++++------ test/psbt.js | 14 +++++++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 2333d65..8092177 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -293,14 +293,8 @@ ], "finalizer": [ { - "description": "BIP174 test case", "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==" - }, - { - "description": "P2MS input", - "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=", - "result": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAABB/0gAQBIMEUCIQDqqpZ1MJnPj6imVcAADCzzQirJB4VYJNaV0fXe55gekgIgNAtuInVXbYJffrCAUfAN2WuVnLMDSd4vD1kzFkddAuEBSDBFAiEA2cMFRBaNLyENM+J1Fn3msZkrctYIshkHU1jH3754FPwCIBK6ktH4o1rVJ7t6gGRZBwOfL4FoF8xD6nVFMIBd3v8YAUyLUiECxZrqO4BvDo4JmjuQB7gv9Trw2q0Ldeke1RClPHZEUQghAuF++G/r0Rd1y4Ml8hno5hOVQdX90WPyEXlIIMjR4HS2IQPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuURCEDveDmb+sjw+JBGUmQarmAOxT7AAWWelALXlbhMO31cc5UrgAA" } ], "extractor": [ @@ -429,6 +423,13 @@ } ] }, + "finalizeAllInputs": [ + { + "type": "P2MS", + "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=", + "result": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAABB/0gAQBIMEUCIQDqqpZ1MJnPj6imVcAADCzzQirJB4VYJNaV0fXe55gekgIgNAtuInVXbYJffrCAUfAN2WuVnLMDSd4vD1kzFkddAuEBSDBFAiEA2cMFRBaNLyENM+J1Fn3msZkrctYIshkHU1jH3754FPwCIBK6ktH4o1rVJ7t6gGRZBwOfL4FoF8xD6nVFMIBd3v8YAUyLUiECxZrqO4BvDo4JmjuQB7gv9Trw2q0Ldeke1RClPHZEUQghAuF++G/r0Rd1y4Ml8hno5hOVQdX90WPyEXlIIMjR4HS2IQPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuURCEDveDmb+sjw+JBGUmQarmAOxT7AAWWelALXlbhMO31cc5UrgAA" + } + ], "fromTransaction": [ { "transaction": "020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000", diff --git a/test/psbt.js b/test/psbt.js index 9f7cc1d..6dd01a9 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -122,7 +122,7 @@ describe(`Psbt`, () => { }) fixtures.bip174.finalizer.forEach(f => { - it(`Finalizes inputs and gives the expected PSBT: ${f.description}`, () => { + it("Finalizes inputs and gives the expected PSBT", () => { const psbt = Psbt.fromBase64(f.psbt) psbt.finalizeAllInputs() @@ -266,6 +266,18 @@ describe(`Psbt`, () => { }) }) + describe('finalizeAllInputs', () => { + fixtures.finalizeAllInputs.forEach(f => { + it(`Finalizes inputs of type "${f.type}"`, () => { + const psbt = Psbt.fromBase64(f.psbt) + + psbt.finalizeAllInputs() + + assert.strictEqual(psbt.toBase64(), f.result) + }) + }) + }) + describe('fromTransaction', () => { fixtures.fromTransaction.forEach(f => { it('Creates the expected PSBT from a transaction buffer', () => { From bc56ca0fa1210d991138e78ccb8ff0781581d415 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 10 Jul 2019 19:21:33 +0700 Subject: [PATCH 404/568] Test finalizeAllInputs against P2PK input --- test/fixtures/psbt.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 8092177..ad6ffdf 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -424,6 +424,11 @@ ] }, "finalizeAllInputs": [ + { + "type": "P2PK", + "psbt": "cHNidP8BAFUCAAAAAZfdvR7JvbVsuXunG1P19tTYi8Z0l6WHiz+0jpzTNTt+AAAAAAD/////AYA4AQAAAAAAGXapFMjtQ0xCK7OPd32ZJTY2UcdpFIaJiKwAAAAAAAEAygIAAAABb7TMmNs5UwEEpccLHlaaM0w3HEHhh85NjrUObLDMuZsBAAAAa0gwRQIhANWUevBBcqfRpH67rDuFqI+KHqxViKXxmSM+6/jE5ZHjAiBgO/vQg2EsYhaOD5lQbmZYaymRLukA6SCoXhkN21hiOwEhAoKzWGkj1LJ2iOt5wj6jMOtAMGJH50ZjAYZU81uUu6y+/////wGQXwEAAAAAACMhAzpK4NeYX+xyBOKynjD3BEPDq7EN8brZXje488gxshXwrAAAAAAiAgM6SuDXmF/scgTisp4w9wRDw6uxDfG62V43uPPIMbIV8EgwRQIhAL3H/XEPRAZEbpjBwkuLqUKBSu1Inpb2rganXNFcY2JsAiASrXTM2xODEKp7m7RTzYqBchqlvbl88zO/CGW9SePj2gEAAA==", + "result": "cHNidP8BAFUCAAAAAZfdvR7JvbVsuXunG1P19tTYi8Z0l6WHiz+0jpzTNTt+AAAAAAD/////AYA4AQAAAAAAGXapFMjtQ0xCK7OPd32ZJTY2UcdpFIaJiKwAAAAAAAEAygIAAAABb7TMmNs5UwEEpccLHlaaM0w3HEHhh85NjrUObLDMuZsBAAAAa0gwRQIhANWUevBBcqfRpH67rDuFqI+KHqxViKXxmSM+6/jE5ZHjAiBgO/vQg2EsYhaOD5lQbmZYaymRLukA6SCoXhkN21hiOwEhAoKzWGkj1LJ2iOt5wj6jMOtAMGJH50ZjAYZU81uUu6y+/////wGQXwEAAAAAACMhAzpK4NeYX+xyBOKynjD3BEPDq7EN8brZXje488gxshXwrAAAAAABB0lIMEUCIQC9x/1xD0QGRG6YwcJLi6lCgUrtSJ6W9q4Gp1zRXGNibAIgEq10zNsTgxCqe5u0U82KgXIapb25fPMzvwhlvUnj49oBAAA=" + }, { "type": "P2MS", "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=", From f55ee323861bcdd25ccd8c951bf578e7e0ac3965 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 10 Jul 2019 19:21:56 +0700 Subject: [PATCH 405/568] Test finalizeAllInputs against P2PKH input --- test/fixtures/psbt.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index ad6ffdf..c275f9b 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -429,6 +429,11 @@ "psbt": "cHNidP8BAFUCAAAAAZfdvR7JvbVsuXunG1P19tTYi8Z0l6WHiz+0jpzTNTt+AAAAAAD/////AYA4AQAAAAAAGXapFMjtQ0xCK7OPd32ZJTY2UcdpFIaJiKwAAAAAAAEAygIAAAABb7TMmNs5UwEEpccLHlaaM0w3HEHhh85NjrUObLDMuZsBAAAAa0gwRQIhANWUevBBcqfRpH67rDuFqI+KHqxViKXxmSM+6/jE5ZHjAiBgO/vQg2EsYhaOD5lQbmZYaymRLukA6SCoXhkN21hiOwEhAoKzWGkj1LJ2iOt5wj6jMOtAMGJH50ZjAYZU81uUu6y+/////wGQXwEAAAAAACMhAzpK4NeYX+xyBOKynjD3BEPDq7EN8brZXje488gxshXwrAAAAAAiAgM6SuDXmF/scgTisp4w9wRDw6uxDfG62V43uPPIMbIV8EgwRQIhAL3H/XEPRAZEbpjBwkuLqUKBSu1Inpb2rganXNFcY2JsAiASrXTM2xODEKp7m7RTzYqBchqlvbl88zO/CGW9SePj2gEAAA==", "result": "cHNidP8BAFUCAAAAAZfdvR7JvbVsuXunG1P19tTYi8Z0l6WHiz+0jpzTNTt+AAAAAAD/////AYA4AQAAAAAAGXapFMjtQ0xCK7OPd32ZJTY2UcdpFIaJiKwAAAAAAAEAygIAAAABb7TMmNs5UwEEpccLHlaaM0w3HEHhh85NjrUObLDMuZsBAAAAa0gwRQIhANWUevBBcqfRpH67rDuFqI+KHqxViKXxmSM+6/jE5ZHjAiBgO/vQg2EsYhaOD5lQbmZYaymRLukA6SCoXhkN21hiOwEhAoKzWGkj1LJ2iOt5wj6jMOtAMGJH50ZjAYZU81uUu6y+/////wGQXwEAAAAAACMhAzpK4NeYX+xyBOKynjD3BEPDq7EN8brZXje488gxshXwrAAAAAABB0lIMEUCIQC9x/1xD0QGRG6YwcJLi6lCgUrtSJ6W9q4Gp1zRXGNibAIgEq10zNsTgxCqe5u0U82KgXIapb25fPMzvwhlvUnj49oBAAA=" }, + { + "type": "P2PKH", + "psbt": "cHNidP8BAFUCAAAAAaTbj9mE+B5Z8PLsuGUNGOzDqrtwdB08vvSccSCrezh+AAAAAAD/////AYA4AQAAAAAAGXapFDuK+0mR+qtL9tyadI72bKMwH+vuiKwAAAAAAAEAwAIAAAABks7XL87tkSusFA3L2u2CdJeNkqTMobehPNm/wkGQUzoBAAAAa0gwRQIhAMtzqC7axSA7/7nbio9NKQZz2ePuKeaF5T4c8JSXWEdgAiAID39I05hbJMNGSomrF7XVWEsEEHHzX/lkL3vnOOu4cAEhAvu+bfurbDCzxfOLxEhvz9ZyxPLdI1h9wMT8gl2nyLYV/////wGQXwEAAAAAABl2qRTH5239BfS9zrbwUtvpATwjua4LGYisAAAAACICA1oNX0U/6GwAuVI7JHhneD94sm/o+YrfTnRAhdRUMjoISDBFAiEAhk+HbvY6YShBCUmBVCk42sFWH9LTwUv2wbRC/tIuEAwCIED/UkklY3fpdDBN7qSBHFDyEOeHMUzXD0bvtFTAiKP7AQAA", + "result": "cHNidP8BAFUCAAAAAaTbj9mE+B5Z8PLsuGUNGOzDqrtwdB08vvSccSCrezh+AAAAAAD/////AYA4AQAAAAAAGXapFDuK+0mR+qtL9tyadI72bKMwH+vuiKwAAAAAAAEAwAIAAAABks7XL87tkSusFA3L2u2CdJeNkqTMobehPNm/wkGQUzoBAAAAa0gwRQIhAMtzqC7axSA7/7nbio9NKQZz2ePuKeaF5T4c8JSXWEdgAiAID39I05hbJMNGSomrF7XVWEsEEHHzX/lkL3vnOOu4cAEhAvu+bfurbDCzxfOLxEhvz9ZyxPLdI1h9wMT8gl2nyLYV/////wGQXwEAAAAAABl2qRTH5239BfS9zrbwUtvpATwjua4LGYisAAAAAAEHa0gwRQIhAIZPh272OmEoQQlJgVQpONrBVh/S08FL9sG0Qv7SLhAMAiBA/1JJJWN36XQwTe6kgRxQ8hDnhzFM1w9G77RUwIij+wEhA1oNX0U/6GwAuVI7JHhneD94sm/o+YrfTnRAhdRUMjoIAAA=" + }, { "type": "P2MS", "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=", From 7377566f98c8497b6f261a34127d6b7bcb57328f Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 10 Jul 2019 19:22:38 +0700 Subject: [PATCH 406/568] Test finalizeAllInputs against P2SH-P2WPKH input --- test/fixtures/psbt.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index c275f9b..de514f9 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -438,6 +438,11 @@ "type": "P2MS", "psbt": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAAiAgLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCEgwRQIhAOqqlnUwmc+PqKZVwAAMLPNCKskHhVgk1pXR9d7nmB6SAiA0C24idVdtgl9+sIBR8A3Za5WcswNJ3i8PWTMWR10C4QEiAgPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuUREgwRQIhANnDBUQWjS8hDTPidRZ95rGZK3LWCLIZB1NYx9++eBT8AiASupLR+KNa1Se7eoBkWQcDny+BaBfMQ+p1RTCAXd7/GAEBBItSIQLFmuo7gG8OjgmaO5AHuC/1OvDarQt16R7VEKU8dkRRCCEC4X74b+vRF3XLgyXyGejmE5VB1f3RY/IReUggyNHgdLYhA+dqu1GC9JCdInbJ1G7BI9cN1Tc/VhDCFiwrtfN8a5REIQO94OZv6yPD4kEZSZBquYA7FPsABZZ6UAteVuEw7fVxzlSuAAA=", "result": "cHNidP8BAFUCAAAAAbmLTo7GeyF7IT5PJznDiTVjhv5pQKcKe8Qa+8rqUpXuAAAAAAD/////AYA4AQAAAAAAGXapFF2G82oAzUpx/kcDUPy58goX16gkiKwAAAAAAAEAvgIAAAABMBwIKVKOFbas6tBD24LPchtrweE2jiXkPgSHdJndEfoAAAAAa0gwRQIhAJNchwx4je/ZqtZXSCD6BKJBQkpkTBGGdLoFZTcPKlDtAiAZefY/giaVw5rcEoJk4TRnFCyMG0dYLV5IVnFWkR38fQEhAi5yX8RMW6plVrMY26hithJ6j1w2mgqh/bOCAEVnbn/Z/////wGQXwEAAAAAABepFIoQ3seh+nGYzkI7MDw5zuSCIxEyhwAAAAABB/0gAQBIMEUCIQDqqpZ1MJnPj6imVcAADCzzQirJB4VYJNaV0fXe55gekgIgNAtuInVXbYJffrCAUfAN2WuVnLMDSd4vD1kzFkddAuEBSDBFAiEA2cMFRBaNLyENM+J1Fn3msZkrctYIshkHU1jH3754FPwCIBK6ktH4o1rVJ7t6gGRZBwOfL4FoF8xD6nVFMIBd3v8YAUyLUiECxZrqO4BvDo4JmjuQB7gv9Trw2q0Ldeke1RClPHZEUQghAuF++G/r0Rd1y4Ml8hno5hOVQdX90WPyEXlIIMjR4HS2IQPnartRgvSQnSJ2ydRuwSPXDdU3P1YQwhYsK7XzfGuURCEDveDmb+sjw+JBGUmQarmAOxT7AAWWelALXlbhMO31cc5UrgAA" + }, + { + "type": "P2SH-P2WPKH", + "psbt": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAAiAgKj88rhJwk3Zxm0p0Rp+xC/6cxmj+I741DHPWPWN7iA+0cwRAIgTRhd9WUpoHYl9tUVmoJ336fJAJInIjdYsoatvRiW8hgCIGOYMlpKRHiHA428Sfa2CdAIIGGQCGhuIgIzj2FN6USnAQEEFgAU4sZupXPxqhcsOB1ghJxBvH4XcesAAA==", + "result": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAABBxcWABTixm6lc/GqFyw4HWCEnEG8fhdx6wAA" } ], "fromTransaction": [ From 876a241e0c6d1f4e69596de945bc453bcd9c3976 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Wed, 10 Jul 2019 19:23:01 +0700 Subject: [PATCH 407/568] Test finalizeAllInputs against P2WPKH input --- test/fixtures/psbt.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index de514f9..72d5662 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -443,6 +443,11 @@ "type": "P2SH-P2WPKH", "psbt": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAAiAgKj88rhJwk3Zxm0p0Rp+xC/6cxmj+I741DHPWPWN7iA+0cwRAIgTRhd9WUpoHYl9tUVmoJ336fJAJInIjdYsoatvRiW8hgCIGOYMlpKRHiHA428Sfa2CdAIIGGQCGhuIgIzj2FN6USnAQEEFgAU4sZupXPxqhcsOB1ghJxBvH4XcesAAA==", "result": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAABBxcWABTixm6lc/GqFyw4HWCEnEG8fhdx6wAA" + }, + { + "type": "P2WPKH", + "psbt": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAIgIDV7l7X08dWtzMR2saPXJo782Tot5+PQBMZZOm3GGTUHpIMEUCIQDxPCn2fwfxoYgGVyi5++mXNAmCX2wWRS7LuJB2qQkMVQIgHsxylMEWd7ladApdSpnEtSwWb4/QJGOnsDGZCPxvfucBAAA=", + "result": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAAQcAAAA=" } ], "fromTransaction": [ From 9ee115b030b7effaf10b18c4e24e5aa9181efc8f Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 11 Jul 2019 10:11:51 +0900 Subject: [PATCH 408/568] assert the fee calculation is correct --- test/fixtures/psbt.json | 3 ++- test/psbt.js | 34 ++++++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 72d5662..638eb19 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -464,7 +464,8 @@ "nonExistantIndex": 42 }, "getFeeRate": { - "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA" + "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", + "fee": 21 }, "cache": { "nonWitnessUtxo": { diff --git a/test/psbt.js b/test/psbt.js index 6dd01a9..0ccd8ef 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -97,22 +97,22 @@ describe(`Psbt`, () => { fixtures.bip174.signer.forEach(f => { it('Signs PSBT to the expected result', () => { const psbt = Psbt.fromBase64(f.psbt) - + f.keys.forEach(({inputToSign, WIF}) => { const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet); psbt.signInput(inputToSign, keyPair); }) - + assert.strictEqual(psbt.toBase64(), f.result) }) }) - + fixtures.bip174.combiner.forEach(f => { it('Combines two PSBTs to the expected result', () => { const psbts = f.psbts.map(psbt => Psbt.fromBase64(psbt)) - + psbts[0].combine(psbts[1]) - + // Produces a different Base64 string due to implemetation specific key-value ordering. // That means this test will fail: // assert.strictEqual(psbts[0].toBase64(), f.result) @@ -120,41 +120,41 @@ describe(`Psbt`, () => { assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result)) }) }) - + fixtures.bip174.finalizer.forEach(f => { it("Finalizes inputs and gives the expected PSBT", () => { const psbt = Psbt.fromBase64(f.psbt) - + psbt.finalizeAllInputs() - + assert.strictEqual(psbt.toBase64(), f.result) }) }) - + fixtures.bip174.extractor.forEach(f => { it('Extracts the expected transaction from a PSBT', () => { const psbt1 = Psbt.fromBase64(f.psbt) const transaction1 = psbt1.extractTransaction(true).toHex() - + const psbt2 = Psbt.fromBase64(f.psbt) const transaction2 = psbt2.extractTransaction().toHex() - + assert.strictEqual(transaction1, transaction2) assert.strictEqual(transaction1, f.transaction) - + const psbt3 = Psbt.fromBase64(f.psbt) delete psbt3.inputs[0].finalScriptSig delete psbt3.inputs[0].finalScriptWitness assert.throws(() => { psbt3.extractTransaction() }, new RegExp('Not finalized')) - + const psbt4 = Psbt.fromBase64(f.psbt) psbt4.setMaximumFeeRate(1) assert.throws(() => { psbt4.extractTransaction() }, new RegExp('Warning: You are paying around [\\d.]+ in fees')) - + const psbt5 = Psbt.fromBase64(f.psbt) psbt5.extractTransaction(true) const fr1 = psbt5.getFeeRate() @@ -426,9 +426,7 @@ describe(`Psbt`, () => { psbt.finalizeAllInputs() - assert.doesNotThrow(() => { - psbt.getFeeRate() - }) + assert.strictEqual(psbt.getFeeRate(), f.fee) }) }) @@ -516,4 +514,4 @@ describe(`Psbt`, () => { assert.ok(psbt.inputs[index].nonWitnessUtxo.equals(value)) }) }) -}) \ No newline at end of file +}) From 266302a3ae29413362d8108ba18e6bfd515e9820 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 11 Jul 2019 10:21:36 +0900 Subject: [PATCH 409/568] Add P2WSH-P2PK finalize vector --- test/fixtures/psbt.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 638eb19..aeed39d 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -448,6 +448,11 @@ "type": "P2WPKH", "psbt": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAIgIDV7l7X08dWtzMR2saPXJo782Tot5+PQBMZZOm3GGTUHpIMEUCIQDxPCn2fwfxoYgGVyi5++mXNAmCX2wWRS7LuJB2qQkMVQIgHsxylMEWd7ladApdSpnEtSwWb4/QJGOnsDGZCPxvfucBAAA=", "result": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAAQcAAAA=" + }, + { + "type": "P2WSH-P2PK", + "psbt": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8iAgNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06UgwRQIhALO0xRpuqP3aVkm+DPykrtqe6fPNSgNblp9K9MAwmtHJAiAHV5ZvZN8Vi49n/o9ISFyvtHsPXXPKqBxC9m2m2HlpYgEBBSMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PIgIDXzYJxZxD0vbOD2PyOW3s6VogBM0OA0UwEoHUnXjDNOlIMEUCIQC6XN6tpo9mYlZej4BXSSh5D1K6aILBfQ4WBWXUrISx6wIgVaxFUsz8y59xJ1V4sZ1qarHX9pZ+MJmLKbl2ZSadisoBAQUjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA=", + "result": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8BCG4CSDBFAiEAs7TFGm6o/dpWSb4M/KSu2p7p881KA1uWn0r0wDCa0ckCIAdXlm9k3xWLj2f+j0hIXK+0ew9dc8qoHEL2babYeWliASMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PAQhuAkgwRQIhALpc3q2mj2ZiVl6PgFdJKHkPUrpogsF9DhYFZdSshLHrAiBVrEVSzPzLn3EnVXixnWpqsdf2ln4wmYspuXZlJp2KygEjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA=" } ], "fromTransaction": [ From 8a5104c33366da88d56b1102be261fe81a9e35d9 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 11 Jul 2019 11:09:05 +0900 Subject: [PATCH 410/568] Add tests --- test/fixtures/psbt.json | 18 ++++++++--- test/psbt.js | 68 ++++++++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index aeed39d..61ab114 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -314,7 +314,7 @@ "exception": "Error adding input." }, { - "description": "checks for hash and index", + "description": "should be equal", "inputData": { "hash": "Buffer.from('000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f', 'hex')", "index": 2 @@ -326,12 +326,19 @@ "addOutput": { "checks": [ { - "description": "checks for hash and index", + "description": "Checks value is number", "outputData": { "address": "1P2NFEBp32V2arRwZNww6tgXEV58FG94mr", "value": "xyz" }, "exception": "Error adding output." + }, + { + "description": "Adds output normally", + "outputData": { + "address": "1P2NFEBp32V2arRwZNww6tgXEV58FG94mr", + "value": 42 + } } ] }, @@ -446,8 +453,8 @@ }, { "type": "P2WPKH", - "psbt": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAIgIDV7l7X08dWtzMR2saPXJo782Tot5+PQBMZZOm3GGTUHpIMEUCIQDxPCn2fwfxoYgGVyi5++mXNAmCX2wWRS7LuJB2qQkMVQIgHsxylMEWd7ladApdSpnEtSwWb4/QJGOnsDGZCPxvfucBAAA=", - "result": "cHNidP8BAFUCAAAAAQgW434j2ReNazU0B0+Wa45cPWyUkrl6beWc1ZA6s7qYAAAAAAD/////AYA4AQAAAAAAGXapFC6fT3nay4TXcKcXnM8ZZ89Pmi8xiKwAAAAAAAEAvAIAAAABX6KFQJqSuzjln4vq33jes8S/HAJw/W8fB2hmYJvF95AAAAAAakcwRAIgBfylcQEFaoyuzZcVT1AG3nU3FxDE2ZRNSQbH0Pb38pkCIBh+SztZRlITl/WbdA/Z7SVsVqApPeT1HmzpiLcunN4yASECHwUJGd/AygUjfs6zraDrLcjE5arbSBzHCVX/DQD3tuf/////AZBfAQAAAAAAFgAU2gkxRpZzSTR+etKYJ+B243D+sbYAAAAAAQcAAAA=" + "psbt": "cHNidP8BAFICAAAAAb5UIQFSoXPjqxSmuiSZkM5PjaycnrXQ6VB+u2+MzbSGAAAAAAD/////ASBOAAAAAAAAFgAUXsVBEaHSlhycDORbHHtBKwDo4zIAAAAAAAEBHzB1AAAAAAAAFgAUvIgag7HZu7Rjd/ugmJC/MHlZmAYiAgLN1zezMD4c4uegTbgfJ1GCtN5/hlJYaJt7e8mt1BVzIEcwRAIgXhgL5G81tXP7MAaKJtA0QaFu17bLocOGqxXmDoIfYUACIAOIynpoPS/dTz9Omg2h7v5kiql7ab0SPzWDdxpvpsUcAQAA", + "result": "cHNidP8BAFICAAAAAb5UIQFSoXPjqxSmuiSZkM5PjaycnrXQ6VB+u2+MzbSGAAAAAAD/////ASBOAAAAAAAAFgAUXsVBEaHSlhycDORbHHtBKwDo4zIAAAAAAAEBHzB1AAAAAAAAFgAUvIgag7HZu7Rjd/ugmJC/MHlZmAYBCGsCRzBEAiBeGAvkbzW1c/swBoom0DRBoW7Xtsuhw4arFeYOgh9hQAIgA4jKemg9L91PP06aDaHu/mSKqXtpvRI/NYN3Gm+mxRwBIQLN1zezMD4c4uegTbgfJ1GCtN5/hlJYaJt7e8mt1BVzIAAA" }, { "type": "P2WSH-P2PK", @@ -478,5 +485,8 @@ "nonWitnessUtxo": "Buffer.from('0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000', 'hex')", "inputIndex": 0 } + }, + "clone": { + "psbt": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8iAgNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06UgwRQIhALO0xRpuqP3aVkm+DPykrtqe6fPNSgNblp9K9MAwmtHJAiAHV5ZvZN8Vi49n/o9ISFyvtHsPXXPKqBxC9m2m2HlpYgEBBSMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PIgIDXzYJxZxD0vbOD2PyOW3s6VogBM0OA0UwEoHUnXjDNOlIMEUCIQC6XN6tpo9mYlZej4BXSSh5D1K6aILBfQ4WBWXUrISx6wIgVaxFUsz8y59xJ1V4sZ1qarHX9pZ+MJmLKbl2ZSadisoBAQUjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA=" } } diff --git a/test/psbt.js b/test/psbt.js index 0ccd8ef..99bd9c1 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -28,7 +28,7 @@ describe(`Psbt`, () => { it(`Invalid: ${f.description}`, () => { assert.throws(() => { Psbt.fromBase64(f.psbt) - }, {message: f.errorMessage}) + }, new RegExp(f.errorMessage)) }) }) @@ -46,7 +46,7 @@ describe(`Psbt`, () => { const psbt = Psbt.fromBase64(f.psbt) assert.throws(() => { psbt.signInput(f.inputToCheck, keyPair) - }, {message: f.errorMessage}) + }, new RegExp(f.errorMessage)) }) }) @@ -181,7 +181,7 @@ describe(`Psbt`, () => { f.shouldThrow.inputToCheck, ECPair.fromWIF(f.shouldThrow.WIF), ) - }, {message: f.shouldThrow.errorMessage}) + }, new RegExp(f.shouldThrow.errorMessage)) assert.rejects(async () => { await psbtThatShouldThrow.signInputAsync( f.shouldThrow.inputToCheck, @@ -208,7 +208,7 @@ describe(`Psbt`, () => { f.shouldThrow.inputToCheck, ECPair.fromWIF(f.shouldThrow.WIF), ) - }, {message: f.shouldThrow.errorMessage}) + }, new RegExp(f.shouldThrow.errorMessage)) assert.throws(() => { psbtThatShouldThrow.signInput( f.shouldThrow.inputToCheck, @@ -276,6 +276,23 @@ describe(`Psbt`, () => { assert.strictEqual(psbt.toBase64(), f.result) }) }) + it('fails if no script found', () => { + const psbt = new Psbt() + psbt.addInput({ + hash: '0000000000000000000000000000000000000000000000000000000000000000', + index: 0 + }) + assert.throws(() => { + psbt.finalizeAllInputs() + }, new RegExp('No script found for input #0')) + psbt.addWitnessUtxoToInput(0, { + script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d', 'hex'), + value: 2e5 + }) + assert.throws(() => { + psbt.finalizeAllInputs() + }, new RegExp('Can not finalize input #0')) + }) }) describe('fromTransaction', () => { @@ -289,9 +306,6 @@ describe(`Psbt`, () => { describe('addInput', () => { fixtures.addInput.checks.forEach(f => { - for (const attr of Object.keys(f.inputData)) { - f.inputData[attr] = f.inputData[attr] - } it(f.description, () => { const psbt = new Psbt() @@ -299,13 +313,14 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.addInput(f.inputData) }, new RegExp(f.exception)) + assert.throws(() => { + psbt.addInputs([f.inputData]) + }, new RegExp(f.exception)) } else { assert.doesNotThrow(() => { - psbt.addInput(f.inputData) + psbt.addInputs([f.inputData]) if (f.equals) { assert.strictEqual(psbt.toBase64(), f.equals) - } else { - console.log(psbt.toBase64()) } }) assert.throws(() => { @@ -318,9 +333,6 @@ describe(`Psbt`, () => { describe('addOutput', () => { fixtures.addOutput.checks.forEach(f => { - for (const attr of Object.keys(f.outputData)) { - f.outputData[attr] = f.outputData[attr] - } it(f.description, () => { const psbt = new Psbt() @@ -328,10 +340,15 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.addOutput(f.outputData) }, new RegExp(f.exception)) + assert.throws(() => { + psbt.addOutputs([f.outputData]) + }, new RegExp(f.exception)) } else { assert.doesNotThrow(() => { psbt.addOutput(f.outputData) - console.log(psbt.toBase64()) + }) + assert.doesNotThrow(() => { + psbt.addOutputs([f.outputData]) }) } }) @@ -381,7 +398,26 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.setSequence(1, 0) - }, {message: 'Input index too high'}) + }, new RegExp('Input index too high')) + }) + }) + + describe('clone', () => { + it('Should clone a psbt exactly with no reference', () => { + const f = fixtures.clone + const psbt = Psbt.fromBase64(f.psbt) + const notAClone = Object.assign(new Psbt(), psbt) // references still active + const clone = psbt.clone() + + assert.strictEqual(psbt.validateAllSignatures(), true) + + assert.strictEqual(clone.toBase64(), psbt.toBase64()) + assert.strictEqual(clone.toBase64(), notAClone.toBase64()) + assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) + psbt.globalMap.unsignedTx[3] = 0xff + assert.notStrictEqual(clone.toBase64(), psbt.toBase64()) + assert.notStrictEqual(clone.toBase64(), notAClone.toBase64()) + assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) }) }) @@ -427,6 +463,8 @@ describe(`Psbt`, () => { psbt.finalizeAllInputs() assert.strictEqual(psbt.getFeeRate(), f.fee) + psbt.__CACHE.__FEE_RATE = undefined + assert.strictEqual(psbt.getFeeRate(), f.fee) }) }) From 2f1609b9189d877810a80d40a4c8e6653f440434 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 11 Jul 2019 11:28:09 +0900 Subject: [PATCH 411/568] Fix: P2WPKH was signing with nonWitnessUtxo --- src/psbt.js | 23 +++++++++++++---------- test/fixtures/psbt.json | 2 +- ts_src/psbt.ts | 25 +++++++++++++++---------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index c7e8b55..752714f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -404,6 +404,7 @@ const isP2MS = isPaymentFactory(payments.p2ms); const isP2PK = isPaymentFactory(payments.p2pk); const isP2PKH = isPaymentFactory(payments.p2pkh); const isP2WPKH = isPaymentFactory(payments.p2wpkh); +const isP2WSHScript = isPaymentFactory(payments.p2wsh); function check32Bit(num) { if ( typeof num !== 'number' || @@ -611,19 +612,16 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) { // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript(inputIndex, prevout.script, input.redeemScript); script = input.redeemScript; - hash = unsignedTx.hashForSignature( - inputIndex, - input.redeemScript, - sighashType, - ); } else { script = prevout.script; - hash = unsignedTx.hashForSignature( - inputIndex, - prevout.script, - sighashType, + } + if (isP2WPKH(script) || isP2WSHScript(script)) { + throw new Error( + `Input #${inputIndex} has nonWitnessUtxo but segwit script: ` + + `${script.toString('hex')}`, ); } + hash = unsignedTx.hashForSignature(inputIndex, script, sighashType); } else if (input.witnessUtxo) { let _script; // so we don't shadow the `let script` above if (input.redeemScript) { @@ -647,7 +645,7 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) { sighashType, ); script = _script; - } else { + } else if (isP2WSHScript(_script)) { if (!input.witnessScript) throw new Error('Segwit input needs witnessScript if not P2WPKH'); checkWitnessScript(inputIndex, _script, input.witnessScript); @@ -659,6 +657,11 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) { ); // want to make sure the script we return is the actual meaningful script script = input.witnessScript; + } else { + throw new Error( + `Input #${inputIndex} has witnessUtxo but non-segwit script: ` + + `${_script.toString('hex')}`, + ); } } else { throw new Error('Need a Utxo input item for signing'); diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 61ab114..fbad821 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -121,7 +121,7 @@ "failSignChecks": [ { "description": "A Witness UTXO is provided for a non-witness input", - "errorMessage": "Segwit input needs witnessScript if not P2WPKH", + "errorMessage": "Input #0 has witnessUtxo but non-segwit script", "psbt": "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEBItPf9QUAAAAAGXapFNSO0xELlAFMsRS9Mtb00GbcdCVriKwAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=", "inputToCheck": 0 }, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 8fb1e97..928ca04 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -509,6 +509,7 @@ const isP2MS = isPaymentFactory(payments.p2ms); const isP2PK = isPaymentFactory(payments.p2pk); const isP2PKH = isPaymentFactory(payments.p2pkh); const isP2WPKH = isPaymentFactory(payments.p2wpkh); +const isP2WSHScript = isPaymentFactory(payments.p2wsh); function check32Bit(num: number): void { if ( @@ -764,19 +765,18 @@ function getHashForSig( // If a redeemScript is provided, the scriptPubKey must be for that redeemScript checkRedeemScript(inputIndex, prevout.script, input.redeemScript); script = input.redeemScript; - hash = unsignedTx.hashForSignature( - inputIndex, - input.redeemScript, - sighashType, - ); } else { script = prevout.script; - hash = unsignedTx.hashForSignature( - inputIndex, - prevout.script, - sighashType, + } + + if (isP2WPKH(script) || isP2WSHScript(script)) { + throw new Error( + `Input #${inputIndex} has nonWitnessUtxo but segwit script: ` + + `${script.toString('hex')}`, ); } + + hash = unsignedTx.hashForSignature(inputIndex, script, sighashType); } else if (input.witnessUtxo) { let _script: Buffer; // so we don't shadow the `let script` above if (input.redeemScript) { @@ -800,7 +800,7 @@ function getHashForSig( sighashType, ); script = _script; - } else { + } else if (isP2WSHScript(_script)) { if (!input.witnessScript) throw new Error('Segwit input needs witnessScript if not P2WPKH'); checkWitnessScript(inputIndex, _script, input.witnessScript); @@ -812,6 +812,11 @@ function getHashForSig( ); // want to make sure the script we return is the actual meaningful script script = input.witnessScript; + } else { + throw new Error( + `Input #${inputIndex} has witnessUtxo but non-segwit script: ` + + `${_script.toString('hex')}`, + ); } } else { throw new Error('Need a Utxo input item for signing'); From c403757ce84e35cd1b63d87780cde1187ca8630a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 11 Jul 2019 14:49:26 +0900 Subject: [PATCH 412/568] Add name attribute to Payments --- src/payments/embed.js | 2 +- src/payments/p2ms.js | 4 ++++ src/payments/p2pk.js | 2 +- src/payments/p2pkh.js | 2 +- src/payments/p2sh.js | 5 +++++ src/payments/p2wpkh.js | 2 +- src/payments/p2wsh.js | 5 +++++ test/fixtures/p2ms.json | 7 +++++++ test/fixtures/p2pk.json | 5 +++++ test/fixtures/p2pkh.json | 6 ++++++ test/fixtures/p2sh.json | 11 +++++++++++ test/fixtures/p2wpkh.json | 6 ++++++ test/fixtures/p2wsh.json | 11 +++++++++++ test/payments.js | 21 +++++++++++++++++++++ ts_src/payments/embed.ts | 2 +- ts_src/payments/index.ts | 1 + ts_src/payments/p2ms.ts | 4 ++++ ts_src/payments/p2pk.ts | 2 +- ts_src/payments/p2pkh.ts | 2 +- ts_src/payments/p2sh.ts | 5 +++++ ts_src/payments/p2wpkh.ts | 2 +- ts_src/payments/p2wsh.ts | 5 +++++ types/payments/index.d.ts | 1 + 23 files changed, 105 insertions(+), 8 deletions(-) diff --git a/src/payments/embed.js b/src/payments/embed.js index 3ddceb9..19df35d 100644 --- a/src/payments/embed.js +++ b/src/payments/embed.js @@ -24,7 +24,7 @@ function p2data(a, opts) { a, ); const network = a.network || networks_1.bitcoin; - const o = { network }; + const o = { name: 'embed', network }; lazy.prop(o, 'output', () => { if (!a.data) return; return bscript.compile([OPS.OP_RETURN].concat(a.data)); diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index 1e7c6ba..9fed788 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -93,6 +93,10 @@ function p2ms(a, opts) { if (!o.input) return; return []; }); + lazy.prop(o, 'name', () => { + if (!o.m || !o.n) return; + return `p2ms(${o.m} of ${o.n})`; + }); // extended validation if (opts.validate) { if (a.output) { diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index 13356d1..702669e 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -26,7 +26,7 @@ function p2pk(a, opts) { return bscript.decompile(a.input); }); const network = a.network || networks_1.bitcoin; - const o = { network }; + const o = { name: 'p2pk', network }; lazy.prop(o, 'output', () => { if (!a.pubkey) return; return bscript.compile([a.pubkey, OPS.OP_CHECKSIG]); diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index ceb7093..94c801f 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -36,7 +36,7 @@ function p2pkh(a, opts) { return bscript.decompile(a.input); }); const network = a.network || networks_1.bitcoin; - const o = { network }; + const o = { name: 'p2pkh', network }; lazy.prop(o, 'address', () => { if (!o.hash) return; const payload = Buffer.allocUnsafe(21); diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index 5fe660a..42b1e23 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -93,6 +93,11 @@ function p2sh(a, opts) { if (o.redeem && o.redeem.witness) return o.redeem.witness; if (o.input) return []; }); + lazy.prop(o, 'name', () => { + const nameParts = ['p2sh']; + if (o.redeem !== undefined) nameParts.push(o.redeem.name); + return nameParts.join('-'); + }); if (opts.validate) { let hash = Buffer.from([]); if (a.address) { diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index 9571e50..b32e808 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -40,7 +40,7 @@ function p2wpkh(a, opts) { }; }); const network = a.network || networks_1.bitcoin; - const o = { network }; + const o = { name: 'p2wpkh', network }; lazy.prop(o, 'address', () => { if (!o.hash) return; const words = bech32.toWords(o.hash); diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index 9363718..6a4aa24 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -103,6 +103,11 @@ function p2wsh(a, opts) { if (!a.redeem.witness) return; return [].concat(a.redeem.witness, a.redeem.output); }); + lazy.prop(o, 'name', () => { + const nameParts = ['p2wsh']; + if (o.redeem !== undefined) nameParts.push(o.redeem.name); + return nameParts.join('-'); + }); // extended validation if (opts.validate) { let hash = Buffer.from([]); diff --git a/test/fixtures/p2ms.json b/test/fixtures/p2ms.json index 2f41270..f84b4d5 100644 --- a/test/fixtures/p2ms.json +++ b/test/fixtures/p2ms.json @@ -9,6 +9,7 @@ "expected": { "m": 2, "n": 2, + "name": "p2ms(2 of 2)", "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", "pubkeys": [ "030000000000000000000000000000000000000000000000000000000000000001", @@ -31,6 +32,7 @@ "expected": { "m": 1, "n": 2, + "name": "p2ms(1 of 2)", "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", "pubkeys": [ "030000000000000000000000000000000000000000000000000000000000000001", @@ -58,6 +60,7 @@ "expected": { "m": 2, "n": 3, + "name": "p2ms(2 of 3)", "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", "pubkeys": [ "030000000000000000000000000000000000000000000000000000000000000001", @@ -84,6 +87,7 @@ "expected": { "m": 2, "n": 3, + "name": "p2ms(2 of 3)", "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", "pubkeys": [ "030000000000000000000000000000000000000000000000000000000000000001", @@ -107,6 +111,7 @@ "expected": { "m": 2, "n": 3, + "name": "p2ms(2 of 3)", "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", "pubkeys": [ "030000000000000000000000000000000000000000000000000000000000000001", @@ -133,6 +138,7 @@ "expected": { "m": 2, "n": 2, + "name": "p2ms(2 of 2)", "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", "pubkeys": [ "030000000000000000000000000000000000000000000000000000000000000001", @@ -161,6 +167,7 @@ "expected": { "m": 2, "n": 2, + "name": "p2ms(2 of 2)", "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", "pubkeys": [ "030000000000000000000000000000000000000000000000000000000000000001", diff --git a/test/fixtures/p2pk.json b/test/fixtures/p2pk.json index 58f51cb..f3982d3 100644 --- a/test/fixtures/p2pk.json +++ b/test/fixtures/p2pk.json @@ -7,6 +7,7 @@ }, "options": {}, "expected": { + "name": "p2pk", "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", "signature": null, "input": null, @@ -19,6 +20,7 @@ "pubkey": "030000000000000000000000000000000000000000000000000000000000000001" }, "expected": { + "name": "p2pk", "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", "signature": null, "input": null, @@ -32,6 +34,7 @@ "signature": "300602010002010001" }, "expected": { + "name": "p2pk", "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", "input": "300602010002010001", "witness": [] @@ -44,6 +47,7 @@ "signature": "300602010002010001" }, "expected": { + "name": "p2pk", "output": "030000000000000000000000000000000000000000000000000000000000000001 OP_CHECKSIG", "input": "300602010002010001", "witness": [] @@ -56,6 +60,7 @@ "input": "300602010002010001" }, "expected": { + "name": "p2pk", "pubkey": "030000000000000000000000000000000000000000000000000000000000000001", "signature": "300602010002010001", "witness": [] diff --git a/test/fixtures/p2pkh.json b/test/fixtures/p2pkh.json index 44c20fe..4efbb59 100644 --- a/test/fixtures/p2pkh.json +++ b/test/fixtures/p2pkh.json @@ -7,6 +7,7 @@ }, "options": {}, "expected": { + "name": "p2pkh", "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", "signature": null, @@ -20,6 +21,7 @@ "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1" }, "expected": { + "name": "p2pkh", "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", "signature": null, @@ -33,6 +35,7 @@ "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG" }, "expected": { + "name": "p2pkh", "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", "signature": null, @@ -46,6 +49,7 @@ "pubkey": "030000000000000000000000000000000000000000000000000000000000000001" }, "expected": { + "name": "p2pkh", "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", @@ -61,6 +65,7 @@ "signature": "300602010002010001" }, "expected": { + "name": "p2pkh", "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", @@ -74,6 +79,7 @@ "input": "300602010002010001 030000000000000000000000000000000000000000000000000000000000000001" }, "expected": { + "name": "p2pkh", "address": "134D6gYy8DsR5m4416BnmgASuMBqKvogQh", "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", "output": "OP_DUP OP_HASH160 168b992bcfc44050310b3a94bd0771136d0b28d1 OP_EQUALVERIFY OP_CHECKSIG", diff --git a/test/fixtures/p2sh.json b/test/fixtures/p2sh.json index 595b85e..21b63cc 100644 --- a/test/fixtures/p2sh.json +++ b/test/fixtures/p2sh.json @@ -7,6 +7,7 @@ }, "options": {}, "expected": { + "name": "p2sh", "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", "redeem": null, @@ -20,6 +21,7 @@ "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086" }, "expected": { + "name": "p2sh", "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", "redeem": null, @@ -33,6 +35,7 @@ "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL" }, "expected": { + "name": "p2sh", "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", "redeem": null, @@ -49,6 +52,7 @@ } }, "expected": { + "name": "p2sh-p2pkh", "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", @@ -65,6 +69,7 @@ } }, "expected": { + "name": "p2sh-p2wpkh", "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", @@ -81,6 +86,7 @@ } }, "expected": { + "name": "p2sh-p2pk", "address": "36TibC8RrPB9WrBdPoGXhHqDHJosyFVtVQ", "hash": "3454c084887afe854e80221c69d6282926f809c4", "output": "OP_HASH160 3454c084887afe854e80221c69d6282926f809c4 OP_EQUAL", @@ -97,6 +103,7 @@ } }, "expected": { + "name": "p2sh-p2pkh", "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", @@ -117,6 +124,7 @@ } }, "expected": { + "name": "p2sh-p2wpkh", "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", @@ -133,6 +141,7 @@ "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 2103e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058ac" }, "expected": { + "name": "p2sh-p2pk", "address": "36TibC8RrPB9WrBdPoGXhHqDHJosyFVtVQ", "hash": "3454c084887afe854e80221c69d6282926f809c4", "output": "OP_HASH160 3454c084887afe854e80221c69d6282926f809c4 OP_EQUAL", @@ -154,6 +163,7 @@ ] }, "expected": { + "name": "p2sh-p2wpkh", "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", @@ -177,6 +187,7 @@ } }, "expected": { + "name": "p2sh-p2pkh", "address": "2N7nfc7zeWuADtpdR4MrR7Wq3dzr7LxTCgS", "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", diff --git a/test/fixtures/p2wpkh.json b/test/fixtures/p2wpkh.json index f667bdc..4d62b60 100644 --- a/test/fixtures/p2wpkh.json +++ b/test/fixtures/p2wpkh.json @@ -7,6 +7,7 @@ }, "options": {}, "expected": { + "name": "p2wpkh", "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727", "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", "signature": null, @@ -20,6 +21,7 @@ "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727" }, "expected": { + "name": "p2wpkh", "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727", "signature": null, @@ -33,6 +35,7 @@ "output": "OP_0 ea6d525c0c955d90d3dbd29a81ef8bfb79003727" }, "expected": { + "name": "p2wpkh", "address": "bc1qafk4yhqvj4wep57m62dgrmutldusqde8adh20d", "hash": "ea6d525c0c955d90d3dbd29a81ef8bfb79003727", "signature": null, @@ -46,6 +49,7 @@ "pubkey": "030000000000000000000000000000000000000000000000000000000000000001" }, "expected": { + "name": "p2wpkh", "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", @@ -61,6 +65,7 @@ "signature": "300602010002010001" }, "expected": { + "name": "p2wpkh", "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", @@ -80,6 +85,7 @@ ] }, "expected": { + "name": "p2wpkh", "address": "bc1qz69ej270c3q9qvgt822t6pm3zdksk2x35j2jlm", "hash": "168b992bcfc44050310b3a94bd0771136d0b28d1", "output": "OP_0 168b992bcfc44050310b3a94bd0771136d0b28d1", diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json index e5ce0e0..0350fe9 100644 --- a/test/fixtures/p2wsh.json +++ b/test/fixtures/p2wsh.json @@ -7,6 +7,7 @@ }, "options": {}, "expected": { + "name": "p2wsh", "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", "redeem": null, @@ -20,6 +21,7 @@ "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0" }, "expected": { + "name": "p2wsh", "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", "redeem": null, @@ -33,6 +35,7 @@ "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0" }, "expected": { + "name": "p2wsh", "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", "redeem": null, @@ -49,6 +52,7 @@ } }, "expected": { + "name": "p2wsh-p2pkh", "address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", @@ -65,6 +69,7 @@ } }, "expected": { + "name": "p2wsh-p2wpkh", "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", @@ -81,6 +86,7 @@ } }, "expected": { + "name": "p2wsh-p2pk", "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", @@ -97,6 +103,7 @@ } }, "expected": { + "name": "p2wsh-p2pkh", "address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", @@ -124,6 +131,7 @@ } }, "expected": { + "name": "p2wsh-p2wpkh", "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", @@ -144,6 +152,7 @@ ] }, "expected": { + "name": "p2wsh-p2pk", "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", @@ -167,6 +176,7 @@ ] }, "expected": { + "name": "p2wsh-p2wpkh", "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", @@ -190,6 +200,7 @@ } }, "expected": { + "name": "p2wsh-p2pkh", "address": "tb1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ssaskm8v", "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", diff --git a/test/payments.js b/test/payments.js index 5619cdb..1386ea4 100644 --- a/test/payments.js +++ b/test/payments.js @@ -41,6 +41,27 @@ const u = require('./payments.utils') }) }) + if (p === 'p2sh') { + const p2wsh = require('../src/payments/p2wsh').p2wsh + const p2pk = require('../src/payments/p2pk').p2pk + it('properly assembles nested p2wsh with names', () => { + const actual = fn({ + redeem: p2wsh({ + redeem: p2pk({ + pubkey: Buffer.from( + '03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058', + 'hex', + ) + }) + }) + }) + assert.strictEqual(actual.address, '3MGbrbye4ttNUXM8WAvBFRKry4fkS9fjuw') + assert.strictEqual(actual.name, 'p2sh-p2wsh-p2pk') + assert.strictEqual(actual.redeem.name, 'p2wsh-p2pk') + assert.strictEqual(actual.redeem.redeem.name, 'p2pk') + }) + } + // cross-verify dynamically too if (!fixtures.dynamic) return const { depends, details } = fixtures.dynamic diff --git a/ts_src/payments/embed.ts b/ts_src/payments/embed.ts index 38a8162..c54e279 100644 --- a/ts_src/payments/embed.ts +++ b/ts_src/payments/embed.ts @@ -29,7 +29,7 @@ export function p2data(a: Payment, opts?: PaymentOpts): Payment { ); const network = a.network || BITCOIN_NETWORK; - const o = { network } as Payment; + const o = { name: 'embed', network } as Payment; lazy.prop(o, 'output', () => { if (!a.data) return; diff --git a/ts_src/payments/index.ts b/ts_src/payments/index.ts index acd6ddb..c48a6f1 100644 --- a/ts_src/payments/index.ts +++ b/ts_src/payments/index.ts @@ -8,6 +8,7 @@ import { p2wpkh } from './p2wpkh'; import { p2wsh } from './p2wsh'; export interface Payment { + name?: string; network?: Network; output?: Buffer; data?: Buffer[]; diff --git a/ts_src/payments/p2ms.ts b/ts_src/payments/p2ms.ts index bac8b83..7cd6f10 100644 --- a/ts_src/payments/p2ms.ts +++ b/ts_src/payments/p2ms.ts @@ -102,6 +102,10 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { if (!o.input) return; return []; }); + lazy.prop(o, 'name', () => { + if (!o.m || !o.n) return; + return `p2ms(${o.m} of ${o.n})`; + }); // extended validation if (opts.validate) { diff --git a/ts_src/payments/p2pk.ts b/ts_src/payments/p2pk.ts index d14aacf..b4fdbc5 100644 --- a/ts_src/payments/p2pk.ts +++ b/ts_src/payments/p2pk.ts @@ -30,7 +30,7 @@ export function p2pk(a: Payment, opts?: PaymentOpts): Payment { }) as StackFunction; const network = a.network || BITCOIN_NETWORK; - const o: Payment = { network }; + const o: Payment = { name: 'p2pk', network }; lazy.prop(o, 'output', () => { if (!a.pubkey) return; diff --git a/ts_src/payments/p2pkh.ts b/ts_src/payments/p2pkh.ts index 12c9473..6503093 100644 --- a/ts_src/payments/p2pkh.ts +++ b/ts_src/payments/p2pkh.ts @@ -41,7 +41,7 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { }) as StackFunction; const network = a.network || BITCOIN_NETWORK; - const o: Payment = { network }; + const o: Payment = { name: 'p2pkh', network }; lazy.prop(o, 'address', () => { if (!o.hash) return; diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index 46c11cc..8a097bd 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -116,6 +116,11 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { if (o.redeem && o.redeem.witness) return o.redeem.witness; if (o.input) return []; }); + lazy.prop(o, 'name', () => { + const nameParts = ['p2sh']; + if (o.redeem !== undefined) nameParts.push(o.redeem.name!); + return nameParts.join('-'); + }); if (opts.validate) { let hash: Buffer = Buffer.from([]); diff --git a/ts_src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts index 7d2748c..fc2a458 100644 --- a/ts_src/payments/p2wpkh.ts +++ b/ts_src/payments/p2wpkh.ts @@ -45,7 +45,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { }); const network = a.network || BITCOIN_NETWORK; - const o: Payment = { network }; + const o: Payment = { name: 'p2wpkh', network }; lazy.prop(o, 'address', () => { if (!o.hash) return; diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 131de45..132dde4 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -116,6 +116,11 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { if (!a.redeem.witness) return; return ([] as Buffer[]).concat(a.redeem.witness, a.redeem.output); }); + lazy.prop(o, 'name', () => { + const nameParts = ['p2wsh']; + if (o.redeem !== undefined) nameParts.push(o.redeem.name!); + return nameParts.join('-'); + }); // extended validation if (opts.validate) { diff --git a/types/payments/index.d.ts b/types/payments/index.d.ts index 102f20a..7f17b79 100644 --- a/types/payments/index.d.ts +++ b/types/payments/index.d.ts @@ -8,6 +8,7 @@ import { p2sh } from './p2sh'; import { p2wpkh } from './p2wpkh'; import { p2wsh } from './p2wsh'; export interface Payment { + name?: string; network?: Network; output?: Buffer; data?: Buffer[]; From 1feef9569c60f5c7eaf0072a8fd79a81e2cc44b7 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 11 Jul 2019 17:17:49 +0900 Subject: [PATCH 413/568] Composition over inheritance --- package-lock.json | 6 +- package.json | 2 +- src/psbt.js | 199 ++++++++++++++++++++++++--------- test/psbt.js | 12 +- ts_src/psbt.ts | 277 +++++++++++++++++++++++++++++++++++----------- types/psbt.d.ts | 36 +++++- 6 files changed, 402 insertions(+), 130 deletions(-) diff --git a/package-lock.json b/package-lock.json index fce68c4..fabd361 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.14.tgz", - "integrity": "sha512-v9cre0W4ZpAJS1v18WUJLE9yKdSZyenGpZBg7CXiZ5n35JPXganH92d4Yk8WXpRfbFZ4SMXTqKLEgpLPX1TmcA==" + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.15.tgz", + "integrity": "sha512-mK/s9p7i+PG7W2s2cAedNVk1NDZQn9wAoq1DlsS2+1zz5TXR3TRTzqRqm9BQtOXwbkxkhfLwlmsOjuiRdAYkdg==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index 03e6d5f..a32021a 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.14", + "bip174": "0.0.15", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", diff --git a/src/psbt.js b/src/psbt.js index 752714f..578e17c 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -15,9 +15,9 @@ const DEFAULT_OPTS = { network: networks_1.bitcoin, maximumFeeRate: 5000, }; -class Psbt extends bip174_1.Psbt { - constructor(opts = {}) { - super(); +class Psbt { + constructor(opts = {}, data = new bip174_1.Psbt()) { + this.data = data; this.__CACHE = { __NON_WITNESS_UTXO_TX_CACHE: [], __NON_WITNESS_UTXO_BUF_CACHE: [], @@ -27,11 +27,11 @@ class Psbt extends bip174_1.Psbt { // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); const c = this.__CACHE; - c.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); - this.setVersion(2); + c.__TX = transaction_1.Transaction.fromBuffer(data.globalMap.unsignedTx); + if (this.data.inputs.length === 0) this.setVersion(2); // set cache - delete this.globalMap.unsignedTx; - Object.defineProperty(this.globalMap, 'unsignedTx', { + delete data.globalMap.unsignedTx; + Object.defineProperty(data.globalMap, 'unsignedTx', { enumerable: true, get() { const buf = c.__TX_BUF_CACHE; @@ -42,8 +42,8 @@ class Psbt extends bip174_1.Psbt { return c.__TX_BUF_CACHE; } }, - set(data) { - c.__TX_BUF_CACHE = data; + set(_data) { + c.__TX_BUF_CACHE = _data; }, }); // Make data hidden when enumerating @@ -55,29 +55,38 @@ class Psbt extends bip174_1.Psbt { dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } - static fromTransaction(txBuf) { + static fromTransaction(txBuf, opts = {}) { const tx = transaction_1.Transaction.fromBuffer(txBuf); checkTxEmpty(tx); - const psbt = new this(); + const psbtBase = new bip174_1.Psbt(); + const psbt = new Psbt(opts, psbtBase); psbt.__CACHE.__TX = tx; checkTxForDupeIns(tx, psbt.__CACHE); let inputCount = tx.ins.length; let outputCount = tx.outs.length; while (inputCount > 0) { - psbt.inputs.push({ - keyVals: [], + psbtBase.inputs.push({ + unknownKeyVals: [], }); inputCount--; } while (outputCount > 0) { - psbt.outputs.push({ - keyVals: [], + psbtBase.outputs.push({ + unknownKeyVals: [], }); outputCount--; } return psbt; } - static fromBuffer(buffer) { + static fromBase64(data, opts = {}) { + const buffer = Buffer.from(data, 'base64'); + return this.fromBuffer(buffer, opts); + } + static fromHex(data, opts = {}) { + const buffer = Buffer.from(data, 'hex'); + return this.fromBuffer(buffer, opts); + } + static fromBuffer(buffer, opts = {}) { let tx; const txCountGetter = txBuf => { tx = transaction_1.Transaction.fromBuffer(txBuf); @@ -87,17 +96,22 @@ class Psbt extends bip174_1.Psbt { outputCount: tx.outs.length, }; }; - const psbt = super.fromBuffer(buffer, txCountGetter); + const psbtBase = bip174_1.Psbt.fromBuffer(buffer, txCountGetter); + const psbt = new Psbt(opts, psbtBase); psbt.__CACHE.__TX = tx; checkTxForDupeIns(tx, psbt.__CACHE); return psbt; } get inputCount() { - return this.inputs.length; + return this.data.inputs.length; + } + combine(...those) { + this.data.combine(...those.map(o => o.data)); + return this; } clone() { // TODO: more efficient cloning - const res = Psbt.fromBuffer(this.toBuffer()); + const res = Psbt.fromBuffer(this.data.toBuffer()); res.opts = JSON.parse(JSON.stringify(this.opts)); return res; } @@ -107,7 +121,7 @@ class Psbt extends bip174_1.Psbt { } setVersion(version) { check32Bit(version); - checkInputsForPartialSig(this.inputs, 'setVersion'); + checkInputsForPartialSig(this.data.inputs, 'setVersion'); const c = this.__CACHE; c.__TX.version = version; c.__TX_BUF_CACHE = undefined; @@ -116,7 +130,7 @@ class Psbt extends bip174_1.Psbt { } setLocktime(locktime) { check32Bit(locktime); - checkInputsForPartialSig(this.inputs, 'setLocktime'); + checkInputsForPartialSig(this.data.inputs, 'setLocktime'); const c = this.__CACHE; c.__TX.locktime = locktime; c.__TX_BUF_CACHE = undefined; @@ -125,7 +139,7 @@ class Psbt extends bip174_1.Psbt { } setSequence(inputIndex, sequence) { check32Bit(sequence); - checkInputsForPartialSig(this.inputs, 'setSequence'); + checkInputsForPartialSig(this.data.inputs, 'setSequence'); const c = this.__CACHE; if (c.__TX.ins.length <= inputIndex) { throw new Error('Input index too high'); @@ -140,10 +154,15 @@ class Psbt extends bip174_1.Psbt { return this; } addInput(inputData) { - checkInputsForPartialSig(this.inputs, 'addInput'); + checkInputsForPartialSig(this.data.inputs, 'addInput'); const c = this.__CACHE; const inputAdder = getInputAdder(c); - super.addInput(inputData, inputAdder); + this.data.addInput(inputData, inputAdder); + const inputIndex = this.data.inputs.length - 1; + const input = this.data.inputs[inputIndex]; + if (input.nonWitnessUtxo) { + addNonWitnessTxCache(this.__CACHE, input, inputIndex); + } c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -153,7 +172,7 @@ class Psbt extends bip174_1.Psbt { return this; } addOutput(outputData) { - checkInputsForPartialSig(this.inputs, 'addOutput'); + checkInputsForPartialSig(this.data.inputs, 'addOutput'); const { address } = outputData; if (typeof address === 'string') { const { network } = this.opts; @@ -162,30 +181,24 @@ class Psbt extends bip174_1.Psbt { } const c = this.__CACHE; const outputAdder = getOutputAdder(c); - super.addOutput(outputData, true, outputAdder); + this.data.addOutput(outputData, outputAdder, true); c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; } - addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) { - super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); - const input = this.inputs[inputIndex]; - addNonWitnessTxCache(this.__CACHE, input, inputIndex); - return this; - } extractTransaction(disableFeeCheck) { - if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized'); const c = this.__CACHE; if (!disableFeeCheck) { checkFees(this, c, this.opts); } if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; const tx = c.__TX.clone(); - inputFinalizeGetAmts(this.inputs, tx, c, true); + inputFinalizeGetAmts(this.data.inputs, tx, c, true); return tx; } getFeeRate() { - if (!this.inputs.every(isFinalized)) + if (!this.data.inputs.every(isFinalized)) throw new Error('PSBT must be finalized to calculate fee rate'); const c = this.__CACHE; if (c.__FEE_RATE) return c.__FEE_RATE; @@ -197,16 +210,16 @@ class Psbt extends bip174_1.Psbt { } else { tx = c.__TX.clone(); } - inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize); + inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize); return c.__FEE_RATE; } finalizeAllInputs() { - utils_1.checkForInput(this.inputs, 0); // making sure we have at least one - range(this.inputs.length).forEach(idx => this.finalizeInput(idx)); + utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one + range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx)); return this; } finalizeInput(inputIndex) { - const input = utils_1.checkForInput(this.inputs, inputIndex); + const input = utils_1.checkForInput(this.data.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, @@ -226,23 +239,23 @@ class Psbt extends bip174_1.Psbt { isP2WSH, ); if (finalScriptSig) - this.addFinalScriptSigToInput(inputIndex, finalScriptSig); + this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig); if (finalScriptWitness) - this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); + this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); if (!finalScriptSig && !finalScriptWitness) throw new Error(`Unknown error finalizing input #${inputIndex}`); - this.clearFinalizedInput(inputIndex); + this.data.clearFinalizedInput(inputIndex); return this; } validateAllSignatures() { - utils_1.checkForInput(this.inputs, 0); // making sure we have at least one - const results = range(this.inputs.length).map(idx => + utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one + const results = range(this.data.inputs.length).map(idx => this.validateSignatures(idx), ); return results.reduce((final, res) => res === true && final, true); } validateSignatures(inputIndex, pubkey) { - const input = this.inputs[inputIndex]; + const input = this.data.inputs[inputIndex]; const partialSig = (input || {}).partialSig; if (!input || !partialSig || partialSig.length < 1) throw new Error('No signatures to validate'); @@ -280,7 +293,7 @@ class Psbt extends bip174_1.Psbt { // as input information is added, then eventually // optimize this method. const results = []; - for (const i of range(this.inputs.length)) { + for (const i of range(this.data.inputs.length)) { try { this.signInput(i, keyPair, sighashTypes); results.push(true); @@ -302,7 +315,7 @@ class Psbt extends bip174_1.Psbt { // optimize this method. const results = []; const promises = []; - for (const [i] of this.inputs.entries()) { + for (const [i] of this.data.inputs.entries()) { promises.push( this.signInputAsync(i, keyPair, sighashTypes).then( () => { @@ -330,7 +343,7 @@ class Psbt extends bip174_1.Psbt { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); const { hash, sighashType } = getHashAndSighashType( - this.inputs, + this.data.inputs, inputIndex, keyPair.publicKey, this.__CACHE, @@ -340,7 +353,8 @@ class Psbt extends bip174_1.Psbt { pubkey: keyPair.publicKey, signature: bscript.signature.encode(keyPair.sign(hash), sighashType), }; - return this.addPartialSigToInput(inputIndex, partialSig); + this.data.addPartialSigToInput(inputIndex, partialSig); + return this; } signInputAsync( inputIndex, @@ -351,7 +365,7 @@ class Psbt extends bip174_1.Psbt { if (!keyPair || !keyPair.publicKey) return reject(new Error('Need Signer to sign input')); const { hash, sighashType } = getHashAndSighashType( - this.inputs, + this.data.inputs, inputIndex, keyPair.publicKey, this.__CACHE, @@ -362,11 +376,94 @@ class Psbt extends bip174_1.Psbt { pubkey: keyPair.publicKey, signature: bscript.signature.encode(signature, sighashType), }; - this.addPartialSigToInput(inputIndex, partialSig); + this.data.addPartialSigToInput(inputIndex, partialSig); resolve(); }); }); } + toBuffer() { + return this.data.toBuffer(); + } + toHex() { + return this.data.toHex(); + } + toBase64() { + return this.data.toBase64(); + } + addGlobalXpubToGlobal(globalXpub) { + this.data.addGlobalXpubToGlobal(globalXpub); + return this; + } + addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) { + this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); + const input = this.data.inputs[inputIndex]; + addNonWitnessTxCache(this.__CACHE, input, inputIndex); + return this; + } + addWitnessUtxoToInput(inputIndex, witnessUtxo) { + this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo); + return this; + } + addPartialSigToInput(inputIndex, partialSig) { + this.data.addPartialSigToInput(inputIndex, partialSig); + return this; + } + addSighashTypeToInput(inputIndex, sighashType) { + this.data.addSighashTypeToInput(inputIndex, sighashType); + return this; + } + addRedeemScriptToInput(inputIndex, redeemScript) { + this.data.addRedeemScriptToInput(inputIndex, redeemScript); + return this; + } + addWitnessScriptToInput(inputIndex, witnessScript) { + this.data.addWitnessScriptToInput(inputIndex, witnessScript); + return this; + } + addBip32DerivationToInput(inputIndex, bip32Derivation) { + this.data.addBip32DerivationToInput(inputIndex, bip32Derivation); + return this; + } + addFinalScriptSigToInput(inputIndex, finalScriptSig) { + this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig); + return this; + } + addFinalScriptWitnessToInput(inputIndex, finalScriptWitness) { + this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); + return this; + } + addPorCommitmentToInput(inputIndex, porCommitment) { + this.data.addPorCommitmentToInput(inputIndex, porCommitment); + return this; + } + addRedeemScriptToOutput(outputIndex, redeemScript) { + this.data.addRedeemScriptToOutput(outputIndex, redeemScript); + return this; + } + addWitnessScriptToOutput(outputIndex, witnessScript) { + this.data.addWitnessScriptToOutput(outputIndex, witnessScript); + return this; + } + addBip32DerivationToOutput(outputIndex, bip32Derivation) { + this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation); + return this; + } + addUnknownKeyValToGlobal(keyVal) { + this.data.addUnknownKeyValToGlobal(keyVal); + return this; + } + addUnknownKeyValToInput(inputIndex, keyVal) { + this.data.addUnknownKeyValToInput(inputIndex, keyVal); + return this; + } + addUnknownKeyValToOutput(outputIndex, keyVal) { + this.data.addUnknownKeyValToOutput(outputIndex, keyVal); + return this; + } + clearFinalizedInput(inputIndex) { + this.data.clearFinalizedInput(inputIndex); + return this; + } } exports.Psbt = Psbt; function canFinalize(input, script, scriptType) { diff --git a/test/psbt.js b/test/psbt.js index 99bd9c1..1804e67 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -143,8 +143,8 @@ describe(`Psbt`, () => { assert.strictEqual(transaction1, f.transaction) const psbt3 = Psbt.fromBase64(f.psbt) - delete psbt3.inputs[0].finalScriptSig - delete psbt3.inputs[0].finalScriptWitness + delete psbt3.data.inputs[0].finalScriptSig + delete psbt3.data.inputs[0].finalScriptWitness assert.throws(() => { psbt3.extractTransaction() }, new RegExp('Not finalized')) @@ -414,7 +414,7 @@ describe(`Psbt`, () => { assert.strictEqual(clone.toBase64(), psbt.toBase64()) assert.strictEqual(clone.toBase64(), notAClone.toBase64()) assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) - psbt.globalMap.unsignedTx[3] = 0xff + psbt.data.globalMap.unsignedTx[3] = 0xff assert.notStrictEqual(clone.toBase64(), psbt.toBase64()) assert.notStrictEqual(clone.toBase64(), notAClone.toBase64()) assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) @@ -542,14 +542,14 @@ describe(`Psbt`, () => { // Cache is populated psbt.addNonWitnessUtxoToInput(index, f.nonWitnessUtxo) - const value = psbt.inputs[index].nonWitnessUtxo + const value = psbt.data.inputs[index].nonWitnessUtxo assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value)) assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo)) // Cache is rebuilt from internal transaction object when cleared - psbt.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3]) + psbt.data.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3]) psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined - assert.ok(psbt.inputs[index].nonWitnessUtxo.equals(value)) + assert.ok(psbt.data.inputs[index].nonWitnessUtxo.equals(value)) }) }) }) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 928ca04..5a73031 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,11 +1,21 @@ import { Psbt as PsbtBase } from 'bip174'; import * as varuint from 'bip174/src/lib/converter/varint'; import { + Bip32Derivation, + FinalScriptSig, + FinalScriptWitness, + GlobalXpub, + KeyValue, NonWitnessUtxo, PartialSig, + PorCommitment, PsbtInput, + RedeemScript, + SighashType, TransactionInput, TransactionOutput, + WitnessScript, + WitnessUtxo, } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; import { toOutputScript } from './address'; @@ -26,37 +36,42 @@ const DEFAULT_OPTS: PsbtOpts = { maximumFeeRate: 5000, // satoshi per byte }; -export class Psbt extends PsbtBase { - static fromTransaction<T extends typeof PsbtBase>( - this: T, - txBuf: Buffer, - ): InstanceType<T> { +export class Psbt { + static fromTransaction(txBuf: Buffer, opts: PsbtOptsOptional = {}): Psbt { const tx = Transaction.fromBuffer(txBuf); checkTxEmpty(tx); - const psbt = new this() as Psbt; + const psbtBase = new PsbtBase(); + const psbt = new Psbt(opts, psbtBase); psbt.__CACHE.__TX = tx; checkTxForDupeIns(tx, psbt.__CACHE); let inputCount = tx.ins.length; let outputCount = tx.outs.length; while (inputCount > 0) { - psbt.inputs.push({ - keyVals: [], + psbtBase.inputs.push({ + unknownKeyVals: [], }); inputCount--; } while (outputCount > 0) { - psbt.outputs.push({ - keyVals: [], + psbtBase.outputs.push({ + unknownKeyVals: [], }); outputCount--; } - return psbt as InstanceType<T>; + return psbt; } - static fromBuffer<T extends typeof PsbtBase>( - this: T, - buffer: Buffer, - ): InstanceType<T> { + static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt { + const buffer = Buffer.from(data, 'base64'); + return this.fromBuffer(buffer, opts); + } + + static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt { + const buffer = Buffer.from(data, 'hex'); + return this.fromBuffer(buffer, opts); + } + + static fromBuffer(buffer: Buffer, opts: PsbtOptsOptional = {}): Psbt { let tx: Transaction | undefined; const txCountGetter = ( txBuf: Buffer, @@ -71,10 +86,11 @@ export class Psbt extends PsbtBase { outputCount: tx.outs.length, }; }; - const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt; + const psbtBase = PsbtBase.fromBuffer(buffer, txCountGetter); + const psbt = new Psbt(opts, psbtBase); psbt.__CACHE.__TX = tx!; checkTxForDupeIns(tx!, psbt.__CACHE); - return psbt as InstanceType<T>; + return psbt; } private __CACHE: PsbtCache = { @@ -85,17 +101,19 @@ export class Psbt extends PsbtBase { }; private opts: PsbtOpts; - constructor(opts: PsbtOptsOptional = {}) { - super(); + constructor( + opts: PsbtOptsOptional = {}, + readonly data: PsbtBase = new PsbtBase(), + ) { // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); const c = this.__CACHE; - c.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); - this.setVersion(2); + c.__TX = Transaction.fromBuffer(data.globalMap.unsignedTx!); + if (this.data.inputs.length === 0) this.setVersion(2); // set cache - delete this.globalMap.unsignedTx; - Object.defineProperty(this.globalMap, 'unsignedTx', { + delete data.globalMap.unsignedTx; + Object.defineProperty(data.globalMap, 'unsignedTx', { enumerable: true, get(): Buffer { const buf = c.__TX_BUF_CACHE; @@ -106,8 +124,8 @@ export class Psbt extends PsbtBase { return c.__TX_BUF_CACHE; } }, - set(data: Buffer): void { - c.__TX_BUF_CACHE = data; + set(_data: Buffer): void { + c.__TX_BUF_CACHE = _data; }, }); @@ -127,12 +145,17 @@ export class Psbt extends PsbtBase { } get inputCount(): number { - return this.inputs.length; + return this.data.inputs.length; + } + + combine(...those: Psbt[]): this { + this.data.combine(...those.map(o => o.data)); + return this; } clone(): Psbt { // TODO: more efficient cloning - const res = Psbt.fromBuffer(this.toBuffer()); + const res = Psbt.fromBuffer(this.data.toBuffer()); res.opts = JSON.parse(JSON.stringify(this.opts)); return res; } @@ -144,7 +167,7 @@ export class Psbt extends PsbtBase { setVersion(version: number): this { check32Bit(version); - checkInputsForPartialSig(this.inputs, 'setVersion'); + checkInputsForPartialSig(this.data.inputs, 'setVersion'); const c = this.__CACHE; c.__TX.version = version; c.__TX_BUF_CACHE = undefined; @@ -154,7 +177,7 @@ export class Psbt extends PsbtBase { setLocktime(locktime: number): this { check32Bit(locktime); - checkInputsForPartialSig(this.inputs, 'setLocktime'); + checkInputsForPartialSig(this.data.inputs, 'setLocktime'); const c = this.__CACHE; c.__TX.locktime = locktime; c.__TX_BUF_CACHE = undefined; @@ -164,7 +187,7 @@ export class Psbt extends PsbtBase { setSequence(inputIndex: number, sequence: number): this { check32Bit(sequence); - checkInputsForPartialSig(this.inputs, 'setSequence'); + checkInputsForPartialSig(this.data.inputs, 'setSequence'); const c = this.__CACHE; if (c.__TX.ins.length <= inputIndex) { throw new Error('Input index too high'); @@ -181,10 +204,16 @@ export class Psbt extends PsbtBase { } addInput(inputData: TransactionInput): this { - checkInputsForPartialSig(this.inputs, 'addInput'); + checkInputsForPartialSig(this.data.inputs, 'addInput'); const c = this.__CACHE; const inputAdder = getInputAdder(c); - super.addInput(inputData, inputAdder); + this.data.addInput(inputData, inputAdder); + + const inputIndex = this.data.inputs.length - 1; + const input = this.data.inputs[inputIndex]; + if (input.nonWitnessUtxo) { + addNonWitnessTxCache(this.__CACHE, input, inputIndex); + } c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -196,7 +225,7 @@ export class Psbt extends PsbtBase { } addOutput(outputData: TransactionOutput): this { - checkInputsForPartialSig(this.inputs, 'addOutput'); + checkInputsForPartialSig(this.data.inputs, 'addOutput'); const { address } = outputData as any; if (typeof address === 'string') { const { network } = this.opts; @@ -205,36 +234,26 @@ export class Psbt extends PsbtBase { } const c = this.__CACHE; const outputAdder = getOutputAdder(c); - super.addOutput(outputData, true, outputAdder); + this.data.addOutput(outputData, outputAdder, true); c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; } - addNonWitnessUtxoToInput( - inputIndex: number, - nonWitnessUtxo: NonWitnessUtxo, - ): this { - super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); - const input = this.inputs[inputIndex]; - addNonWitnessTxCache(this.__CACHE, input, inputIndex); - return this; - } - extractTransaction(disableFeeCheck?: boolean): Transaction { - if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); + if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized'); const c = this.__CACHE; if (!disableFeeCheck) { checkFees(this, c, this.opts); } if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; const tx = c.__TX.clone(); - inputFinalizeGetAmts(this.inputs, tx, c, true); + inputFinalizeGetAmts(this.data.inputs, tx, c, true); return tx; } getFeeRate(): number { - if (!this.inputs.every(isFinalized)) + if (!this.data.inputs.every(isFinalized)) throw new Error('PSBT must be finalized to calculate fee rate'); const c = this.__CACHE; if (c.__FEE_RATE) return c.__FEE_RATE; @@ -246,18 +265,18 @@ export class Psbt extends PsbtBase { } else { tx = c.__TX.clone(); } - inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize); + inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize); return c.__FEE_RATE!; } finalizeAllInputs(): this { - checkForInput(this.inputs, 0); // making sure we have at least one - range(this.inputs.length).forEach(idx => this.finalizeInput(idx)); + checkForInput(this.data.inputs, 0); // making sure we have at least one + range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx)); return this; } finalizeInput(inputIndex: number): this { - const input = checkForInput(this.inputs, inputIndex); + const input = checkForInput(this.data.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, @@ -281,26 +300,26 @@ export class Psbt extends PsbtBase { ); if (finalScriptSig) - this.addFinalScriptSigToInput(inputIndex, finalScriptSig); + this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig); if (finalScriptWitness) - this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); + this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); if (!finalScriptSig && !finalScriptWitness) throw new Error(`Unknown error finalizing input #${inputIndex}`); - this.clearFinalizedInput(inputIndex); + this.data.clearFinalizedInput(inputIndex); return this; } validateAllSignatures(): boolean { - checkForInput(this.inputs, 0); // making sure we have at least one - const results = range(this.inputs.length).map(idx => + checkForInput(this.data.inputs, 0); // making sure we have at least one + const results = range(this.data.inputs.length).map(idx => this.validateSignatures(idx), ); return results.reduce((final, res) => res === true && final, true); } validateSignatures(inputIndex: number, pubkey?: Buffer): boolean { - const input = this.inputs[inputIndex]; + const input = this.data.inputs[inputIndex]; const partialSig = (input || {}).partialSig; if (!input || !partialSig || partialSig.length < 1) throw new Error('No signatures to validate'); @@ -343,7 +362,7 @@ export class Psbt extends PsbtBase { // as input information is added, then eventually // optimize this method. const results: boolean[] = []; - for (const i of range(this.inputs.length)) { + for (const i of range(this.data.inputs.length)) { try { this.signInput(i, keyPair, sighashTypes); results.push(true); @@ -371,7 +390,7 @@ export class Psbt extends PsbtBase { // optimize this method. const results: boolean[] = []; const promises: Array<Promise<void>> = []; - for (const [i] of this.inputs.entries()) { + for (const [i] of this.data.inputs.entries()) { promises.push( this.signInputAsync(i, keyPair, sighashTypes).then( () => { @@ -401,7 +420,7 @@ export class Psbt extends PsbtBase { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); const { hash, sighashType } = getHashAndSighashType( - this.inputs, + this.data.inputs, inputIndex, keyPair.publicKey, this.__CACHE, @@ -413,7 +432,8 @@ export class Psbt extends PsbtBase { signature: bscript.signature.encode(keyPair.sign(hash), sighashType), }; - return this.addPartialSigToInput(inputIndex, partialSig); + this.data.addPartialSigToInput(inputIndex, partialSig); + return this; } signInputAsync( @@ -426,7 +446,7 @@ export class Psbt extends PsbtBase { if (!keyPair || !keyPair.publicKey) return reject(new Error('Need Signer to sign input')); const { hash, sighashType } = getHashAndSighashType( - this.inputs, + this.data.inputs, inputIndex, keyPair.publicKey, this.__CACHE, @@ -439,12 +459,143 @@ export class Psbt extends PsbtBase { signature: bscript.signature.encode(signature, sighashType), }; - this.addPartialSigToInput(inputIndex, partialSig); + this.data.addPartialSigToInput(inputIndex, partialSig); resolve(); }); }, ); } + + toBuffer(): Buffer { + return this.data.toBuffer(); + } + + toHex(): string { + return this.data.toHex(); + } + + toBase64(): string { + return this.data.toBase64(); + } + + addGlobalXpubToGlobal(globalXpub: GlobalXpub): this { + this.data.addGlobalXpubToGlobal(globalXpub); + return this; + } + + addNonWitnessUtxoToInput( + inputIndex: number, + nonWitnessUtxo: NonWitnessUtxo, + ): this { + this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); + const input = this.data.inputs[inputIndex]; + addNonWitnessTxCache(this.__CACHE, input, inputIndex); + return this; + } + + addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this { + this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo); + return this; + } + + addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this { + this.data.addPartialSigToInput(inputIndex, partialSig); + return this; + } + + addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this { + this.data.addSighashTypeToInput(inputIndex, sighashType); + return this; + } + + addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this { + this.data.addRedeemScriptToInput(inputIndex, redeemScript); + return this; + } + + addWitnessScriptToInput( + inputIndex: number, + witnessScript: WitnessScript, + ): this { + this.data.addWitnessScriptToInput(inputIndex, witnessScript); + return this; + } + + addBip32DerivationToInput( + inputIndex: number, + bip32Derivation: Bip32Derivation, + ): this { + this.data.addBip32DerivationToInput(inputIndex, bip32Derivation); + return this; + } + + addFinalScriptSigToInput( + inputIndex: number, + finalScriptSig: FinalScriptSig, + ): this { + this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig); + return this; + } + + addFinalScriptWitnessToInput( + inputIndex: number, + finalScriptWitness: FinalScriptWitness, + ): this { + this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); + return this; + } + + addPorCommitmentToInput( + inputIndex: number, + porCommitment: PorCommitment, + ): this { + this.data.addPorCommitmentToInput(inputIndex, porCommitment); + return this; + } + + addRedeemScriptToOutput( + outputIndex: number, + redeemScript: RedeemScript, + ): this { + this.data.addRedeemScriptToOutput(outputIndex, redeemScript); + return this; + } + + addWitnessScriptToOutput( + outputIndex: number, + witnessScript: WitnessScript, + ): this { + this.data.addWitnessScriptToOutput(outputIndex, witnessScript); + return this; + } + + addBip32DerivationToOutput( + outputIndex: number, + bip32Derivation: Bip32Derivation, + ): this { + this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation); + return this; + } + + addUnknownKeyValToGlobal(keyVal: KeyValue): this { + this.data.addUnknownKeyValToGlobal(keyVal); + return this; + } + + addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this { + this.data.addUnknownKeyValToInput(inputIndex, keyVal); + return this; + } + + addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this { + this.data.addUnknownKeyValToOutput(outputIndex, keyVal); + return this; + } + + clearFinalizedInput(inputIndex: number): this { + this.data.clearFinalizedInput(inputIndex); + return this; + } } interface PsbtCache { diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 95f6624..533b8fd 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,16 +1,20 @@ /// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; -import { NonWitnessUtxo, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; +import { Bip32Derivation, FinalScriptSig, FinalScriptWitness, GlobalXpub, KeyValue, NonWitnessUtxo, PartialSig, PorCommitment, RedeemScript, SighashType, TransactionInput, TransactionOutput, WitnessScript, WitnessUtxo } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; -export declare class Psbt extends PsbtBase { - static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>; - static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>; +export declare class Psbt { + readonly data: PsbtBase; + static fromTransaction(txBuf: Buffer, opts?: PsbtOptsOptional): Psbt; + static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt; + static fromHex(data: string, opts?: PsbtOptsOptional): Psbt; + static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt; private __CACHE; private opts; - constructor(opts?: PsbtOptsOptional); + constructor(opts?: PsbtOptsOptional, data?: PsbtBase); readonly inputCount: number; + combine(...those: Psbt[]): this; clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void; setVersion(version: number): this; @@ -20,7 +24,6 @@ export declare class Psbt extends PsbtBase { addInput(inputData: TransactionInput): this; addOutputs(outputDatas: TransactionOutput[]): this; addOutput(outputData: TransactionOutput): this; - addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this; extractTransaction(disableFeeCheck?: boolean): Transaction; getFeeRate(): number; finalizeAllInputs(): this; @@ -31,6 +34,27 @@ export declare class Psbt extends PsbtBase { signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>; signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this; signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>; + toBuffer(): Buffer; + toHex(): string; + toBase64(): string; + addGlobalXpubToGlobal(globalXpub: GlobalXpub): this; + addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this; + addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this; + addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this; + addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this; + addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this; + addWitnessScriptToInput(inputIndex: number, witnessScript: WitnessScript): this; + addBip32DerivationToInput(inputIndex: number, bip32Derivation: Bip32Derivation): this; + addFinalScriptSigToInput(inputIndex: number, finalScriptSig: FinalScriptSig): this; + addFinalScriptWitnessToInput(inputIndex: number, finalScriptWitness: FinalScriptWitness): this; + addPorCommitmentToInput(inputIndex: number, porCommitment: PorCommitment): this; + addRedeemScriptToOutput(outputIndex: number, redeemScript: RedeemScript): this; + addWitnessScriptToOutput(outputIndex: number, witnessScript: WitnessScript): this; + addBip32DerivationToOutput(outputIndex: number, bip32Derivation: Bip32Derivation): this; + addUnknownKeyValToGlobal(keyVal: KeyValue): this; + addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this; + addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this; + clearFinalizedInput(inputIndex: number): this; } interface PsbtOptsOptional { network?: Network; From f25938d3ca8abf8fcbac2074f81b629661bd1e03 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 11 Jul 2019 16:24:35 +0700 Subject: [PATCH 414/568] Test signing a non-whitelisted sighashtype --- test/fixtures/psbt.json | 9 +++ test/psbt.js | 160 +++++++++++++++++++++++----------------- 2 files changed, 101 insertions(+), 68 deletions(-) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index fbad821..1a37031 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -427,6 +427,15 @@ "inputToCheck": 0, "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" } + }, + { + "description": "allows signing non-whitelisted sighashtype when explicitly passed in", + "shouldSign": { + "psbt": "cHNidP8BADMBAAAAAYaq+PdOUY2PnV9kZKa82XlqrPByOqwH2TRg2+LQdqs2AAAAAAD/////AAAAAAAAAQEgAOH1BQAAAAAXqRSTNeWHqa9INvSnQ120SZeJc+6JSocBAwSBAAAAAQQWABQvLKRyDqYsPYImhD3eURpDGL10RwAA", + "sighashTypes": [129], + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" + } } ] }, diff --git a/test/psbt.js b/test/psbt.js index 1804e67..8ed7b51 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -167,26 +167,32 @@ describe(`Psbt`, () => { describe('signInputAsync', () => { fixtures.signInput.checks.forEach(f => { it(f.description, async () => { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) - assert.doesNotReject(async () => { - await psbtThatShouldsign.signInputAsync( - f.shouldSign.inputToCheck, - ECPair.fromWIF(f.shouldSign.WIF), - ) - }) + if (f.shouldSign) { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotReject(async () => { + await psbtThatShouldsign.signInputAsync( + f.shouldSign.inputToCheck, + ECPair.fromWIF(f.shouldSign.WIF), + f.shouldSign.sighashTypes || undefined, + ) + }) + } - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) - assert.rejects(async () => { - await psbtThatShouldThrow.signInputAsync( - f.shouldThrow.inputToCheck, - ECPair.fromWIF(f.shouldThrow.WIF), - ) - }, new RegExp(f.shouldThrow.errorMessage)) - assert.rejects(async () => { - await psbtThatShouldThrow.signInputAsync( - f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need Signer to sign input')) + if (f.shouldThrow) { + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.rejects(async () => { + await psbtThatShouldThrow.signInputAsync( + f.shouldThrow.inputToCheck, + ECPair.fromWIF(f.shouldThrow.WIF), + f.shouldThrow.sighashTypes || undefined, + ) + }, new RegExp(f.shouldThrow.errorMessage)) + assert.rejects(async () => { + await psbtThatShouldThrow.signInputAsync( + f.shouldThrow.inputToCheck, + ) + }, new RegExp('Need Signer to sign input')) + } }) }) }) @@ -194,26 +200,32 @@ describe(`Psbt`, () => { describe('signInput', () => { fixtures.signInput.checks.forEach(f => { it(f.description, () => { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) - assert.doesNotThrow(() => { - psbtThatShouldsign.signInput( - f.shouldSign.inputToCheck, - ECPair.fromWIF(f.shouldSign.WIF), - ) - }) + if (f.shouldSign) { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotThrow(() => { + psbtThatShouldsign.signInput( + f.shouldSign.inputToCheck, + ECPair.fromWIF(f.shouldSign.WIF), + f.shouldSign.sighashTypes || undefined, + ) + }) + } - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) - assert.throws(() => { - psbtThatShouldThrow.signInput( - f.shouldThrow.inputToCheck, - ECPair.fromWIF(f.shouldThrow.WIF), - ) - }, new RegExp(f.shouldThrow.errorMessage)) - assert.throws(() => { - psbtThatShouldThrow.signInput( - f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need Signer to sign input')) + if (f.shouldThrow) { + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.throws(() => { + psbtThatShouldThrow.signInput( + f.shouldThrow.inputToCheck, + ECPair.fromWIF(f.shouldThrow.WIF), + f.shouldThrow.sighashTypes || undefined, + ) + }, new RegExp(f.shouldThrow.errorMessage)) + assert.throws(() => { + psbtThatShouldThrow.signInput( + f.shouldThrow.inputToCheck, + ) + }, new RegExp('Need Signer to sign input')) + } }) }) }) @@ -222,22 +234,28 @@ describe(`Psbt`, () => { fixtures.signInput.checks.forEach(f => { if (f.description === 'checks the input exists') return it(f.description, async () => { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) - assert.doesNotReject(async () => { - await psbtThatShouldsign.signAsync( - ECPair.fromWIF(f.shouldSign.WIF), - ) - }) + if (f.shouldSign) { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotReject(async () => { + await psbtThatShouldsign.signAsync( + ECPair.fromWIF(f.shouldSign.WIF), + f.shouldSign.sighashTypes || undefined, + ) + }) + } - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) - assert.rejects(async () => { - await psbtThatShouldThrow.signAsync( - ECPair.fromWIF(f.shouldThrow.WIF), - ) - }, new RegExp('No inputs were signed')) - assert.rejects(async () => { - await psbtThatShouldThrow.signAsync() - }, new RegExp('Need Signer to sign input')) + if (f.shouldThrow) { + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.rejects(async () => { + await psbtThatShouldThrow.signAsync( + ECPair.fromWIF(f.shouldThrow.WIF), + f.shouldThrow.sighashTypes || undefined, + ) + }, new RegExp('No inputs were signed')) + assert.rejects(async () => { + await psbtThatShouldThrow.signAsync() + }, new RegExp('Need Signer to sign input')) + } }) }) }) @@ -246,22 +264,28 @@ describe(`Psbt`, () => { fixtures.signInput.checks.forEach(f => { if (f.description === 'checks the input exists') return it(f.description, () => { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) - assert.doesNotThrow(() => { - psbtThatShouldsign.sign( - ECPair.fromWIF(f.shouldSign.WIF), - ) - }) + if (f.shouldSign) { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotThrow(() => { + psbtThatShouldsign.sign( + ECPair.fromWIF(f.shouldSign.WIF), + f.shouldSign.sighashTypes || undefined, + ) + }) + } - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) - assert.throws(() => { - psbtThatShouldThrow.sign( - ECPair.fromWIF(f.shouldThrow.WIF), - ) - }, new RegExp('No inputs were signed')) - assert.throws(() => { - psbtThatShouldThrow.sign() - }, new RegExp('Need Signer to sign input')) + if (f.shouldThrow) { + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.throws(() => { + psbtThatShouldThrow.sign( + ECPair.fromWIF(f.shouldThrow.WIF), + f.shouldThrow.sighashTypes || undefined, + ) + }, new RegExp('No inputs were signed')) + assert.throws(() => { + psbtThatShouldThrow.sign() + }, new RegExp('Need Signer to sign input')) + } }) }) }) From d790288048eb73082fdfddb5ab4bff19e2b17987 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Thu, 11 Jul 2019 16:25:30 +0700 Subject: [PATCH 415/568] Test the sighashtype is checked when signing --- test/fixtures/psbt.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 1a37031..a29bc8b 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -428,6 +428,15 @@ "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" } }, + { + "description": "checks the sighash type", + "shouldThrow": { + "errorMessage": "Sighash type is not allowed. Retry the sign method passing the sighashTypes array of whitelisted types. Sighash type: SIGHASH_ANYONECANPAY | SIGHASH_ALL", + "psbt": "cHNidP8BADMBAAAAAYaq+PdOUY2PnV9kZKa82XlqrPByOqwH2TRg2+LQdqs2AAAAAAD/////AAAAAAAAAQEgAOH1BQAAAAAXqRSTNeWHqa9INvSnQ120SZeJc+6JSocBAwSBAAAAAQQWABQvLKRyDqYsPYImhD3eURpDGL10RwAA", + "inputToCheck": 0, + "WIF": "KxnAnQh6UJBxLF8Weup77yn8tWhLHhDhnXeyJuzmmcZA5aRdMJni" + } + }, { "description": "allows signing non-whitelisted sighashtype when explicitly passed in", "shouldSign": { From 71ddd656a3bc6a0416d9f4e486143aefc4110d77 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 18 Jul 2019 11:43:24 +0900 Subject: [PATCH 416/568] Modify for new BIP174 interface system --- package-lock.json | 5 +- package.json | 2 +- src/psbt.js | 241 ++++++++++++++-------------------- test/psbt.js | 28 ++-- ts_src/psbt.ts | 324 +++++++++++++++++----------------------------- types/psbt.d.ts | 20 +-- 6 files changed, 231 insertions(+), 389 deletions(-) diff --git a/package-lock.json b/package-lock.json index fabd361..3a7f8be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,8 @@ } }, "bip174": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.15.tgz", - "integrity": "sha512-mK/s9p7i+PG7W2s2cAedNVk1NDZQn9wAoq1DlsS2+1zz5TXR3TRTzqRqm9BQtOXwbkxkhfLwlmsOjuiRdAYkdg==" + "version": "git+https://github.com/bitcoinjs/bip174.git#5137e367c7a3a4e281ee01574f88977cdd4be896", + "from": "git+https://github.com/bitcoinjs/bip174.git#interface" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index a32021a..0b91e4e 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.15", + "bip174": "git+https://github.com/bitcoinjs/bip174.git#interface", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", diff --git a/src/psbt.js b/src/psbt.js index 578e17c..97e9e9d 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -16,7 +16,7 @@ const DEFAULT_OPTS = { maximumFeeRate: 5000, }; class Psbt { - constructor(opts = {}, data = new bip174_1.Psbt()) { + constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) { this.data = data; this.__CACHE = { __NON_WITNESS_UTXO_TX_CACHE: [], @@ -27,11 +27,11 @@ class Psbt { // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); const c = this.__CACHE; - c.__TX = transaction_1.Transaction.fromBuffer(data.globalMap.unsignedTx); + c.__TX = this.data.globalMap.unsignedTx.tx; if (this.data.inputs.length === 0) this.setVersion(2); // set cache - delete data.globalMap.unsignedTx; - Object.defineProperty(data.globalMap, 'unsignedTx', { + this.unsignedTx = Buffer.from([]); + Object.defineProperty(this, 'unsignedTx', { enumerable: true, get() { const buf = c.__TX_BUF_CACHE; @@ -56,24 +56,20 @@ class Psbt { dpew(this, 'opts', false, true); } static fromTransaction(txBuf, opts = {}) { - const tx = transaction_1.Transaction.fromBuffer(txBuf); - checkTxEmpty(tx); - const psbtBase = new bip174_1.Psbt(); + const tx = new PsbtTransaction(txBuf); + checkTxEmpty(tx.tx); + const psbtBase = new bip174_1.Psbt(tx); const psbt = new Psbt(opts, psbtBase); - psbt.__CACHE.__TX = tx; - checkTxForDupeIns(tx, psbt.__CACHE); - let inputCount = tx.ins.length; - let outputCount = tx.outs.length; + psbt.__CACHE.__TX = tx.tx; + checkTxForDupeIns(tx.tx, psbt.__CACHE); + let inputCount = tx.tx.ins.length; + let outputCount = tx.tx.outs.length; while (inputCount > 0) { - psbtBase.inputs.push({ - unknownKeyVals: [], - }); + psbtBase.inputs.push({}); inputCount--; } while (outputCount > 0) { - psbtBase.outputs.push({ - unknownKeyVals: [], - }); + psbtBase.outputs.push({}); outputCount--; } return psbt; @@ -87,16 +83,8 @@ class Psbt { return this.fromBuffer(buffer, opts); } static fromBuffer(buffer, opts = {}) { - let tx; - const txCountGetter = txBuf => { - tx = transaction_1.Transaction.fromBuffer(txBuf); - checkTxEmpty(tx); - return { - inputCount: tx.ins.length, - outputCount: tx.outs.length, - }; - }; - const psbtBase = bip174_1.Psbt.fromBuffer(buffer, txCountGetter); + const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer); + const tx = psbtBase.globalMap.unsignedTx.tx; const psbt = new Psbt(opts, psbtBase); psbt.__CACHE.__TX = tx; checkTxForDupeIns(tx, psbt.__CACHE); @@ -156,8 +144,9 @@ class Psbt { addInput(inputData) { checkInputsForPartialSig(this.data.inputs, 'addInput'); const c = this.__CACHE; - const inputAdder = getInputAdder(c); - this.data.addInput(inputData, inputAdder); + this.data.addInput(inputData); + const txIn = c.__TX.ins[c.__TX.ins.length - 1]; + checkTxInputCache(c, txIn); const inputIndex = this.data.inputs.length - 1; const input = this.data.inputs[inputIndex]; if (input.nonWitnessUtxo) { @@ -180,8 +169,7 @@ class Psbt { outputData = Object.assign(outputData, { script }); } const c = this.__CACHE; - const outputAdder = getOutputAdder(c); - this.data.addOutput(outputData, outputAdder, true); + this.data.addOutput(outputData); c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -238,10 +226,9 @@ class Psbt { isP2SH, isP2WSH, ); - if (finalScriptSig) - this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig); + if (finalScriptSig) this.data.updateInput(inputIndex, { finalScriptSig }); if (finalScriptWitness) - this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); + this.data.updateInput(inputIndex, { finalScriptWitness }); if (!finalScriptSig && !finalScriptWitness) throw new Error(`Unknown error finalizing input #${inputIndex}`); this.data.clearFinalizedInput(inputIndex); @@ -349,11 +336,13 @@ class Psbt { this.__CACHE, sighashTypes, ); - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(keyPair.sign(hash), sighashType), - }; - this.data.addPartialSigToInput(inputIndex, partialSig); + const partialSig = [ + { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(keyPair.sign(hash), sighashType), + }, + ]; + this.data.updateInput(inputIndex, { partialSig }); return this; } signInputAsync( @@ -372,11 +361,13 @@ class Psbt { sighashTypes, ); Promise.resolve(keyPair.sign(hash)).then(signature => { - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(signature, sighashType), - }; - this.data.addPartialSigToInput(inputIndex, partialSig); + const partialSig = [ + { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(signature, sighashType), + }, + ]; + this.data.updateInput(inputIndex, { partialSig }); resolve(); }); }); @@ -390,62 +381,23 @@ class Psbt { toBase64() { return this.data.toBase64(); } - addGlobalXpubToGlobal(globalXpub) { - this.data.addGlobalXpubToGlobal(globalXpub); + updateGlobal(updateData) { + this.data.updateGlobal(updateData); return this; } - addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) { - this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); - const input = this.data.inputs[inputIndex]; - addNonWitnessTxCache(this.__CACHE, input, inputIndex); + updateInput(inputIndex, updateData) { + this.data.updateInput(inputIndex, updateData); + if (updateData.nonWitnessUtxo) { + addNonWitnessTxCache( + this.__CACHE, + this.data.inputs[inputIndex], + inputIndex, + ); + } return this; } - addWitnessUtxoToInput(inputIndex, witnessUtxo) { - this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo); - return this; - } - addPartialSigToInput(inputIndex, partialSig) { - this.data.addPartialSigToInput(inputIndex, partialSig); - return this; - } - addSighashTypeToInput(inputIndex, sighashType) { - this.data.addSighashTypeToInput(inputIndex, sighashType); - return this; - } - addRedeemScriptToInput(inputIndex, redeemScript) { - this.data.addRedeemScriptToInput(inputIndex, redeemScript); - return this; - } - addWitnessScriptToInput(inputIndex, witnessScript) { - this.data.addWitnessScriptToInput(inputIndex, witnessScript); - return this; - } - addBip32DerivationToInput(inputIndex, bip32Derivation) { - this.data.addBip32DerivationToInput(inputIndex, bip32Derivation); - return this; - } - addFinalScriptSigToInput(inputIndex, finalScriptSig) { - this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig); - return this; - } - addFinalScriptWitnessToInput(inputIndex, finalScriptWitness) { - this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); - return this; - } - addPorCommitmentToInput(inputIndex, porCommitment) { - this.data.addPorCommitmentToInput(inputIndex, porCommitment); - return this; - } - addRedeemScriptToOutput(outputIndex, redeemScript) { - this.data.addRedeemScriptToOutput(outputIndex, redeemScript); - return this; - } - addWitnessScriptToOutput(outputIndex, witnessScript) { - this.data.addWitnessScriptToOutput(outputIndex, witnessScript); - return this; - } - addBip32DerivationToOutput(outputIndex, bip32Derivation) { - this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation); + updateOutput(outputIndex, updateData) { + this.data.updateOutput(outputIndex, updateData); return this; } addUnknownKeyValToGlobal(keyVal) { @@ -466,6 +418,54 @@ class Psbt { } } exports.Psbt = Psbt; +const transactionFromBuffer = buffer => new PsbtTransaction(buffer); +class PsbtTransaction { + constructor(buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) { + this.tx = transaction_1.Transaction.fromBuffer(buffer); + if (this.tx.ins.some(input => input.script.length !== 0)) { + throw new Error('Format Error: Transaction ScriptSigs are not empty'); + } + Object.defineProperty(this, 'tx', { + enumerable: false, + writable: true, + }); + } + getInputOutputCounts() { + return { + inputCount: this.tx.ins.length, + outputCount: this.tx.outs.length, + }; + } + addInput(input) { + if ( + input.hash === undefined || + input.index === undefined || + (!Buffer.isBuffer(input.hash) && typeof input.hash !== 'string') || + typeof input.index !== 'number' + ) { + throw new Error('Error adding input.'); + } + const hash = + typeof input.hash === 'string' + ? bufferutils_1.reverseBuffer(Buffer.from(input.hash, 'hex')) + : input.hash; + this.tx.addInput(hash, input.index, input.sequence); + } + addOutput(output) { + if ( + output.script === undefined || + output.value === undefined || + !Buffer.isBuffer(output.script) || + typeof output.value !== 'number' + ) { + throw new Error('Error adding output.'); + } + this.tx.addOutput(output.script, output.value); + } + toBuffer() { + return this.tx.toBuffer(); + } +} function canFinalize(input, script, scriptType) { switch (scriptType) { case 'pubkey': @@ -769,55 +769,6 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) { hash, }; } -function getInputAdder(cache) { - const selfCache = cache; - return (_inputData, txBuf) => { - if ( - !txBuf || - _inputData.hash === undefined || - _inputData.index === undefined || - (!Buffer.isBuffer(_inputData.hash) && - typeof _inputData.hash !== 'string') || - typeof _inputData.index !== 'number' - ) { - throw new Error('Error adding input.'); - } - const prevHash = Buffer.isBuffer(_inputData.hash) - ? _inputData.hash - : bufferutils_1.reverseBuffer(Buffer.from(_inputData.hash, 'hex')); - // Check if input already exists in cache. - const input = { hash: prevHash, index: _inputData.index }; - checkTxInputCache(selfCache, input); - selfCache.__TX.ins.push( - Object.assign({}, input, { - script: Buffer.alloc(0), - sequence: - _inputData.sequence || transaction_1.Transaction.DEFAULT_SEQUENCE, - witness: [], - }), - ); - return selfCache.__TX.toBuffer(); - }; -} -function getOutputAdder(cache) { - const selfCache = cache; - return (_outputData, txBuf) => { - if ( - !txBuf || - _outputData.script === undefined || - _outputData.value === undefined || - !Buffer.isBuffer(_outputData.script) || - typeof _outputData.value !== 'number' - ) { - throw new Error('Error adding output.'); - } - selfCache.__TX.outs.push({ - script: _outputData.script, - value: _outputData.value, - }); - return selfCache.__TX.toBuffer(); - }; -} function getPayment(script, scriptType, partialSig) { let payment; switch (scriptType) { diff --git a/test/psbt.js b/test/psbt.js index 8ed7b51..e52fae2 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -72,20 +72,8 @@ describe(`Psbt`, () => { const fixtureData = f[`${inputOrOutput}Data`] if (fixtureData) { for (const [i, data] of fixtureData.entries()) { - const attrs = Object.keys(data) - for (const attr of attrs) { - const upperAttr = upperCaseFirstLetter(attr) - let adder = psbt[`add${upperAttr}To${upperCaseFirstLetter(inputOrOutput)}`] - if (adder !== undefined) { - adder = adder.bind(psbt) - const arg = data[attr] - if (Array.isArray(arg)) { - arg.forEach(a => adder(i, a)) - } else { - adder(i, arg) - } - } - } + const txt = upperCaseFirstLetter(inputOrOutput) + psbt[`update${txt}`](i, data) } } } @@ -309,9 +297,11 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.finalizeAllInputs() }, new RegExp('No script found for input #0')) - psbt.addWitnessUtxoToInput(0, { - script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d', 'hex'), - value: 2e5 + psbt.updateInput(0, { + witnessUtxo: { + script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d', 'hex'), + value: 2e5 + } }) assert.throws(() => { psbt.finalizeAllInputs() @@ -438,7 +428,7 @@ describe(`Psbt`, () => { assert.strictEqual(clone.toBase64(), psbt.toBase64()) assert.strictEqual(clone.toBase64(), notAClone.toBase64()) assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) - psbt.data.globalMap.unsignedTx[3] = 0xff + psbt.__CACHE.__TX.version |= 0xff0000 assert.notStrictEqual(clone.toBase64(), psbt.toBase64()) assert.notStrictEqual(clone.toBase64(), notAClone.toBase64()) assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) @@ -565,7 +555,7 @@ describe(`Psbt`, () => { assert.strictEqual(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index], undefined) // Cache is populated - psbt.addNonWitnessUtxoToInput(index, f.nonWitnessUtxo) + psbt.updateInput(index, { nonWitnessUtxo: f.nonWitnessUtxo }) const value = psbt.data.inputs[index].nonWitnessUtxo assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value)) assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo)) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 5a73031..0c20bc2 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,21 +1,16 @@ import { Psbt as PsbtBase } from 'bip174'; import * as varuint from 'bip174/src/lib/converter/varint'; import { - Bip32Derivation, - FinalScriptSig, - FinalScriptWitness, - GlobalXpub, KeyValue, - NonWitnessUtxo, PartialSig, - PorCommitment, + PsbtGlobalUpdate, PsbtInput, - RedeemScript, - SighashType, + PsbtInputUpdate, + PsbtOutputUpdate, + Transaction as ITransaction, + TransactionFromBuffer, TransactionInput, TransactionOutput, - WitnessScript, - WitnessUtxo, } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; import { toOutputScript } from './address'; @@ -38,24 +33,20 @@ const DEFAULT_OPTS: PsbtOpts = { export class Psbt { static fromTransaction(txBuf: Buffer, opts: PsbtOptsOptional = {}): Psbt { - const tx = Transaction.fromBuffer(txBuf); - checkTxEmpty(tx); - const psbtBase = new PsbtBase(); + const tx = new PsbtTransaction(txBuf); + checkTxEmpty(tx.tx); + const psbtBase = new PsbtBase(tx); const psbt = new Psbt(opts, psbtBase); - psbt.__CACHE.__TX = tx; - checkTxForDupeIns(tx, psbt.__CACHE); - let inputCount = tx.ins.length; - let outputCount = tx.outs.length; + psbt.__CACHE.__TX = tx.tx; + checkTxForDupeIns(tx.tx, psbt.__CACHE); + let inputCount = tx.tx.ins.length; + let outputCount = tx.tx.outs.length; while (inputCount > 0) { - psbtBase.inputs.push({ - unknownKeyVals: [], - }); + psbtBase.inputs.push({}); inputCount--; } while (outputCount > 0) { - psbtBase.outputs.push({ - unknownKeyVals: [], - }); + psbtBase.outputs.push({}); outputCount--; } return psbt; @@ -72,27 +63,16 @@ export class Psbt { } static fromBuffer(buffer: Buffer, opts: PsbtOptsOptional = {}): Psbt { - let tx: Transaction | undefined; - const txCountGetter = ( - txBuf: Buffer, - ): { - inputCount: number; - outputCount: number; - } => { - tx = Transaction.fromBuffer(txBuf); - checkTxEmpty(tx); - return { - inputCount: tx.ins.length, - outputCount: tx.outs.length, - }; - }; - const psbtBase = PsbtBase.fromBuffer(buffer, txCountGetter); + const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer); + const tx: Transaction = (psbtBase.globalMap.unsignedTx as PsbtTransaction) + .tx; const psbt = new Psbt(opts, psbtBase); - psbt.__CACHE.__TX = tx!; - checkTxForDupeIns(tx!, psbt.__CACHE); + psbt.__CACHE.__TX = tx; + checkTxForDupeIns(tx, psbt.__CACHE); return psbt; } + unsignedTx: Buffer; private __CACHE: PsbtCache = { __NON_WITNESS_UTXO_TX_CACHE: [], __NON_WITNESS_UTXO_BUF_CACHE: [], @@ -103,17 +83,17 @@ export class Psbt { constructor( opts: PsbtOptsOptional = {}, - readonly data: PsbtBase = new PsbtBase(), + readonly data: PsbtBase = new PsbtBase(new PsbtTransaction()), ) { // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); const c = this.__CACHE; - c.__TX = Transaction.fromBuffer(data.globalMap.unsignedTx!); + c.__TX = (this.data.globalMap.unsignedTx as PsbtTransaction).tx; if (this.data.inputs.length === 0) this.setVersion(2); // set cache - delete data.globalMap.unsignedTx; - Object.defineProperty(data.globalMap, 'unsignedTx', { + this.unsignedTx = Buffer.from([]); + Object.defineProperty(this, 'unsignedTx', { enumerable: true, get(): Buffer { const buf = c.__TX_BUF_CACHE; @@ -206,8 +186,9 @@ export class Psbt { addInput(inputData: TransactionInput): this { checkInputsForPartialSig(this.data.inputs, 'addInput'); const c = this.__CACHE; - const inputAdder = getInputAdder(c); - this.data.addInput(inputData, inputAdder); + this.data.addInput(inputData); + const txIn = c.__TX.ins[c.__TX.ins.length - 1]; + checkTxInputCache(c, txIn); const inputIndex = this.data.inputs.length - 1; const input = this.data.inputs[inputIndex]; @@ -233,8 +214,7 @@ export class Psbt { outputData = Object.assign(outputData, { script }); } const c = this.__CACHE; - const outputAdder = getOutputAdder(c); - this.data.addOutput(outputData, outputAdder, true); + this.data.addOutput(outputData); c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -299,10 +279,9 @@ export class Psbt { isP2WSH, ); - if (finalScriptSig) - this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig); + if (finalScriptSig) this.data.updateInput(inputIndex, { finalScriptSig }); if (finalScriptWitness) - this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); + this.data.updateInput(inputIndex, { finalScriptWitness }); if (!finalScriptSig && !finalScriptWitness) throw new Error(`Unknown error finalizing input #${inputIndex}`); @@ -427,12 +406,14 @@ export class Psbt { sighashTypes, ); - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(keyPair.sign(hash), sighashType), - }; + const partialSig = [ + { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(keyPair.sign(hash), sighashType), + }, + ]; - this.data.addPartialSigToInput(inputIndex, partialSig); + this.data.updateInput(inputIndex, { partialSig }); return this; } @@ -454,12 +435,14 @@ export class Psbt { ); Promise.resolve(keyPair.sign(hash)).then(signature => { - const partialSig = { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(signature, sighashType), - }; + const partialSig = [ + { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(signature, sighashType), + }, + ]; - this.data.addPartialSigToInput(inputIndex, partialSig); + this.data.updateInput(inputIndex, { partialSig }); resolve(); }); }, @@ -478,102 +461,25 @@ export class Psbt { return this.data.toBase64(); } - addGlobalXpubToGlobal(globalXpub: GlobalXpub): this { - this.data.addGlobalXpubToGlobal(globalXpub); + updateGlobal(updateData: PsbtGlobalUpdate): this { + this.data.updateGlobal(updateData); return this; } - addNonWitnessUtxoToInput( - inputIndex: number, - nonWitnessUtxo: NonWitnessUtxo, - ): this { - this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo); - const input = this.data.inputs[inputIndex]; - addNonWitnessTxCache(this.__CACHE, input, inputIndex); + updateInput(inputIndex: number, updateData: PsbtInputUpdate): this { + this.data.updateInput(inputIndex, updateData); + if (updateData.nonWitnessUtxo) { + addNonWitnessTxCache( + this.__CACHE, + this.data.inputs[inputIndex], + inputIndex, + ); + } return this; } - addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this { - this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo); - return this; - } - - addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this { - this.data.addPartialSigToInput(inputIndex, partialSig); - return this; - } - - addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this { - this.data.addSighashTypeToInput(inputIndex, sighashType); - return this; - } - - addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this { - this.data.addRedeemScriptToInput(inputIndex, redeemScript); - return this; - } - - addWitnessScriptToInput( - inputIndex: number, - witnessScript: WitnessScript, - ): this { - this.data.addWitnessScriptToInput(inputIndex, witnessScript); - return this; - } - - addBip32DerivationToInput( - inputIndex: number, - bip32Derivation: Bip32Derivation, - ): this { - this.data.addBip32DerivationToInput(inputIndex, bip32Derivation); - return this; - } - - addFinalScriptSigToInput( - inputIndex: number, - finalScriptSig: FinalScriptSig, - ): this { - this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig); - return this; - } - - addFinalScriptWitnessToInput( - inputIndex: number, - finalScriptWitness: FinalScriptWitness, - ): this { - this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); - return this; - } - - addPorCommitmentToInput( - inputIndex: number, - porCommitment: PorCommitment, - ): this { - this.data.addPorCommitmentToInput(inputIndex, porCommitment); - return this; - } - - addRedeemScriptToOutput( - outputIndex: number, - redeemScript: RedeemScript, - ): this { - this.data.addRedeemScriptToOutput(outputIndex, redeemScript); - return this; - } - - addWitnessScriptToOutput( - outputIndex: number, - witnessScript: WitnessScript, - ): this { - this.data.addWitnessScriptToOutput(outputIndex, witnessScript); - return this; - } - - addBip32DerivationToOutput( - outputIndex: number, - bip32Derivation: Bip32Derivation, - ): this { - this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation); + updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this { + this.data.updateOutput(outputIndex, updateData); return this; } @@ -618,6 +524,67 @@ interface PsbtOpts { maximumFeeRate: number; } +const transactionFromBuffer: TransactionFromBuffer = ( + buffer: Buffer, +): ITransaction => new PsbtTransaction(buffer); + +class PsbtTransaction implements ITransaction { + tx: Transaction; + constructor(buffer: Buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) { + this.tx = Transaction.fromBuffer(buffer); + if (this.tx.ins.some(input => input.script.length !== 0)) { + throw new Error('Format Error: Transaction ScriptSigs are not empty'); + } + Object.defineProperty(this, 'tx', { + enumerable: false, + writable: true, + }); + } + + getInputOutputCounts(): { + inputCount: number; + outputCount: number; + } { + return { + inputCount: this.tx.ins.length, + outputCount: this.tx.outs.length, + }; + } + + addInput(input: any): void { + if ( + (input as any).hash === undefined || + (input as any).index === undefined || + (!Buffer.isBuffer((input as any).hash) && + typeof (input as any).hash !== 'string') || + typeof (input as any).index !== 'number' + ) { + throw new Error('Error adding input.'); + } + const hash = + typeof input.hash === 'string' + ? reverseBuffer(Buffer.from(input.hash, 'hex')) + : input.hash; + this.tx.addInput(hash, input.index, input.sequence); + } + + addOutput(output: any): void { + if ( + (output as any).script === undefined || + (output as any).value === undefined || + !Buffer.isBuffer((output as any).script) || + typeof (output as any).value !== 'number' + ) { + throw new Error('Error adding output.'); + } + this.tx.addOutput(output.script, output.value); + } + + toBuffer(): Buffer { + return this.tx.toBuffer(); + } +} + function canFinalize( input: PsbtInput, script: Buffer, @@ -979,61 +946,6 @@ function getHashForSig( }; } -function getInputAdder( - cache: PsbtCache, -): (_inputData: TransactionInput, txBuf: Buffer) => Buffer { - const selfCache = cache; - return (_inputData: TransactionInput, txBuf: Buffer): Buffer => { - if ( - !txBuf || - (_inputData as any).hash === undefined || - (_inputData as any).index === undefined || - (!Buffer.isBuffer((_inputData as any).hash) && - typeof (_inputData as any).hash !== 'string') || - typeof (_inputData as any).index !== 'number' - ) { - throw new Error('Error adding input.'); - } - const prevHash = Buffer.isBuffer(_inputData.hash) - ? _inputData.hash - : reverseBuffer(Buffer.from(_inputData.hash, 'hex')); - - // Check if input already exists in cache. - const input = { hash: prevHash, index: _inputData.index }; - checkTxInputCache(selfCache, input); - - selfCache.__TX.ins.push({ - ...input, - script: Buffer.alloc(0), - sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE, - witness: [], - }); - return selfCache.__TX.toBuffer(); - }; -} - -function getOutputAdder( - cache: PsbtCache, -): (_outputData: TransactionOutput, txBuf: Buffer) => Buffer { - const selfCache = cache; - return (_outputData: TransactionOutput, txBuf: Buffer): Buffer => { - if ( - !txBuf || - (_outputData as any).script === undefined || - (_outputData as any).value === undefined || - !Buffer.isBuffer((_outputData as any).script) || - typeof (_outputData as any).value !== 'number' - ) { - throw new Error('Error adding output.'); - } - selfCache.__TX.outs.push({ - script: (_outputData as any).script!, - value: _outputData.value, - }); - return selfCache.__TX.toBuffer(); - }; -} - function getPayment( script: Buffer, scriptType: string, diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 533b8fd..e537656 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,6 +1,6 @@ /// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; -import { Bip32Derivation, FinalScriptSig, FinalScriptWitness, GlobalXpub, KeyValue, NonWitnessUtxo, PartialSig, PorCommitment, RedeemScript, SighashType, TransactionInput, TransactionOutput, WitnessScript, WitnessUtxo } from 'bip174/src/lib/interfaces'; +import { KeyValue, PsbtGlobalUpdate, PsbtInputUpdate, PsbtOutputUpdate, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; @@ -10,6 +10,7 @@ export declare class Psbt { static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt; static fromHex(data: string, opts?: PsbtOptsOptional): Psbt; static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt; + unsignedTx: Buffer; private __CACHE; private opts; constructor(opts?: PsbtOptsOptional, data?: PsbtBase); @@ -37,20 +38,9 @@ export declare class Psbt { toBuffer(): Buffer; toHex(): string; toBase64(): string; - addGlobalXpubToGlobal(globalXpub: GlobalXpub): this; - addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this; - addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this; - addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this; - addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this; - addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this; - addWitnessScriptToInput(inputIndex: number, witnessScript: WitnessScript): this; - addBip32DerivationToInput(inputIndex: number, bip32Derivation: Bip32Derivation): this; - addFinalScriptSigToInput(inputIndex: number, finalScriptSig: FinalScriptSig): this; - addFinalScriptWitnessToInput(inputIndex: number, finalScriptWitness: FinalScriptWitness): this; - addPorCommitmentToInput(inputIndex: number, porCommitment: PorCommitment): this; - addRedeemScriptToOutput(outputIndex: number, redeemScript: RedeemScript): this; - addWitnessScriptToOutput(outputIndex: number, witnessScript: WitnessScript): this; - addBip32DerivationToOutput(outputIndex: number, bip32Derivation: Bip32Derivation): this; + updateGlobal(updateData: PsbtGlobalUpdate): this; + updateInput(inputIndex: number, updateData: PsbtInputUpdate): this; + updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this; addUnknownKeyValToGlobal(keyVal: KeyValue): this; addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this; addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this; From 19a33f7da868a65434c9dc486478f732ff771faf Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 18 Jul 2019 14:20:44 +0900 Subject: [PATCH 417/568] Add comments and remove fromTransaction --- src/psbt.js | 76 +++++++++++++++++++++++++++----------- test/fixtures/psbt.json | 6 --- test/psbt.js | 14 ------- ts_src/psbt.ts | 81 ++++++++++++++++++++++++++++------------- types/psbt.d.ts | 37 +++++++++++++++++-- 5 files changed, 144 insertions(+), 70 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 97e9e9d..a3191b4 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -11,10 +11,54 @@ const networks_1 = require('./networks'); const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); +/** + * These are the default arguments for a Psbt instance. + */ const DEFAULT_OPTS = { + /** + * A bitcoinjs Network object. This is only used if you pass an `address` + * parameter to addOutput. Otherwise it is not needed and can be left default. + */ network: networks_1.bitcoin, + /** + * When extractTransaction is called, the fee rate is checked. + * THIS IS NOT TO BE RELIED ON. + * It is only here as a last ditch effort to prevent sending a 500 BTC fee etc. + */ maximumFeeRate: 5000, }; +/** + * Psbt class can parse and generate a PSBT binary based off of the BIP174. + * There are 6 roles that this class fulfills. (Explained in BIP174) + * + * Creator: This can be done with `new Psbt()` + * Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`, + * `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to + * add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`, + * `psbt.updateInput(itemObject)`, `psbt.updateOutput(itemObject)` + * addInput requires hash: Buffer | string; and index: number; as attributes + * and can also include any attributes that are used in updateInput method. + * addOutput requires script: Buffer; and value: number; and likewise can include + * data for updateOutput. + * For a list of what attributes should be what types. Check the bip174 library. + * Also, check the integration tests for some examples of usage. + * Signer: There are a few methods. sign and signAsync, which will search all input + * information for your pubkey or pubkeyhash, and only sign inputs where it finds + * your info. Or you can explicitly sign a specific input with signInput and + * signInputAsync. For the async methods you can create a SignerAsync object + * and use something like a hardware wallet to sign with. (You must implement this) + * Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)` + * the psbt calling combine will always have precedence when a conflict occurs. + * Combine checks if the internal bitcoin transaction is the same, so be sure that + * all sequences, version, locktime, etc. are the same before combining. + * Input Finalizer: This role is fairly important. Not only does it need to construct + * the input scriptSigs and witnesses, but it SHOULD verify the signatures etc. + * Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()` + * Running any finalize method will delete any data in the input(s) that are no longer + * needed due to the finalized scripts containing the information. + * Transaction Extractor: This role will perform some checks before returning a + * Transaction object. Such as fee rate not being larger than maximumFeeRate etc. + */ class Psbt { constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) { this.data = data; @@ -55,25 +99,6 @@ class Psbt { dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } - static fromTransaction(txBuf, opts = {}) { - const tx = new PsbtTransaction(txBuf); - checkTxEmpty(tx.tx); - const psbtBase = new bip174_1.Psbt(tx); - const psbt = new Psbt(opts, psbtBase); - psbt.__CACHE.__TX = tx.tx; - checkTxForDupeIns(tx.tx, psbt.__CACHE); - let inputCount = tx.tx.ins.length; - let outputCount = tx.tx.outs.length; - while (inputCount > 0) { - psbtBase.inputs.push({}); - inputCount--; - } - while (outputCount > 0) { - psbtBase.outputs.push({}); - outputCount--; - } - return psbt; - } static fromBase64(data, opts = {}) { const buffer = Buffer.from(data, 'base64'); return this.fromBuffer(buffer, opts); @@ -418,13 +443,20 @@ class Psbt { } } exports.Psbt = Psbt; +/** + * This function is needed to pass to the bip174 base class's fromBuffer. + * It takes the "transaction buffer" portion of the psbt buffer and returns a + * Transaction (From the bip174 library) interface. + */ const transactionFromBuffer = buffer => new PsbtTransaction(buffer); +/** + * This class implements the Transaction interface from bip174 library. + * It contains a bitcoinjs-lib Transaction object. + */ class PsbtTransaction { constructor(buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) { this.tx = transaction_1.Transaction.fromBuffer(buffer); - if (this.tx.ins.some(input => input.script.length !== 0)) { - throw new Error('Format Error: Transaction ScriptSigs are not empty'); - } + checkTxEmpty(this.tx); Object.defineProperty(this, 'tx', { enumerable: false, writable: true, diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index a29bc8b..8d83e9d 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -480,12 +480,6 @@ "result": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8BCG4CSDBFAiEAs7TFGm6o/dpWSb4M/KSu2p7p881KA1uWn0r0wDCa0ckCIAdXlm9k3xWLj2f+j0hIXK+0ew9dc8qoHEL2babYeWliASMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PAQhuAkgwRQIhALpc3q2mj2ZiVl6PgFdJKHkPUrpogsF9DhYFZdSshLHrAiBVrEVSzPzLn3EnVXixnWpqsdf2ln4wmYspuXZlJp2KygEjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA=" } ], - "fromTransaction": [ - { - "transaction": "020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000", - "result": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA=" - } - ], "validateSignatures": { "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", "index": 0, diff --git a/test/psbt.js b/test/psbt.js index e52fae2..1837760 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -309,15 +309,6 @@ describe(`Psbt`, () => { }) }) - describe('fromTransaction', () => { - fixtures.fromTransaction.forEach(f => { - it('Creates the expected PSBT from a transaction buffer', () => { - const psbt = Psbt.fromTransaction(Buffer.from(f.transaction, 'hex')) - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) - }) - describe('addInput', () => { fixtures.addInput.checks.forEach(f => { it(f.description, () => { @@ -521,11 +512,6 @@ describe(`Psbt`, () => { }) describe('Method return types', () => { - it('fromTransaction returns Psbt type (not base class)', () => { - const psbt = Psbt.fromTransaction(Buffer.from([2,0,0,0,0,0,0,0,0,0])); - assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__CACHE.__TX); - }) it('fromBuffer returns Psbt type (not base class)', () => { const psbt = Psbt.fromBuffer(Buffer.from( '70736274ff01000a01000000000000000000000000', 'hex' //cHNidP8BAAoBAAAAAAAAAAAAAAAA diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 0c20bc2..97f615f 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -26,32 +26,56 @@ import * as payments from './payments'; import * as bscript from './script'; import { Output, Transaction } from './transaction'; +/** + * These are the default arguments for a Psbt instance. + */ const DEFAULT_OPTS: PsbtOpts = { + /** + * A bitcoinjs Network object. This is only used if you pass an `address` + * parameter to addOutput. Otherwise it is not needed and can be left default. + */ network: btcNetwork, + /** + * When extractTransaction is called, the fee rate is checked. + * THIS IS NOT TO BE RELIED ON. + * It is only here as a last ditch effort to prevent sending a 500 BTC fee etc. + */ maximumFeeRate: 5000, // satoshi per byte }; +/** + * Psbt class can parse and generate a PSBT binary based off of the BIP174. + * There are 6 roles that this class fulfills. (Explained in BIP174) + * + * Creator: This can be done with `new Psbt()` + * Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`, + * `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to + * add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`, + * `psbt.updateInput(itemObject)`, `psbt.updateOutput(itemObject)` + * addInput requires hash: Buffer | string; and index: number; as attributes + * and can also include any attributes that are used in updateInput method. + * addOutput requires script: Buffer; and value: number; and likewise can include + * data for updateOutput. + * For a list of what attributes should be what types. Check the bip174 library. + * Also, check the integration tests for some examples of usage. + * Signer: There are a few methods. sign and signAsync, which will search all input + * information for your pubkey or pubkeyhash, and only sign inputs where it finds + * your info. Or you can explicitly sign a specific input with signInput and + * signInputAsync. For the async methods you can create a SignerAsync object + * and use something like a hardware wallet to sign with. (You must implement this) + * Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)` + * the psbt calling combine will always have precedence when a conflict occurs. + * Combine checks if the internal bitcoin transaction is the same, so be sure that + * all sequences, version, locktime, etc. are the same before combining. + * Input Finalizer: This role is fairly important. Not only does it need to construct + * the input scriptSigs and witnesses, but it SHOULD verify the signatures etc. + * Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()` + * Running any finalize method will delete any data in the input(s) that are no longer + * needed due to the finalized scripts containing the information. + * Transaction Extractor: This role will perform some checks before returning a + * Transaction object. Such as fee rate not being larger than maximumFeeRate etc. + */ export class Psbt { - static fromTransaction(txBuf: Buffer, opts: PsbtOptsOptional = {}): Psbt { - const tx = new PsbtTransaction(txBuf); - checkTxEmpty(tx.tx); - const psbtBase = new PsbtBase(tx); - const psbt = new Psbt(opts, psbtBase); - psbt.__CACHE.__TX = tx.tx; - checkTxForDupeIns(tx.tx, psbt.__CACHE); - let inputCount = tx.tx.ins.length; - let outputCount = tx.tx.outs.length; - while (inputCount > 0) { - psbtBase.inputs.push({}); - inputCount--; - } - while (outputCount > 0) { - psbtBase.outputs.push({}); - outputCount--; - } - return psbt; - } - static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt { const buffer = Buffer.from(data, 'base64'); return this.fromBuffer(buffer, opts); @@ -356,7 +380,7 @@ export class Psbt { } signAsync( - keyPair: SignerAsync, + keyPair: Signer | SignerAsync, sighashTypes: number[] = [Transaction.SIGHASH_ALL], ): Promise<void> { return new Promise( @@ -419,7 +443,7 @@ export class Psbt { signInputAsync( inputIndex: number, - keyPair: SignerAsync, + keyPair: Signer | SignerAsync, sighashTypes: number[] = [Transaction.SIGHASH_ALL], ): Promise<void> { return new Promise( @@ -524,17 +548,24 @@ interface PsbtOpts { maximumFeeRate: number; } +/** + * This function is needed to pass to the bip174 base class's fromBuffer. + * It takes the "transaction buffer" portion of the psbt buffer and returns a + * Transaction (From the bip174 library) interface. + */ const transactionFromBuffer: TransactionFromBuffer = ( buffer: Buffer, ): ITransaction => new PsbtTransaction(buffer); +/** + * This class implements the Transaction interface from bip174 library. + * It contains a bitcoinjs-lib Transaction object. + */ class PsbtTransaction implements ITransaction { tx: Transaction; constructor(buffer: Buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) { this.tx = Transaction.fromBuffer(buffer); - if (this.tx.ins.some(input => input.script.length !== 0)) { - throw new Error('Format Error: Transaction ScriptSigs are not empty'); - } + checkTxEmpty(this.tx); Object.defineProperty(this, 'tx', { enumerable: false, writable: true, diff --git a/types/psbt.d.ts b/types/psbt.d.ts index e537656..c3a12d0 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -4,9 +4,40 @@ import { KeyValue, PsbtGlobalUpdate, PsbtInputUpdate, PsbtOutputUpdate, Transact import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; +/** + * Psbt class can parse and generate a PSBT binary based off of the BIP174. + * There are 6 roles that this class fulfills. (Explained in BIP174) + * + * Creator: This can be done with `new Psbt()` + * Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`, + * `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to + * add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`, + * `psbt.updateInput(itemObject)`, `psbt.updateOutput(itemObject)` + * addInput requires hash: Buffer | string; and index: number; as attributes + * and can also include any attributes that are used in updateInput method. + * addOutput requires script: Buffer; and value: number; and likewise can include + * data for updateOutput. + * For a list of what attributes should be what types. Check the bip174 library. + * Also, check the integration tests for some examples of usage. + * Signer: There are a few methods. sign and signAsync, which will search all input + * information for your pubkey or pubkeyhash, and only sign inputs where it finds + * your info. Or you can explicitly sign a specific input with signInput and + * signInputAsync. For the async methods you can create a SignerAsync object + * and use something like a hardware wallet to sign with. (You must implement this) + * Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)` + * the psbt calling combine will always have precedence when a conflict occurs. + * Combine checks if the internal bitcoin transaction is the same, so be sure that + * all sequences, version, locktime, etc. are the same before combining. + * Input Finalizer: This role is fairly important. Not only does it need to construct + * the input scriptSigs and witnesses, but it SHOULD verify the signatures etc. + * Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()` + * Running any finalize method will delete any data in the input(s) that are no longer + * needed due to the finalized scripts containing the information. + * Transaction Extractor: This role will perform some checks before returning a + * Transaction object. Such as fee rate not being larger than maximumFeeRate etc. + */ export declare class Psbt { readonly data: PsbtBase; - static fromTransaction(txBuf: Buffer, opts?: PsbtOptsOptional): Psbt; static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt; static fromHex(data: string, opts?: PsbtOptsOptional): Psbt; static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt; @@ -32,9 +63,9 @@ export declare class Psbt { validateAllSignatures(): boolean; validateSignatures(inputIndex: number, pubkey?: Buffer): boolean; sign(keyPair: Signer, sighashTypes?: number[]): this; - signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>; + signAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>; signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this; - signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>; + signInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>; toBuffer(): Buffer; toHex(): string; toBase64(): string; From def2182eaf862ef1d9e7a7bcc9e2333b390fad16 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 18 Jul 2019 15:57:00 +0900 Subject: [PATCH 418/568] Fix: integration test comments --- test/integration/transactions-psbt.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index 96ae073..7a3a85e 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -48,7 +48,9 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // value: 90000, // }, - // Not featured here: redeemScript. A Buffer of the redeemScript + // Not featured here: + // redeemScript. A Buffer of the redeemScript for P2SH + // witnessScript. A Buffer of the witnessScript for P2WSH }); psbt.addOutput({ address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', @@ -149,8 +151,6 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // This step it new. Since we separate the signing operation and // the creation of the scriptSig and witness stack, we are able to psbt.finalizeAllInputs(); - // it returns an array of the success of each input, also a result attribute - // which is true if all array items are true. // build and broadcast our RegTest network await regtestUtils.broadcast(psbt.extractTransaction().toHex()); From 1326e0cc4286b3d6f25f50fc67edd3826505d52b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 10:12:20 +0900 Subject: [PATCH 419/568] Remove the cached buffer getter --- src/psbt.js | 20 -------------------- ts_src/psbt.ts | 23 ----------------------- types/psbt.d.ts | 1 - 3 files changed, 44 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index a3191b4..dab6dbc 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -73,23 +73,6 @@ class Psbt { const c = this.__CACHE; c.__TX = this.data.globalMap.unsignedTx.tx; if (this.data.inputs.length === 0) this.setVersion(2); - // set cache - this.unsignedTx = Buffer.from([]); - Object.defineProperty(this, 'unsignedTx', { - enumerable: true, - get() { - const buf = c.__TX_BUF_CACHE; - if (buf !== undefined) { - return buf; - } else { - c.__TX_BUF_CACHE = c.__TX.toBuffer(); - return c.__TX_BUF_CACHE; - } - }, - set(_data) { - c.__TX_BUF_CACHE = _data; - }, - }); // Make data hidden when enumerating const dpew = (obj, attr, enumerable, writable) => Object.defineProperty(obj, attr, { @@ -137,7 +120,6 @@ class Psbt { checkInputsForPartialSig(this.data.inputs, 'setVersion'); const c = this.__CACHE; c.__TX.version = version; - c.__TX_BUF_CACHE = undefined; c.__EXTRACTED_TX = undefined; return this; } @@ -146,7 +128,6 @@ class Psbt { checkInputsForPartialSig(this.data.inputs, 'setLocktime'); const c = this.__CACHE; c.__TX.locktime = locktime; - c.__TX_BUF_CACHE = undefined; c.__EXTRACTED_TX = undefined; return this; } @@ -158,7 +139,6 @@ class Psbt { throw new Error('Input index too high'); } c.__TX.ins[inputIndex].sequence = sequence; - c.__TX_BUF_CACHE = undefined; c.__EXTRACTED_TX = undefined; return this; } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 97f615f..14dca4b 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -96,7 +96,6 @@ export class Psbt { return psbt; } - unsignedTx: Buffer; private __CACHE: PsbtCache = { __NON_WITNESS_UTXO_TX_CACHE: [], __NON_WITNESS_UTXO_BUF_CACHE: [], @@ -115,24 +114,6 @@ export class Psbt { c.__TX = (this.data.globalMap.unsignedTx as PsbtTransaction).tx; if (this.data.inputs.length === 0) this.setVersion(2); - // set cache - this.unsignedTx = Buffer.from([]); - Object.defineProperty(this, 'unsignedTx', { - enumerable: true, - get(): Buffer { - const buf = c.__TX_BUF_CACHE; - if (buf !== undefined) { - return buf; - } else { - c.__TX_BUF_CACHE = c.__TX.toBuffer(); - return c.__TX_BUF_CACHE; - } - }, - set(_data: Buffer): void { - c.__TX_BUF_CACHE = _data; - }, - }); - // Make data hidden when enumerating const dpew = ( obj: any, @@ -174,7 +155,6 @@ export class Psbt { checkInputsForPartialSig(this.data.inputs, 'setVersion'); const c = this.__CACHE; c.__TX.version = version; - c.__TX_BUF_CACHE = undefined; c.__EXTRACTED_TX = undefined; return this; } @@ -184,7 +164,6 @@ export class Psbt { checkInputsForPartialSig(this.data.inputs, 'setLocktime'); const c = this.__CACHE; c.__TX.locktime = locktime; - c.__TX_BUF_CACHE = undefined; c.__EXTRACTED_TX = undefined; return this; } @@ -197,7 +176,6 @@ export class Psbt { throw new Error('Input index too high'); } c.__TX.ins[inputIndex].sequence = sequence; - c.__TX_BUF_CACHE = undefined; c.__EXTRACTED_TX = undefined; return this; } @@ -533,7 +511,6 @@ interface PsbtCache { __NON_WITNESS_UTXO_BUF_CACHE: Buffer[]; __TX_IN_CACHE: { [index: string]: number }; __TX: Transaction; - __TX_BUF_CACHE?: Buffer; __FEE_RATE?: number; __EXTRACTED_TX?: Transaction; } diff --git a/types/psbt.d.ts b/types/psbt.d.ts index c3a12d0..2b24e65 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -41,7 +41,6 @@ export declare class Psbt { static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt; static fromHex(data: string, opts?: PsbtOptsOptional): Psbt; static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt; - unsignedTx: Buffer; private __CACHE; private opts; constructor(opts?: PsbtOptsOptional, data?: PsbtBase); From 4366b621d7db918b5fa8e59db285ddfefa4f432a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 11:42:45 +0900 Subject: [PATCH 420/568] Add HD signer methods --- src/psbt.js | 108 ++++++++++++++++++++++++++ test/fixtures/psbt.json | 46 +++++++++++ test/psbt.js | 125 ++++++++++++++++++++++++++++++ ts_src/psbt.ts | 166 ++++++++++++++++++++++++++++++++++++++++ types/psbt.d.ts | 33 ++++++++ 5 files changed, 478 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index dab6dbc..f183bbc 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -278,6 +278,86 @@ class Psbt { } return results.every(res => res === true); } + signHD(hdKeyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) { + if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) { + throw new Error('Need HDSigner to sign input'); + } + const results = []; + for (const i of range(this.data.inputs.length)) { + try { + this.signInputHD(i, hdKeyPair, sighashTypes); + results.push(true); + } catch (err) { + results.push(false); + } + } + if (results.every(v => v === false)) { + throw new Error('No inputs were signed'); + } + return this; + } + signHDAsync( + hdKeyPair, + sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], + ) { + return new Promise((resolve, reject) => { + if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) { + return reject(new Error('Need HDSigner to sign input')); + } + const results = []; + const promises = []; + for (const i of range(this.data.inputs.length)) { + promises.push( + this.signInputHDAsync(i, hdKeyPair, sighashTypes).then( + () => { + results.push(true); + }, + () => { + results.push(false); + }, + ), + ); + } + return Promise.all(promises).then(() => { + if (results.every(v => v === false)) { + return reject(new Error('No inputs were signed')); + } + resolve(); + }); + }); + } + signInputHD( + inputIndex, + hdKeyPair, + sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], + ) { + if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) { + throw new Error('Need HDSigner to sign input'); + } + const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair); + signers.forEach(signer => this.signInput(inputIndex, signer, sighashTypes)); + return this; + } + signInputHDAsync( + inputIndex, + hdKeyPair, + sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], + ) { + return new Promise((resolve, reject) => { + if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) { + return reject(new Error('Need HDSigner to sign input')); + } + const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair); + const promises = signers.map(signer => + this.signInputAsync(inputIndex, signer, sighashTypes), + ); + return Promise.all(promises) + .then(() => { + resolve(); + }) + .catch(reject); + }); + } sign(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); @@ -853,6 +933,34 @@ function getScriptFromInput(inputIndex, input, cache) { } return res; } +function getSignersFromHD(inputIndex, inputs, hdKeyPair) { + const input = utils_1.checkForInput(inputs, inputIndex); + if (!input.bip32Derivation || input.bip32Derivation.length === 0) { + throw new Error('Need bip32Derivation to sign with HD'); + } + const myDerivations = input.bip32Derivation + .map(bipDv => { + if (bipDv.masterFingerprint.equals(hdKeyPair.fingerprint)) { + return bipDv; + } else { + return; + } + }) + .filter(v => !!v); + if (myDerivations.length === 0) { + throw new Error( + 'Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint', + ); + } + const signers = myDerivations.map(bipDv => { + const node = hdKeyPair.derivePath(bipDv.path); + if (!bipDv.pubkey.equals(node.publicKey)) { + throw new Error('pubkey did not match bip32Derivation'); + } + return node; + }); + return signers; +} function getSortedSigs(script, partialSig) { const p2ms = payments.p2ms({ output: script }); // for each pubkey in order of p2ms script diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 8d83e9d..528e922 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -448,6 +448,52 @@ } ] }, + "signInputHD": { + "checks": [ + { + "description": "checks the bip32Derivation exists", + "shouldSign": { + "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYDn85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kYBCppsiwAAIAAAACAAAAAgAAAAAAAAAAAAAA=", + "inputToCheck": 0, + "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev" + }, + "shouldThrow": { + "errorMessage": "Need bip32Derivation to sign with HD", + "psbt": "cHNidP8BADMBAAAAAXVa+rWvBGNyifYXEMlTten9+qC0xuHcAMxQYrQTwX1dAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFC8spHIOpiw9giaEPd5RGkMYvXRHiKwAAAAAAAA=", + "inputToCheck": 0, + "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev" + } + }, + { + "description": "checks the bip32Derivation exists", + "shouldSign": { + "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYDn85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kYBCppsiwAAIAAAACAAAAAgAAAAAAAAAAAAAA=", + "inputToCheck": 0, + "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev" + }, + "shouldThrow": { + "errorMessage": "Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint", + "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYD/85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kY/////ywAAIAAAACAAAAAgAAAAAAAAAAAAAA=", + "inputToCheck": 0, + "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev" + } + }, + { + "description": "checks the bip32Derivation exists", + "shouldSign": { + "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYDn85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kYBCppsiwAAIAAAACAAAAAgAAAAAAAAAAAAAA=", + "inputToCheck": 0, + "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev" + }, + "shouldThrow": { + "errorMessage": "pubkey did not match bip32Derivation", + "psbt": "cHNidP8BADMBAAAAARtEptsZNydT9Bh9A5ptwIZz87yH8NXwzr1bjJorAZEAAAAAAAD/////AAAAAAAAAQDAAgAAAAH//////////////////////////////////////////wAAAABrSDBFAiEAjtCPUj0vx3I5HFQKAUWHN0vCnT17jd41/omb4nobq/sCIAilSeQVi4mqykgBbs+Wz6PyqdMThi2gT463v4kPWk6cASECaSihTgej6zyYUQLWkPnBx68mOUGCIuXcWbZDMArbhWH/////AQDh9QUAAAAAGXapFJWXNY2Vp7E5pNOey64rnhhgUlohiKwAAAAAIgYD/85vSg5rlR25fd4MOU2ANsFoO+q828zuOI/5b8tj89kYBCppsiwAAIAAAACAAAAAgAAAAAAAAAAAAAA=", + "inputToCheck": 0, + "xprv": "xprv9s21ZrQH143K2XNCa3o3tii6nbyJAET6GjTfzcF6roTjAMzLUBe8nt7QHNYqKah8JBv8V67MTWBCqPptRr6khjTSvCUVru78KHW13Viwnev" + } + } + ] + }, "finalizeAllInputs": [ { "type": "P2PK", diff --git a/test/psbt.js b/test/psbt.js index 1837760..18a4c99 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -1,6 +1,7 @@ const { describe, it } = require('mocha') const assert = require('assert') +const bip32 = require('bip32') const ECPair = require('../src/ecpair') const Psbt = require('..').Psbt const NETWORKS = require('../src/networks') @@ -278,6 +279,130 @@ describe(`Psbt`, () => { }) }) + describe('signInputHDAsync', () => { + fixtures.signInputHD.checks.forEach(f => { + it(f.description, async () => { + if (f.shouldSign) { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotReject(async () => { + await psbtThatShouldsign.signInputHDAsync( + f.shouldSign.inputToCheck, + bip32.fromBase58(f.shouldSign.xprv), + f.shouldSign.sighashTypes || undefined, + ) + }) + } + + if (f.shouldThrow) { + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.rejects(async () => { + await psbtThatShouldThrow.signInputHDAsync( + f.shouldThrow.inputToCheck, + bip32.fromBase58(f.shouldThrow.xprv), + f.shouldThrow.sighashTypes || undefined, + ) + }, new RegExp(f.shouldThrow.errorMessage)) + assert.rejects(async () => { + await psbtThatShouldThrow.signInputHDAsync( + f.shouldThrow.inputToCheck, + ) + }, new RegExp('Need HDSigner to sign input')) + } + }) + }) + }) + + describe('signInputHD', () => { + fixtures.signInputHD.checks.forEach(f => { + it(f.description, () => { + if (f.shouldSign) { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotThrow(() => { + psbtThatShouldsign.signInputHD( + f.shouldSign.inputToCheck, + bip32.fromBase58(f.shouldSign.xprv), + f.shouldSign.sighashTypes || undefined, + ) + }) + } + + if (f.shouldThrow) { + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.throws(() => { + psbtThatShouldThrow.signInputHD( + f.shouldThrow.inputToCheck, + bip32.fromBase58(f.shouldThrow.xprv), + f.shouldThrow.sighashTypes || undefined, + ) + }, new RegExp(f.shouldThrow.errorMessage)) + assert.throws(() => { + psbtThatShouldThrow.signInputHD( + f.shouldThrow.inputToCheck, + ) + }, new RegExp('Need HDSigner to sign input')) + } + }) + }) + }) + + describe('signHDAsync', () => { + fixtures.signInputHD.checks.forEach(f => { + it(f.description, async () => { + if (f.shouldSign) { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotReject(async () => { + await psbtThatShouldsign.signHDAsync( + bip32.fromBase58(f.shouldSign.xprv), + f.shouldSign.sighashTypes || undefined, + ) + }) + } + + if (f.shouldThrow) { + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.rejects(async () => { + await psbtThatShouldThrow.signHDAsync( + bip32.fromBase58(f.shouldThrow.xprv), + f.shouldThrow.sighashTypes || undefined, + ) + }, new RegExp('No inputs were signed')) + assert.rejects(async () => { + await psbtThatShouldThrow.signHDAsync() + }, new RegExp('Need HDSigner to sign input')) + } + }) + }) + }) + + describe('signHD', () => { + fixtures.signInputHD.checks.forEach(f => { + it(f.description, () => { + if (f.shouldSign) { + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + assert.doesNotThrow(() => { + psbtThatShouldsign.signHD( + bip32.fromBase58(f.shouldSign.xprv), + f.shouldSign.sighashTypes || undefined, + ) + }) + } + + if (f.shouldThrow) { + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + assert.throws(() => { + psbtThatShouldThrow.signHD( + bip32.fromBase58(f.shouldThrow.xprv), + f.shouldThrow.sighashTypes || undefined, + ) + }, new RegExp('No inputs were signed')) + assert.throws(() => { + psbtThatShouldThrow.signHD() + }, new RegExp('Need HDSigner to sign input')) + } + }) + }) + }) + describe('finalizeAllInputs', () => { fixtures.finalizeAllInputs.forEach(f => { it(`Finalizes inputs of type "${f.type}"`, () => { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 14dca4b..0778226 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -332,6 +332,107 @@ export class Psbt { return results.every(res => res === true); } + signHD( + hdKeyPair: HDSigner, + sighashTypes: number[] = [Transaction.SIGHASH_ALL], + ): this { + if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) { + throw new Error('Need HDSigner to sign input'); + } + + const results: boolean[] = []; + for (const i of range(this.data.inputs.length)) { + try { + this.signInputHD(i, hdKeyPair, sighashTypes); + results.push(true); + } catch (err) { + results.push(false); + } + } + if (results.every(v => v === false)) { + throw new Error('No inputs were signed'); + } + return this; + } + + signHDAsync( + hdKeyPair: HDSigner | HDSignerAsync, + sighashTypes: number[] = [Transaction.SIGHASH_ALL], + ): Promise<void> { + return new Promise( + (resolve, reject): any => { + if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) { + return reject(new Error('Need HDSigner to sign input')); + } + + const results: boolean[] = []; + const promises: Array<Promise<void>> = []; + for (const i of range(this.data.inputs.length)) { + promises.push( + this.signInputHDAsync(i, hdKeyPair, sighashTypes).then( + () => { + results.push(true); + }, + () => { + results.push(false); + }, + ), + ); + } + return Promise.all(promises).then(() => { + if (results.every(v => v === false)) { + return reject(new Error('No inputs were signed')); + } + resolve(); + }); + }, + ); + } + + signInputHD( + inputIndex: number, + hdKeyPair: HDSigner, + sighashTypes: number[] = [Transaction.SIGHASH_ALL], + ): this { + if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) { + throw new Error('Need HDSigner to sign input'); + } + const signers = getSignersFromHD( + inputIndex, + this.data.inputs, + hdKeyPair, + ) as Signer[]; + signers.forEach(signer => this.signInput(inputIndex, signer, sighashTypes)); + return this; + } + + signInputHDAsync( + inputIndex: number, + hdKeyPair: HDSigner | HDSignerAsync, + sighashTypes: number[] = [Transaction.SIGHASH_ALL], + ): Promise<void> { + return new Promise( + (resolve, reject): any => { + if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) { + return reject(new Error('Need HDSigner to sign input')); + } + const signers = getSignersFromHD( + inputIndex, + this.data.inputs, + hdKeyPair, + ); + const promises = signers.map(signer => + this.signInputAsync(inputIndex, signer, sighashTypes), + ); + return Promise.all(promises) + .then(() => { + resolve(); + }) + .catch(reject); + }, + ); + } + sign( keyPair: Signer, sighashTypes: number[] = [Transaction.SIGHASH_ALL], @@ -525,6 +626,38 @@ interface PsbtOpts { maximumFeeRate: number; } +interface HDSignerBase { + /** + * DER format compressed publicKey buffer + */ + publicKey: Buffer; + /** + * The first 4 bytes of the sha256-ripemd160 of the publicKey + */ + fingerprint: Buffer; +} + +interface HDSigner extends HDSignerBase { + /** + * The path string must match /^m(\/\d+'?)+$/ + * ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations + */ + derivePath(path: string): HDSigner; + /** + * Input hash (the "message digest") for the signature algorithm + * Return a 64 byte signature (32 byte r and 32 byte s in that order) + */ + sign(hash: Buffer): Buffer; +} + +/** + * Same as above but with async sign method + */ +interface HDSignerAsync extends HDSignerBase { + derivePath(path: string): HDSignerAsync; + sign(hash: Buffer): Promise<Buffer>; +} + /** * This function is needed to pass to the bip174 base class's fromBuffer. * It takes the "transaction buffer" portion of the psbt buffer and returns a @@ -1042,6 +1175,39 @@ function getScriptFromInput( return res; } +function getSignersFromHD( + inputIndex: number, + inputs: PsbtInput[], + hdKeyPair: HDSigner | HDSignerAsync, +): Array<Signer | SignerAsync> { + const input = checkForInput(inputs, inputIndex); + if (!input.bip32Derivation || input.bip32Derivation.length === 0) { + throw new Error('Need bip32Derivation to sign with HD'); + } + const myDerivations = input.bip32Derivation + .map(bipDv => { + if (bipDv.masterFingerprint.equals(hdKeyPair.fingerprint)) { + return bipDv; + } else { + return; + } + }) + .filter(v => !!v); + if (myDerivations.length === 0) { + throw new Error( + 'Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint', + ); + } + const signers: Array<Signer | SignerAsync> = myDerivations.map(bipDv => { + const node = hdKeyPair.derivePath(bipDv!.path); + if (!bipDv!.pubkey.equals(node.publicKey)) { + throw new Error('pubkey did not match bip32Derivation'); + } + return node; + }); + return signers; +} + function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] { const p2ms = payments.p2ms({ output: script }); // for each pubkey in order of p2ms script diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 2b24e65..9557f79 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -61,6 +61,10 @@ export declare class Psbt { finalizeInput(inputIndex: number): this; validateAllSignatures(): boolean; validateSignatures(inputIndex: number, pubkey?: Buffer): boolean; + signHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; + signHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>; + signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this; + signInputHDAsync(inputIndex: number, hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>; sign(keyPair: Signer, sighashTypes?: number[]): this; signAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>; signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this; @@ -80,4 +84,33 @@ interface PsbtOptsOptional { network?: Network; maximumFeeRate?: number; } +interface HDSignerBase { + /** + * DER format compressed publicKey buffer + */ + publicKey: Buffer; + /** + * The first 4 bytes of the sha256-ripemd160 of the publicKey + */ + fingerprint: Buffer; +} +interface HDSigner extends HDSignerBase { + /** + * The path string must match /^m(\/\d+'?)+$/ + * ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations + */ + derivePath(path: string): HDSigner; + /** + * Input hash (the "message digest") for the signature algorithm + * Return a 64 byte signature (32 byte r and 32 byte s in that order) + */ + sign(hash: Buffer): Buffer; +} +/** + * Same as above but with async sign method + */ +interface HDSignerAsync extends HDSignerBase { + derivePath(path: string): HDSignerAsync; + sign(hash: Buffer): Promise<Buffer>; +} export {}; From 1c5b0025c8aedc63a201a63046e7cb7ac5712850 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 12:03:32 +0900 Subject: [PATCH 421/568] 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 => { From acf59f167c369cdf771ddbd06f8e8fa20220a447 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 12:41:12 +0900 Subject: [PATCH 422/568] Use bip174@1.0.0 --- package-lock.json | 5 +++-- package.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3a7f8be..8e641b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,8 +200,9 @@ } }, "bip174": { - "version": "git+https://github.com/bitcoinjs/bip174.git#5137e367c7a3a4e281ee01574f88977cdd4be896", - "from": "git+https://github.com/bitcoinjs/bip174.git#interface" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-1.0.0.tgz", + "integrity": "sha512-AaoWrkYtv6A2y8H+qzs6NvRWypzNbADT8PQGpM9rnP+jLzeol+uzhe3Myeuq/dwrHYtmsW8V71HmX2oXhQGagw==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index 0b91e4e..806de55 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "git+https://github.com/bitcoinjs/bip174.git#interface", + "bip174": "^1.0.0", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", From e19bc58b303fea38a8f7d58d9cea943495f540f1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 14:53:54 +0900 Subject: [PATCH 423/568] Rename methods --- src/psbt.js | 26 +++++++++++------- test/fixtures/psbt.json | 2 +- test/integration/transactions-psbt.js | 34 ++++++++++++------------ test/psbt.js | 38 +++++++++++++-------------- ts_src/psbt.ts | 20 +++++++------- types/psbt.d.ts | 14 +++++----- 6 files changed, 70 insertions(+), 64 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index f183bbc..31ac164 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -42,7 +42,7 @@ const DEFAULT_OPTS = { * data for updateOutput. * For a list of what attributes should be what types. Check the bip174 library. * Also, check the integration tests for some examples of usage. - * Signer: There are a few methods. sign and signAsync, which will search all input + * Signer: There are a few methods. signAllInputs and signAsync, which will search all input * information for your pubkey or pubkeyhash, and only sign inputs where it finds * your info. Or you can explicitly sign a specific input with signInput and * signInputAsync. For the async methods you can create a SignerAsync object @@ -53,7 +53,7 @@ const DEFAULT_OPTS = { * all sequences, version, locktime, etc. are the same before combining. * Input Finalizer: This role is fairly important. Not only does it need to construct * the input scriptSigs and witnesses, but it SHOULD verify the signatures etc. - * Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()` + * Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()` * Running any finalize method will delete any data in the input(s) that are no longer * needed due to the finalized scripts containing the information. * Transaction Extractor: This role will perform some checks before returning a @@ -131,9 +131,9 @@ class Psbt { c.__EXTRACTED_TX = undefined; return this; } - setSequence(inputIndex, sequence) { + setInputSequence(inputIndex, sequence) { check32Bit(sequence); - checkInputsForPartialSig(this.data.inputs, 'setSequence'); + checkInputsForPartialSig(this.data.inputs, 'setInputSequence'); const c = this.__CACHE; if (c.__TX.ins.length <= inputIndex) { throw new Error('Input index too high'); @@ -239,14 +239,14 @@ class Psbt { this.data.clearFinalizedInput(inputIndex); return this; } - validateAllSignatures() { + validateSignaturesOfAllInputs() { utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one const results = range(this.data.inputs.length).map(idx => - this.validateSignatures(idx), + this.validateSignaturesOfInput(idx), ); return results.reduce((final, res) => res === true && final, true); } - validateSignatures(inputIndex, pubkey) { + validateSignaturesOfInput(inputIndex, pubkey) { const input = this.data.inputs[inputIndex]; const partialSig = (input || {}).partialSig; if (!input || !partialSig || partialSig.length < 1) @@ -278,7 +278,10 @@ class Psbt { } return results.every(res => res === true); } - signHD(hdKeyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) { + signAllInputsHD( + hdKeyPair, + sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], + ) { if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) { throw new Error('Need HDSigner to sign input'); } @@ -358,7 +361,10 @@ class Psbt { .catch(reject); }); } - sign(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) { + signAllInputs( + keyPair, + sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], + ) { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); // TODO: Add a pubkey/pubkeyhash cache to each input @@ -635,7 +641,7 @@ function checkInputsForPartialSig(inputs, action) { case transaction_1.Transaction.SIGHASH_SINGLE: case transaction_1.Transaction.SIGHASH_NONE: whitelist.push('addOutput'); - whitelist.push('setSequence'); + whitelist.push('setInputSequence'); break; } if (whitelist.indexOf(action) === -1) { diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 528e922..22655da 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -526,7 +526,7 @@ "result": "cHNidP8BAKYCAAAAAlwKQ3suPWwEJ/zQ9sZsIioOcHKU1KoLMxlMNSXVIkEWAAAAAAD/////YYDJMap+mYgbTrCNAdpWHN+EkKvl+XYao/6co/EQfwMAAAAAAP////8CkF8BAAAAAAAWABRnPBAmVHz2HL+8/1U+QG5L2thjmjhKAAAAAAAAIgAg700yfFRyhWzQnPHIUb/XQqsjlpf4A0uw682pCVWuQ8IAAAAAAAEBKzB1AAAAAAAAIgAgth9oE4cDfC5aV58VgkW5CptHsIxppYzJV8C5kT6aTo8BCG4CSDBFAiEAs7TFGm6o/dpWSb4M/KSu2p7p881KA1uWn0r0wDCa0ckCIAdXlm9k3xWLj2f+j0hIXK+0ew9dc8qoHEL2babYeWliASMhA182CcWcQ9L2zg9j8jlt7OlaIATNDgNFMBKB1J14wzTprAABASuAOAEAAAAAACIAILYfaBOHA3wuWlefFYJFuQqbR7CMaaWMyVfAuZE+mk6PAQhuAkgwRQIhALpc3q2mj2ZiVl6PgFdJKHkPUrpogsF9DhYFZdSshLHrAiBVrEVSzPzLn3EnVXixnWpqsdf2ln4wmYspuXZlJp2KygEjIQNfNgnFnEPS9s4PY/I5bezpWiAEzQ4DRTASgdSdeMM06awAAAA=" } ], - "validateSignatures": { + "validateSignaturesOfInput": { "psbt": "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgf0cwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMASICAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgIDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtxHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwEiAgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc0cwRAIgZfRbpZmLWaJ//hp77QFq8fH5DVSzqo90UKpfVqJRA70CIH9yRwOtHtuWaAsoS1bU/8uI9/t1nqu+CKow8puFE4PSAQEDBAEAAAABBCIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQVHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4iBgI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8OcxDZDGpPAAAAgAAAAIADAACAIgYDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwQ2QxqTwAAAIAAAACAAgAAgAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", "index": 0, "pubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f', 'hex')", diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index 0e4bfe7..07035ab 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -59,7 +59,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { value: 80000, }); psbt.signInput(0, alice); - psbt.validateSignatures(0); + psbt.validateSignaturesOfInput(0); psbt.finalizeAllInputs(); assert.strictEqual( psbt.extractTransaction().toHex(), @@ -128,8 +128,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // Alice signs each input with the respective private keys // signInput and signInputAsync are better // (They take the input index explicitly as the first arg) - signer1.sign(alice1.keys[0]); - signer2.sign(alice2.keys[0]); + signer1.signAllInputs(alice1.keys[0]); + signer2.signAllInputs(alice2.keys[0]); // If your signer object's sign method returns a promise, use the following // await signer2.signAsync(alice2.keys[0]) @@ -147,8 +147,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // Finalizer wants to check all signatures are valid before finalizing. // If the finalizer wants to check for specific pubkeys, the second arg // can be passed. See the first multisig example below. - assert.strictEqual(psbt.validateSignatures(0), true); - assert.strictEqual(psbt.validateSignatures(1), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(1), true); // This step it new. Since we separate the signing operation and // the creation of the scriptSig and witness stack, we are able to @@ -183,7 +183,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, alice1.keys[0]); - assert.strictEqual(psbt.validateSignatures(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); psbt.finalizeAllInputs(); // build and broadcast to the RegTest network @@ -215,13 +215,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .signInput(0, multisig.keys[0]) .signInput(0, multisig.keys[2]); - assert.strictEqual(psbt.validateSignatures(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); assert.strictEqual( - psbt.validateSignatures(0, multisig.keys[0].publicKey), + psbt.validateSignaturesOfInput(0, multisig.keys[0].publicKey), true, ); assert.throws(() => { - psbt.validateSignatures(0, multisig.keys[3].publicKey); + psbt.validateSignaturesOfInput(0, multisig.keys[3].publicKey); }, new RegExp('No signatures for this pubkey')); psbt.finalizeAllInputs(); @@ -267,7 +267,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { const tx = new bitcoin.Psbt() .addInputs([inputData, inputData2]) .addOutputs([outputData, outputData2]) - .sign(keyPair) + .signAllInputs(keyPair) .finalizeAllInputs() .extractTransaction(); @@ -300,7 +300,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, p2wpkh.keys[0]); - assert.strictEqual(psbt.validateSignatures(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); psbt.finalizeAllInputs(); const tx = psbt.extractTransaction(); @@ -340,7 +340,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, p2wsh.keys[0]); - assert.strictEqual(psbt.validateSignatures(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); psbt.finalizeAllInputs(); const tx = psbt.extractTransaction(); @@ -383,13 +383,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .signInput(0, p2sh.keys[2]) .signInput(0, p2sh.keys[3]); - assert.strictEqual(psbt.validateSignatures(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); assert.strictEqual( - psbt.validateSignatures(0, p2sh.keys[3].publicKey), + psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey), true, ); assert.throws(() => { - psbt.validateSignatures(0, p2sh.keys[1].publicKey); + psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey); }, new RegExp('No signatures for this pubkey')); psbt.finalizeAllInputs(); @@ -449,8 +449,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInputHD(0, hdRoot); // must sign with root!!! - assert.strictEqual(psbt.validateSignatures(0), true); - assert.strictEqual(psbt.validateSignatures(0, childNode.publicKey), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0, childNode.publicKey), true); psbt.finalizeAllInputs(); const tx = psbt.extractTransaction(); diff --git a/test/psbt.js b/test/psbt.js index 18a4c99..2b630d7 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -249,14 +249,14 @@ describe(`Psbt`, () => { }) }) - describe('sign', () => { + describe('signAllInputs', () => { fixtures.signInput.checks.forEach(f => { if (f.description === 'checks the input exists') return it(f.description, () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) assert.doesNotThrow(() => { - psbtThatShouldsign.sign( + psbtThatShouldsign.signAllInputs( ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, ) @@ -266,13 +266,13 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) assert.throws(() => { - psbtThatShouldThrow.sign( + psbtThatShouldThrow.signAllInputs( ECPair.fromWIF(f.shouldThrow.WIF), f.shouldThrow.sighashTypes || undefined, ) }, new RegExp('No inputs were signed')) assert.throws(() => { - psbtThatShouldThrow.sign() + psbtThatShouldThrow.signAllInputs() }, new RegExp('Need Signer to sign input')) } }) @@ -374,13 +374,13 @@ describe(`Psbt`, () => { }) }) - describe('signHD', () => { + describe('signAllInputsHD', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) assert.doesNotThrow(() => { - psbtThatShouldsign.signHD( + psbtThatShouldsign.signAllInputsHD( bip32.fromBase58(f.shouldSign.xprv), f.shouldSign.sighashTypes || undefined, ) @@ -390,13 +390,13 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) assert.throws(() => { - psbtThatShouldThrow.signHD( + psbtThatShouldThrow.signAllInputsHD( bip32.fromBase58(f.shouldThrow.xprv), f.shouldThrow.sighashTypes || undefined, ) }, new RegExp('No inputs were signed')) assert.throws(() => { - psbtThatShouldThrow.signHD() + psbtThatShouldThrow.signAllInputsHD() }, new RegExp('Need HDSigner to sign input')) } }) @@ -505,7 +505,7 @@ describe(`Psbt`, () => { }) }) - describe('setSequence', () => { + describe('setInputSequence', () => { it('Sets the sequence number for a given input', () => { const psbt = new Psbt() psbt.addInput({ @@ -515,7 +515,7 @@ describe(`Psbt`, () => { assert.strictEqual(psbt.inputCount, 1) assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0xffffffff) - psbt.setSequence(0, 0) + psbt.setInputSequence(0, 0) assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0) }) @@ -527,7 +527,7 @@ describe(`Psbt`, () => { }); assert.throws(() => { - psbt.setSequence(1, 0) + psbt.setInputSequence(1, 0) }, new RegExp('Input index too high')) }) }) @@ -539,7 +539,7 @@ describe(`Psbt`, () => { const notAClone = Object.assign(new Psbt(), psbt) // references still active const clone = psbt.clone() - assert.strictEqual(psbt.validateAllSignatures(), true) + assert.strictEqual(psbt.validateSignaturesOfAllInputs(), true) assert.strictEqual(clone.toBase64(), psbt.toBase64()) assert.strictEqual(clone.toBase64(), notAClone.toBase64()) @@ -561,22 +561,22 @@ describe(`Psbt`, () => { }) }) - describe('validateSignatures', () => { - const f = fixtures.validateSignatures + describe('validateSignaturesOfInput', () => { + const f = fixtures.validateSignaturesOfInput it('Correctly validates a signature', () => { const psbt = Psbt.fromBase64(f.psbt) - assert.strictEqual(psbt.validateSignatures(f.index), true) + assert.strictEqual(psbt.validateSignaturesOfInput(f.index), true) assert.throws(() => { - psbt.validateSignatures(f.nonExistantIndex) + psbt.validateSignaturesOfInput(f.nonExistantIndex) }, new RegExp('No signatures to validate')) }) it('Correctly validates a signature against a pubkey', () => { const psbt = Psbt.fromBase64(f.psbt) - assert.strictEqual(psbt.validateSignatures(f.index, f.pubkey), true) + assert.strictEqual(psbt.validateSignaturesOfInput(f.index, f.pubkey), true) assert.throws(() => { - psbt.validateSignatures(f.index, f.incorrectPubkey) + psbt.validateSignaturesOfInput(f.index, f.incorrectPubkey) }, new RegExp('No signatures for this pubkey')) }) }) @@ -623,7 +623,7 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.setVersion(3) }, new RegExp('Can not modify transaction, signatures exist.')) - psbt.validateSignatures(0) + psbt.validateSignaturesOfInput(0) psbt.finalizeAllInputs() assert.strictEqual( psbt.extractTransaction().toHex(), diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 0778226..863b331 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -58,7 +58,7 @@ const DEFAULT_OPTS: PsbtOpts = { * data for updateOutput. * For a list of what attributes should be what types. Check the bip174 library. * Also, check the integration tests for some examples of usage. - * Signer: There are a few methods. sign and signAsync, which will search all input + * Signer: There are a few methods. signAllInputs and signAsync, which will search all input * information for your pubkey or pubkeyhash, and only sign inputs where it finds * your info. Or you can explicitly sign a specific input with signInput and * signInputAsync. For the async methods you can create a SignerAsync object @@ -69,7 +69,7 @@ const DEFAULT_OPTS: PsbtOpts = { * all sequences, version, locktime, etc. are the same before combining. * Input Finalizer: This role is fairly important. Not only does it need to construct * the input scriptSigs and witnesses, but it SHOULD verify the signatures etc. - * Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()` + * Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()` * Running any finalize method will delete any data in the input(s) that are no longer * needed due to the finalized scripts containing the information. * Transaction Extractor: This role will perform some checks before returning a @@ -168,9 +168,9 @@ export class Psbt { return this; } - setSequence(inputIndex: number, sequence: number): this { + setInputSequence(inputIndex: number, sequence: number): this { check32Bit(sequence); - checkInputsForPartialSig(this.data.inputs, 'setSequence'); + checkInputsForPartialSig(this.data.inputs, 'setInputSequence'); const c = this.__CACHE; if (c.__TX.ins.length <= inputIndex) { throw new Error('Input index too high'); @@ -291,15 +291,15 @@ export class Psbt { return this; } - validateAllSignatures(): boolean { + validateSignaturesOfAllInputs(): boolean { checkForInput(this.data.inputs, 0); // making sure we have at least one const results = range(this.data.inputs.length).map(idx => - this.validateSignatures(idx), + this.validateSignaturesOfInput(idx), ); return results.reduce((final, res) => res === true && final, true); } - validateSignatures(inputIndex: number, pubkey?: Buffer): boolean { + validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean { const input = this.data.inputs[inputIndex]; const partialSig = (input || {}).partialSig; if (!input || !partialSig || partialSig.length < 1) @@ -332,7 +332,7 @@ export class Psbt { return results.every(res => res === true); } - signHD( + signAllInputsHD( hdKeyPair: HDSigner, sighashTypes: number[] = [Transaction.SIGHASH_ALL], ): this { @@ -433,7 +433,7 @@ export class Psbt { ); } - sign( + signAllInputs( keyPair: Signer, sighashTypes: number[] = [Transaction.SIGHASH_ALL], ): this { @@ -812,7 +812,7 @@ function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void { case Transaction.SIGHASH_SINGLE: case Transaction.SIGHASH_NONE: whitelist.push('addOutput'); - whitelist.push('setSequence'); + whitelist.push('setInputSequence'); break; } if (whitelist.indexOf(action) === -1) { diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 9557f79..a133feb 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -19,7 +19,7 @@ import { Transaction } from './transaction'; * data for updateOutput. * For a list of what attributes should be what types. Check the bip174 library. * Also, check the integration tests for some examples of usage. - * Signer: There are a few methods. sign and signAsync, which will search all input + * Signer: There are a few methods. signAllInputs and signAsync, which will search all input * information for your pubkey or pubkeyhash, and only sign inputs where it finds * your info. Or you can explicitly sign a specific input with signInput and * signInputAsync. For the async methods you can create a SignerAsync object @@ -30,7 +30,7 @@ import { Transaction } from './transaction'; * all sequences, version, locktime, etc. are the same before combining. * Input Finalizer: This role is fairly important. Not only does it need to construct * the input scriptSigs and witnesses, but it SHOULD verify the signatures etc. - * Before running `psbt.finalizeAllInputs()` please run `psbt.validateAllSignatures()` + * Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()` * Running any finalize method will delete any data in the input(s) that are no longer * needed due to the finalized scripts containing the information. * Transaction Extractor: This role will perform some checks before returning a @@ -50,7 +50,7 @@ export declare class Psbt { setMaximumFeeRate(satoshiPerByte: number): void; setVersion(version: number): this; setLocktime(locktime: number): this; - setSequence(inputIndex: number, sequence: number): this; + setInputSequence(inputIndex: number, sequence: number): this; addInputs(inputDatas: TransactionInput[]): this; addInput(inputData: TransactionInput): this; addOutputs(outputDatas: TransactionOutput[]): this; @@ -59,13 +59,13 @@ export declare class Psbt { getFeeRate(): number; finalizeAllInputs(): this; finalizeInput(inputIndex: number): this; - validateAllSignatures(): boolean; - validateSignatures(inputIndex: number, pubkey?: Buffer): boolean; - signHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; + validateSignaturesOfAllInputs(): boolean; + validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean; + signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; signHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>; signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this; signInputHDAsync(inputIndex: number, hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>; - sign(keyPair: Signer, sighashTypes?: number[]): this; + signAllInputs(keyPair: Signer, sighashTypes?: number[]): this; signAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>; signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this; signInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>; From d05806fe699b1538aca0fb5bcab241a2f5a3f918 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 15:10:58 +0900 Subject: [PATCH 424/568] Update README, add deprecation warning --- README.md | 8 ++++++++ src/transaction_builder.js | 7 +++++++ test/integration/cltv.js | 1 + test/integration/csv.js | 1 + test/integration/payments.js | 1 + test/integration/transactions.js | 1 + test/transaction_builder.js | 2 ++ ts_src/transaction_builder.ts | 7 +++++++ 8 files changed, 28 insertions(+) diff --git a/README.md b/README.md index 00b5816..6f46004 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,14 @@ The below examples are implemented as integration tests, they should be very eas Otherwise, pull requests are appreciated. Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). +### Warning: Currently the tests use TransactionBuilder, which will be removed in the future (v6.x.x or higher) +We will move towards replacing all instances of TransactionBuilder in the tests with the new Psbt. + +Currently we have a few examples on how to use the newer Psbt class at the following link: +- [Psbt examples](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions-psbt.js) + +The rest of the examples are below (using TransactionBuilder for Transaction creation) + - [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) - [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) - [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index e63bdd0..a13c481 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -57,6 +57,13 @@ class TransactionBuilder { this.__TX = new transaction_1.Transaction(); this.__TX.version = 2; this.__USE_LOW_R = false; + console.warn( + 'Deprecation Warning: TransactionBuilder will be removed in the future. ' + + '(v6.x.x or later) Please use the Psbt class instead. Examples of usage ' + + 'are available in the transactions-psbt.js integration test file on our ' + + 'Github. A high level explanation is available in the psbt.ts and psbt.js ' + + 'files as well.', + ); } static fromTransaction(transaction, network) { const txb = new TransactionBuilder(network); diff --git a/test/integration/cltv.js b/test/integration/cltv.js index b2fd478..e123652 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -7,6 +7,7 @@ const bip65 = require('bip65') const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) +console.warn = () => {} // Silence the Deprecation Warning describe('bitcoinjs-lib (transactions w/ CLTV)', () => { // force update MTP diff --git a/test/integration/csv.js b/test/integration/csv.js index f213c6c..2e133f0 100644 --- a/test/integration/csv.js +++ b/test/integration/csv.js @@ -9,6 +9,7 @@ const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGk const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) const charles = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf', regtest) const dave = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', regtest) +console.warn = () => {} // Silence the Deprecation Warning describe('bitcoinjs-lib (transactions w/ CSV)', () => { // force update MTP diff --git a/test/integration/payments.js b/test/integration/payments.js index 256bd00..905eda8 100644 --- a/test/integration/payments.js +++ b/test/integration/payments.js @@ -7,6 +7,7 @@ const keyPairs = [ bitcoin.ECPair.makeRandom({ network: NETWORK }), bitcoin.ECPair.makeRandom({ network: NETWORK }) ] +console.warn = () => {} // Silence the Deprecation Warning async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index 464460e..a75b6f2 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -3,6 +3,7 @@ const assert = require('assert') const bitcoin = require('../../') const regtestUtils = require('./_regtest') const regtest = regtestUtils.network +console.warn = () => {} // Silence the Deprecation Warning function rng () { return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64') diff --git a/test/transaction_builder.js b/test/transaction_builder.js index a135cca..6374161 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -9,6 +9,8 @@ const Transaction = require('..').Transaction const TransactionBuilder = require('..').TransactionBuilder const NETWORKS = require('../src/networks') +console.warn = () => {} // Silence the Deprecation Warning + const fixtures = require('./fixtures/transaction_builder') function constructSign (f, txb, useOldSignArgs) { diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts index c486285..1edf0f2 100644 --- a/ts_src/transaction_builder.ts +++ b/ts_src/transaction_builder.ts @@ -146,6 +146,13 @@ export class TransactionBuilder { this.__TX = new Transaction(); this.__TX.version = 2; this.__USE_LOW_R = false; + console.warn( + 'Deprecation Warning: TransactionBuilder will be removed in the future. ' + + '(v6.x.x or later) Please use the Psbt class instead. Examples of usage ' + + 'are available in the transactions-psbt.js integration test file on our ' + + 'Github. A high level explanation is available in the psbt.ts and psbt.js ' + + 'files as well.', + ); } setLowR(setting?: boolean): boolean { From 6e447b1f1b4f68fd38f7b23170f5e4ef3ee053ae Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 15:51:38 +0900 Subject: [PATCH 425/568] Refactor: Create cache in constructor --- src/psbt.js | 12 ++++-------- ts_src/psbt.ts | 20 ++++++++------------ 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 31ac164..e975faa 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -62,16 +62,14 @@ const DEFAULT_OPTS = { class Psbt { constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) { this.data = data; + // set defaults + this.opts = Object.assign({}, DEFAULT_OPTS, opts); this.__CACHE = { __NON_WITNESS_UTXO_TX_CACHE: [], __NON_WITNESS_UTXO_BUF_CACHE: [], __TX_IN_CACHE: {}, - __TX: new transaction_1.Transaction(), + __TX: this.data.globalMap.unsignedTx.tx, }; - // set defaults - this.opts = Object.assign({}, DEFAULT_OPTS, opts); - const c = this.__CACHE; - c.__TX = this.data.globalMap.unsignedTx.tx; if (this.data.inputs.length === 0) this.setVersion(2); // Make data hidden when enumerating const dpew = (obj, attr, enumerable, writable) => @@ -92,10 +90,8 @@ class Psbt { } static fromBuffer(buffer, opts = {}) { const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer); - const tx = psbtBase.globalMap.unsignedTx.tx; const psbt = new Psbt(opts, psbtBase); - psbt.__CACHE.__TX = tx; - checkTxForDupeIns(tx, psbt.__CACHE); + checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE); return psbt; } get inputCount() { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 863b331..6efbc9d 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -88,20 +88,12 @@ export class Psbt { static fromBuffer(buffer: Buffer, opts: PsbtOptsOptional = {}): Psbt { const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer); - const tx: Transaction = (psbtBase.globalMap.unsignedTx as PsbtTransaction) - .tx; const psbt = new Psbt(opts, psbtBase); - psbt.__CACHE.__TX = tx; - checkTxForDupeIns(tx, psbt.__CACHE); + checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE); return psbt; } - private __CACHE: PsbtCache = { - __NON_WITNESS_UTXO_TX_CACHE: [], - __NON_WITNESS_UTXO_BUF_CACHE: [], - __TX_IN_CACHE: {}, - __TX: new Transaction(), - }; + private __CACHE: PsbtCache; private opts: PsbtOpts; constructor( @@ -110,8 +102,12 @@ export class Psbt { ) { // set defaults this.opts = Object.assign({}, DEFAULT_OPTS, opts); - const c = this.__CACHE; - c.__TX = (this.data.globalMap.unsignedTx as PsbtTransaction).tx; + this.__CACHE = { + __NON_WITNESS_UTXO_TX_CACHE: [], + __NON_WITNESS_UTXO_BUF_CACHE: [], + __TX_IN_CACHE: {}, + __TX: (this.data.globalMap.unsignedTx as PsbtTransaction).tx, + }; if (this.data.inputs.length === 0) this.setVersion(2); // Make data hidden when enumerating From ecba58bf0f2094e3c65eb8b56c4013bc0735d117 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 16:16:52 +0900 Subject: [PATCH 426/568] Update CHANGELOG --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a323b8..172d90c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 5.1.0 +__added__ +- A new `Psbt` class for creating, distributing, combining, signing, and compiling Transactions (#1425) +- A `name` attribute to the Payment interface. P2SH and P2WSH are nested with `'-'` as separator, and p2ms is in the format of `'p2ms(m of n)''` all others are just hard coded. (#1433) + +__changed__ +- `TransactionBuilder`: Migrate to stricter type checks during sign by switching to a single object parameter (#1416) +- `tests`: Use regtest-client as separate library (#1421) + +# 5.0.5 +__added__ +- Added `ECPairInterface` `Stack` and `StackElement` interfaces to the main index.ts export (TypeScript only affected) + # 5.0.4 __added__ - low R value support for ECPair, bip32, and TransactionBuilder (default off) via `txb.setLowR()` (#1385) From de20dce4553aff3533cd78533cb53fe9fa36d9a3 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 16:16:58 +0900 Subject: [PATCH 427/568] 5.1.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0dc5a0d..f699a72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.5", + "version": "5.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e8b436a..97fac83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.0.5", + "version": "5.1.0", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From a3bfee75b08af8c02b7aeafd4459b0666fe747ee Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 17:21:31 +0900 Subject: [PATCH 428/568] Fix method names for PSBT --- CHANGELOG.md | 4 ++++ src/psbt.js | 9 ++++++--- test/integration/transactions-psbt.js | 2 +- test/psbt.js | 16 ++++++++-------- ts_src/psbt.ts | 6 +++--- types/psbt.d.ts | 6 +++--- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 172d90c..d1191a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 5.1.1 +__changed__ +- Name inconsistencies for Psbt class. (Quick fix) + # 5.1.0 __added__ - A new `Psbt` class for creating, distributing, combining, signing, and compiling Transactions (#1425) diff --git a/src/psbt.js b/src/psbt.js index e975faa..8fbb2bb 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -42,7 +42,7 @@ const DEFAULT_OPTS = { * data for updateOutput. * For a list of what attributes should be what types. Check the bip174 library. * Also, check the integration tests for some examples of usage. - * Signer: There are a few methods. signAllInputs and signAsync, which will search all input + * Signer: There are a few methods. signAllInputs and signAllInputsAsync, which will search all input * information for your pubkey or pubkeyhash, and only sign inputs where it finds * your info. Or you can explicitly sign a specific input with signInput and * signInputAsync. For the async methods you can create a SignerAsync object @@ -295,7 +295,7 @@ class Psbt { } return this; } - signHDAsync( + signAllInputsHDAsync( hdKeyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], ) { @@ -380,7 +380,10 @@ class Psbt { } return this; } - signAsync(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) { + signAllInputsAsync( + keyPair, + sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], + ) { return new Promise((resolve, reject) => { if (!keyPair || !keyPair.publicKey) return reject(new Error('Need Signer to sign input')); diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index 07035ab..7b9d9c5 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -132,7 +132,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { signer2.signAllInputs(alice2.keys[0]); // If your signer object's sign method returns a promise, use the following - // await signer2.signAsync(alice2.keys[0]) + // await signer2.signAllInputsAsync(alice2.keys[0]) // encode to send back to combiner (signer 1 and 2 are not near each other) const s1text = signer1.toBase64(); diff --git a/test/psbt.js b/test/psbt.js index 2b630d7..b670a99 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -219,14 +219,14 @@ describe(`Psbt`, () => { }) }) - describe('signAsync', () => { + describe('signAllInputsAsync', () => { fixtures.signInput.checks.forEach(f => { if (f.description === 'checks the input exists') return it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) assert.doesNotReject(async () => { - await psbtThatShouldsign.signAsync( + await psbtThatShouldsign.signAllInputsAsync( ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, ) @@ -236,13 +236,13 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) assert.rejects(async () => { - await psbtThatShouldThrow.signAsync( + await psbtThatShouldThrow.signAllInputsAsync( ECPair.fromWIF(f.shouldThrow.WIF), f.shouldThrow.sighashTypes || undefined, ) }, new RegExp('No inputs were signed')) assert.rejects(async () => { - await psbtThatShouldThrow.signAsync() + await psbtThatShouldThrow.signAllInputsAsync() }, new RegExp('Need Signer to sign input')) } }) @@ -345,13 +345,13 @@ describe(`Psbt`, () => { }) }) - describe('signHDAsync', () => { + describe('signAllInputsHDAsync', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) assert.doesNotReject(async () => { - await psbtThatShouldsign.signHDAsync( + await psbtThatShouldsign.signAllInputsHDAsync( bip32.fromBase58(f.shouldSign.xprv), f.shouldSign.sighashTypes || undefined, ) @@ -361,13 +361,13 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) assert.rejects(async () => { - await psbtThatShouldThrow.signHDAsync( + await psbtThatShouldThrow.signAllInputsHDAsync( bip32.fromBase58(f.shouldThrow.xprv), f.shouldThrow.sighashTypes || undefined, ) }, new RegExp('No inputs were signed')) assert.rejects(async () => { - await psbtThatShouldThrow.signHDAsync() + await psbtThatShouldThrow.signAllInputsHDAsync() }, new RegExp('Need HDSigner to sign input')) } }) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 6efbc9d..83e9dee 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -58,7 +58,7 @@ const DEFAULT_OPTS: PsbtOpts = { * data for updateOutput. * For a list of what attributes should be what types. Check the bip174 library. * Also, check the integration tests for some examples of usage. - * Signer: There are a few methods. signAllInputs and signAsync, which will search all input + * Signer: There are a few methods. signAllInputs and signAllInputsAsync, which will search all input * information for your pubkey or pubkeyhash, and only sign inputs where it finds * your info. Or you can explicitly sign a specific input with signInput and * signInputAsync. For the async methods you can create a SignerAsync object @@ -351,7 +351,7 @@ export class Psbt { return this; } - signHDAsync( + signAllInputsHDAsync( hdKeyPair: HDSigner | HDSignerAsync, sighashTypes: number[] = [Transaction.SIGHASH_ALL], ): Promise<void> { @@ -454,7 +454,7 @@ export class Psbt { return this; } - signAsync( + signAllInputsAsync( keyPair: Signer | SignerAsync, sighashTypes: number[] = [Transaction.SIGHASH_ALL], ): Promise<void> { diff --git a/types/psbt.d.ts b/types/psbt.d.ts index a133feb..04a3cfd 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -19,7 +19,7 @@ import { Transaction } from './transaction'; * data for updateOutput. * For a list of what attributes should be what types. Check the bip174 library. * Also, check the integration tests for some examples of usage. - * Signer: There are a few methods. signAllInputs and signAsync, which will search all input + * Signer: There are a few methods. signAllInputs and signAllInputsAsync, which will search all input * information for your pubkey or pubkeyhash, and only sign inputs where it finds * your info. Or you can explicitly sign a specific input with signInput and * signInputAsync. For the async methods you can create a SignerAsync object @@ -62,11 +62,11 @@ export declare class Psbt { validateSignaturesOfAllInputs(): boolean; validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean; signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; - signHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>; + signAllInputsHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>; signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this; signInputHDAsync(inputIndex: number, hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>; signAllInputs(keyPair: Signer, sighashTypes?: number[]): this; - signAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>; + signAllInputsAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>; signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this; signInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>; toBuffer(): Buffer; From 3b44133f8fc3d042e2d1aa28396e76bba23cf060 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Jul 2019 17:21:49 +0900 Subject: [PATCH 429/568] 5.1.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f699a72..2084591 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.0", + "version": "5.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 97fac83..63b33bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.0", + "version": "5.1.1", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From e4844828deca4ea2fc7ce815a32d6dd4f66359fd Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 23 Jul 2019 14:59:54 +0900 Subject: [PATCH 430/568] Check signatures for finalized inputs too --- src/psbt.js | 23 +++++++++++++++++++++-- test/psbt.js | 3 +++ ts_src/psbt.ts | 23 +++++++++++++++++++++-- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 8fbb2bb..0898bc6 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -626,8 +626,27 @@ function checkFees(psbt, cache, opts) { function checkInputsForPartialSig(inputs, action) { inputs.forEach(input => { let throws = false; - if ((input.partialSig || []).length === 0) return; - input.partialSig.forEach(pSig => { + let pSigs = []; + if ((input.partialSig || []).length === 0) { + if (!input.finalScriptSig && !input.finalScriptWitness) return; + const scriptItems = !input.finalScriptSig + ? [] + : bscript.decompile(input.finalScriptSig) || []; + const witnessItems = !input.finalScriptWitness + ? [] + : bscript.decompile(input.finalScriptWitness) || []; + pSigs = scriptItems + .concat(witnessItems) + .filter(item => { + return ( + Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item) + ); + }) + .map(sig => ({ signature: sig })); + } else { + pSigs = input.partialSig; + } + pSigs.forEach(pSig => { const { hashType } = bscript.signature.decode(pSig.signature); const whitelist = []; const isAnyoneCanPay = diff --git a/test/psbt.js b/test/psbt.js index b670a99..467e426 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -625,6 +625,9 @@ describe(`Psbt`, () => { }, new RegExp('Can not modify transaction, signatures exist.')) psbt.validateSignaturesOfInput(0) psbt.finalizeAllInputs() + assert.throws(() => { + psbt.setVersion(3) + }, new RegExp('Can not modify transaction, signatures exist.')) assert.strictEqual( psbt.extractTransaction().toHex(), '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 83e9dee..707ca21 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -795,8 +795,27 @@ function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void { function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void { inputs.forEach(input => { let throws = false; - if ((input.partialSig || []).length === 0) return; - input.partialSig!.forEach(pSig => { + let pSigs: PartialSig[] = []; + if ((input.partialSig || []).length === 0) { + if (!input.finalScriptSig && !input.finalScriptWitness) return; + const scriptItems = !input.finalScriptSig + ? [] + : bscript.decompile(input.finalScriptSig) || []; + const witnessItems = !input.finalScriptWitness + ? [] + : bscript.decompile(input.finalScriptWitness) || []; + pSigs = scriptItems + .concat(witnessItems) + .filter(item => { + return ( + Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item) + ); + }) + .map(sig => ({ signature: sig })) as PartialSig[]; + } else { + pSigs = input.partialSig!; + } + pSigs.forEach(pSig => { const { hashType } = bscript.signature.decode(pSig.signature); const whitelist: string[] = []; const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY; From e36450cbfb72671343540a22561f4a05d148a041 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Tue, 23 Jul 2019 15:44:37 +0900 Subject: [PATCH 431/568] build: npm audit fix --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2084591..88785e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -985,9 +985,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, "lodash.flattendeep": { From 06b38a7d834cf2b07e81bd3375f5f29028f0bf45 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 23 Jul 2019 15:51:12 +0900 Subject: [PATCH 432/568] Refactor: add function getPsigsFromInputFinalScripts --- src/psbt.js | 29 +++++++++++++++-------------- ts_src/psbt.ts | 30 ++++++++++++++++-------------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 0898bc6..051de6e 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -629,20 +629,7 @@ function checkInputsForPartialSig(inputs, action) { let pSigs = []; if ((input.partialSig || []).length === 0) { if (!input.finalScriptSig && !input.finalScriptWitness) return; - const scriptItems = !input.finalScriptSig - ? [] - : bscript.decompile(input.finalScriptSig) || []; - const witnessItems = !input.finalScriptWitness - ? [] - : bscript.decompile(input.finalScriptWitness) || []; - pSigs = scriptItems - .concat(witnessItems) - .filter(item => { - return ( - Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item) - ); - }) - .map(sig => ({ signature: sig })); + pSigs = getPsigsFromInputFinalScripts(input); } else { pSigs = input.partialSig; } @@ -918,6 +905,20 @@ function getPayment(script, scriptType, partialSig) { } return payment; } +function getPsigsFromInputFinalScripts(input) { + const scriptItems = !input.finalScriptSig + ? [] + : bscript.decompile(input.finalScriptSig) || []; + const witnessItems = !input.finalScriptWitness + ? [] + : bscript.decompile(input.finalScriptWitness) || []; + return scriptItems + .concat(witnessItems) + .filter(item => { + return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item); + }) + .map(sig => ({ signature: sig })); +} function getScriptFromInput(inputIndex, input, cache) { const unsignedTx = cache.__TX; const res = { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 707ca21..eb09155 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -798,20 +798,7 @@ function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void { let pSigs: PartialSig[] = []; if ((input.partialSig || []).length === 0) { if (!input.finalScriptSig && !input.finalScriptWitness) return; - const scriptItems = !input.finalScriptSig - ? [] - : bscript.decompile(input.finalScriptSig) || []; - const witnessItems = !input.finalScriptWitness - ? [] - : bscript.decompile(input.finalScriptWitness) || []; - pSigs = scriptItems - .concat(witnessItems) - .filter(item => { - return ( - Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item) - ); - }) - .map(sig => ({ signature: sig })) as PartialSig[]; + pSigs = getPsigsFromInputFinalScripts(input); } else { pSigs = input.partialSig!; } @@ -1140,6 +1127,21 @@ function getPayment( return payment!; } +function getPsigsFromInputFinalScripts(input: PsbtInput): PartialSig[] { + const scriptItems = !input.finalScriptSig + ? [] + : bscript.decompile(input.finalScriptSig) || []; + const witnessItems = !input.finalScriptWitness + ? [] + : bscript.decompile(input.finalScriptWitness) || []; + return scriptItems + .concat(witnessItems) + .filter(item => { + return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item); + }) + .map(sig => ({ signature: sig })) as PartialSig[]; +} + interface GetScriptReturn { script: Buffer | null; isSegwit: boolean; From a3b6cdabb993ba5a1e20ed22f95bf22f057414b9 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 25 Jul 2019 17:13:36 +0900 Subject: [PATCH 433/568] Update regtest-client --- package-lock.json | 10 ++++++---- package.json | 2 +- test/integration/_regtest.js | 3 +-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88785e4..f092200 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1499,12 +1499,14 @@ } }, "regtest-client": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/regtest-client/-/regtest-client-0.1.0.tgz", - "integrity": "sha512-qTy+VvEKx8NRxSCr1jr+l9d+DeF06lxxWU9kmS8+kRVtgWHBTZYgQwRN6KkVqBGYP1Vls6dlG9X874WWTEurSQ==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regtest-client/-/regtest-client-0.2.0.tgz", + "integrity": "sha512-eIcC8Kle/wjS47pRlw7nJpstrJDWp0bkvVPl2KJpJcK3JDNW0fMxJgE/CGpMEUSjhhFXW1rtJMN6kyKw5NIzqg==", "dev": true, "requires": { - "dhttp": "^3.0.3" + "bs58check": "^2.1.2", + "dhttp": "^3.0.3", + "randombytes": "^2.1.0" } }, "release-zalgo": { diff --git a/package.json b/package.json index 63b33bf..dca3bd7 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "nyc": "^14.1.1", "prettier": "1.16.4", "proxyquire": "^2.0.1", - "regtest-client": "0.1.0", + "regtest-client": "0.2.0", "rimraf": "^2.6.3", "tslint": "^5.16.0", "typescript": "3.2.2" diff --git a/test/integration/_regtest.js b/test/integration/_regtest.js index 02913a8..c50ef2f 100644 --- a/test/integration/_regtest.js +++ b/test/integration/_regtest.js @@ -1,9 +1,8 @@ const { RegtestUtils } = require('regtest-client') -const bitcoin = require('../..') const APIPASS = process.env.APIPASS || 'satoshi' const APIURL = process.env.APIURL || 'https://regtest.bitbank.cc/1' -const regtestUtils = new RegtestUtils(bitcoin, { APIPASS, APIURL }) +const regtestUtils = new RegtestUtils({ APIPASS, APIURL }) module.exports = regtestUtils; From 4e8aecb5e045a6b6fcd2527bbc2ccbb025158145 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 25 Jul 2019 17:36:42 +0900 Subject: [PATCH 434/568] Use output for faucetComplex --- test/integration/transactions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index a75b6f2..173fc4a 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -193,7 +193,7 @@ describe('bitcoinjs-lib (transactions)', () => { const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) - const unspent = await regtestUtils.faucetComplex(p2wpkh.address, 5e4) + const unspent = await regtestUtils.faucetComplex(p2wpkh.output, 5e4) // XXX: build the Transaction w/ a P2WPKH input const txb = new bitcoin.TransactionBuilder(regtest) @@ -223,7 +223,7 @@ describe('bitcoinjs-lib (transactions)', () => { const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest }) const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }) - const unspent = await regtestUtils.faucetComplex(p2wsh.address, 5e4) + const unspent = await regtestUtils.faucetComplex(p2wsh.output, 5e4) // XXX: build the Transaction w/ a P2WSH input const txb = new bitcoin.TransactionBuilder(regtest) From 1605f99e17204f174bd18a71333e0fac3ddde687 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 25 Jul 2019 18:15:11 +0900 Subject: [PATCH 435/568] Move lowR to public writable attribute --- src/ecpair.js | 4 +++- ts_src/ecpair.ts | 6 +++++- types/ecpair.d.ts | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index 809987e..91fe3a9 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -16,6 +16,7 @@ class ECPair { constructor(__D, __Q, options) { this.__D = __D; this.__Q = __Q; + this.lowR = false; if (options === undefined) options = {}; this.compressed = options.compressed === undefined ? true : options.compressed; @@ -33,8 +34,9 @@ class ECPair { if (!this.__D) throw new Error('Missing private key'); return wif.encode(this.network.wif, this.__D, this.compressed); } - sign(hash, lowR = false) { + sign(hash, lowR) { if (!this.__D) throw new Error('Missing private key'); + if (lowR === undefined) lowR = this.lowR; if (lowR === false) { return ecc.sign(hash, this.__D); } else { diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index c951f76..9052866 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -36,6 +36,7 @@ export interface SignerAsync { export interface ECPairInterface extends Signer { compressed: boolean; network: Network; + lowR: boolean; privateKey?: Buffer; toWIF(): string; verify(hash: Buffer, signature: Buffer): boolean; @@ -44,12 +45,14 @@ export interface ECPairInterface extends Signer { class ECPair implements ECPairInterface { compressed: boolean; network: Network; + lowR: boolean; constructor( private __D?: Buffer, private __Q?: Buffer, options?: ECPairOptions, ) { + this.lowR = false; if (options === undefined) options = {}; this.compressed = options.compressed === undefined ? true : options.compressed; @@ -73,8 +76,9 @@ class ECPair implements ECPairInterface { return wif.encode(this.network.wif, this.__D, this.compressed); } - sign(hash: Buffer, lowR: boolean = false): Buffer { + sign(hash: Buffer, lowR?: boolean): Buffer { if (!this.__D) throw new Error('Missing private key'); + if (lowR === undefined) lowR = this.lowR; if (lowR === false) { return ecc.sign(hash, this.__D); } else { diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index 8b7d193..07d71d4 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -20,6 +20,7 @@ export interface SignerAsync { export interface ECPairInterface extends Signer { compressed: boolean; network: Network; + lowR: boolean; privateKey?: Buffer; toWIF(): string; verify(hash: Buffer, signature: Buffer): boolean; @@ -29,6 +30,7 @@ declare class ECPair implements ECPairInterface { private __Q?; compressed: boolean; network: Network; + lowR: boolean; constructor(__D?: Buffer | undefined, __Q?: Buffer | undefined, options?: ECPairOptions); readonly privateKey: Buffer | undefined; readonly publicKey: Buffer; From d48864e22a9511db36c0f8880a304b690a796795 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 25 Jul 2019 22:43:06 +0900 Subject: [PATCH 436/568] Bump bip32 --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88785e4..67e1135 100644 --- a/package-lock.json +++ b/package-lock.json @@ -205,9 +205,9 @@ "integrity": "sha512-AaoWrkYtv6A2y8H+qzs6NvRWypzNbADT8PQGpM9rnP+jLzeol+uzhe3Myeuq/dwrHYtmsW8V71HmX2oXhQGagw==" }, "bip32": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.3.tgz", - "integrity": "sha512-Tg4dHUXiYBkJyCQq4g++C2PqKcZRveVqy7cKxyl88Uai7MmmknFGaF88odYrXcXk5EMyrlXLuAMC3yEiLxRnNA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.4.tgz", + "integrity": "sha512-ioPytarPDIrWckWMuK4RNUtvwhvWEc2fvuhnO0WEwu732k5OLjUXv4rXi2c/KJHw9ZMNQMkYRJrBw81RujShGQ==", "requires": { "@types/node": "10.12.18", "bs58check": "^2.1.1", diff --git a/package.json b/package.json index 63b33bf..76f7953 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@types/node": "10.12.18", "bech32": "^1.1.2", "bip174": "^1.0.0", - "bip32": "^2.0.3", + "bip32": "^2.0.4", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", "bs58check": "^2.0.0", From 4a29cb49dcbc3f38b8709a62d2f1a465140d84c2 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 25 Jul 2019 22:43:16 +0900 Subject: [PATCH 437/568] 5.1.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67e1135..b57c244 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.1", + "version": "5.1.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 76f7953..f72d7e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.1", + "version": "5.1.2", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 55804758e945c2102457054799ec6499b7dff747 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 26 Jul 2019 10:34:27 +0900 Subject: [PATCH 438/568] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1191a8..cbb2dea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 5.1.2 +__added__ +- `ECPair` and `bip32` objects now have a lowR boolean attribute defaulted to false. You may set it to true to ensure that the sign method uses low R values (#1442) (This is to enable low R usage in Psbt, since we decided not to give the low R flag to the Psbt class, since it makes more sense to be an attribute of the Signer interface) + # 5.1.1 __changed__ - Name inconsistencies for Psbt class. (Quick fix) From dfda8c5ef3ccacc55ce3398399568da0466b7689 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 6 Aug 2019 18:45:02 +0900 Subject: [PATCH 439/568] Fix Signer interface --- ts_src/ecpair.ts | 4 ++-- types/ecpair.d.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts index 9052866..3c7eb1c 100644 --- a/ts_src/ecpair.ts +++ b/ts_src/ecpair.ts @@ -21,14 +21,14 @@ interface ECPairOptions { export interface Signer { publicKey: Buffer; - network?: Network; + network?: any; sign(hash: Buffer, lowR?: boolean): Buffer; getPublicKey?(): Buffer; } export interface SignerAsync { publicKey: Buffer; - network?: Network; + network?: any; sign(hash: Buffer, lowR?: boolean): Promise<Buffer>; getPublicKey?(): Buffer; } diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index 07d71d4..0b69dfe 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -7,13 +7,13 @@ interface ECPairOptions { } export interface Signer { publicKey: Buffer; - network?: Network; + network?: any; sign(hash: Buffer, lowR?: boolean): Buffer; getPublicKey?(): Buffer; } export interface SignerAsync { publicKey: Buffer; - network?: Network; + network?: any; sign(hash: Buffer, lowR?: boolean): Promise<Buffer>; getPublicKey?(): Buffer; } From 139567d6c5c3afaba9d64eddc2edaa6cc0cf5060 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 7 Aug 2019 17:37:52 +0900 Subject: [PATCH 440/568] Fix addInput and addOuput for Psbt --- ts_src/psbt.ts | 13 +++++++++---- types/psbt.d.ts | 14 +++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index eb09155..a83ced3 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -6,6 +6,7 @@ import { PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, + PsbtOutput, PsbtOutputUpdate, Transaction as ITransaction, TransactionFromBuffer, @@ -176,12 +177,12 @@ export class Psbt { return this; } - addInputs(inputDatas: TransactionInput[]): this { + addInputs(inputDatas: PsbtInputExtended[]): this { inputDatas.forEach(inputData => this.addInput(inputData)); return this; } - addInput(inputData: TransactionInput): this { + addInput(inputData: PsbtInputExtended): this { checkInputsForPartialSig(this.data.inputs, 'addInput'); const c = this.__CACHE; this.data.addInput(inputData); @@ -198,12 +199,12 @@ export class Psbt { return this; } - addOutputs(outputDatas: TransactionOutput[]): this { + addOutputs(outputDatas: PsbtOutputExtended[]): this { outputDatas.forEach(outputData => this.addOutput(outputData)); return this; } - addOutput(outputData: TransactionOutput): this { + addOutput(outputData: PsbtOutputExtended): this { checkInputsForPartialSig(this.data.inputs, 'addOutput'); const { address } = outputData as any; if (typeof address === 'string') { @@ -622,6 +623,10 @@ interface PsbtOpts { maximumFeeRate: number; } +interface PsbtInputExtended extends PsbtInput, TransactionInput {} + +interface PsbtOutputExtended extends PsbtOutput, TransactionOutput {} + interface HDSignerBase { /** * DER format compressed publicKey buffer diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 04a3cfd..6a56636 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,6 +1,6 @@ /// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; -import { KeyValue, PsbtGlobalUpdate, PsbtInputUpdate, PsbtOutputUpdate, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; +import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; @@ -51,10 +51,10 @@ export declare class Psbt { setVersion(version: number): this; setLocktime(locktime: number): this; setInputSequence(inputIndex: number, sequence: number): this; - addInputs(inputDatas: TransactionInput[]): this; - addInput(inputData: TransactionInput): this; - addOutputs(outputDatas: TransactionOutput[]): this; - addOutput(outputData: TransactionOutput): this; + addInputs(inputDatas: PsbtInputExtended[]): this; + addInput(inputData: PsbtInputExtended): this; + addOutputs(outputDatas: PsbtOutputExtended[]): this; + addOutput(outputData: PsbtOutputExtended): this; extractTransaction(disableFeeCheck?: boolean): Transaction; getFeeRate(): number; finalizeAllInputs(): this; @@ -84,6 +84,10 @@ interface PsbtOptsOptional { network?: Network; maximumFeeRate?: number; } +interface PsbtInputExtended extends PsbtInput, TransactionInput { +} +interface PsbtOutputExtended extends PsbtOutput, TransactionOutput { +} interface HDSignerBase { /** * DER format compressed publicKey buffer From 7c33e8056cbe854519e84ced4067072766c4b372 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 7 Aug 2019 18:11:13 +0900 Subject: [PATCH 441/568] Update CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbb2dea..ebfac91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 5.1.3 +__changed__ +- TypeScript types: Made Signer and SignerAsync use any for network since we only check for equivalence. (#1448) +- TypeScript types: Made the args for addInput and addOutput for Psbt actually accept updateInput and updateOutput parameters. (#1449) + # 5.1.2 __added__ - `ECPair` and `bip32` objects now have a lowR boolean attribute defaulted to false. You may set it to true to ensure that the sign method uses low R values (#1442) (This is to enable low R usage in Psbt, since we decided not to give the low R flag to the Psbt class, since it makes more sense to be an attribute of the Signer interface) From 71b7faa52c8fb6e3b4f9bdf5eae4464c0104b5c0 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 7 Aug 2019 18:11:19 +0900 Subject: [PATCH 442/568] 5.1.3 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9aac261..d528ba3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.2", + "version": "5.1.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index daa2813..be2321f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.2", + "version": "5.1.3", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From bf45f3638bce45a2eb2d1dd7f9104745f609c88d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 23 Aug 2019 12:52:04 +0900 Subject: [PATCH 443/568] WIP: Allow nonWitnessUtxo with segwit --- src/psbt.js | 57 +++++---- test/fixtures/psbt.json | 2 +- test/integration/transactions-psbt.js | 168 ++++++++++++++++++++++++++ ts_src/psbt.ts | 60 +++++---- 4 files changed, 236 insertions(+), 51 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 051de6e..e134774 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -815,13 +815,29 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) { } else { script = prevout.script; } - if (isP2WPKH(script) || isP2WSHScript(script)) { - throw new Error( - `Input #${inputIndex} has nonWitnessUtxo but segwit script: ` + - `${script.toString('hex')}`, + if (isP2WSHScript(script)) { + if (!input.witnessScript) + throw new Error('Segwit input needs witnessScript if not P2WPKH'); + checkWitnessScript(inputIndex, script, input.witnessScript); + hash = unsignedTx.hashForWitnessV0( + inputIndex, + input.witnessScript, + prevout.value, + sighashType, ); + script = input.witnessScript; + } else if (isP2WPKH(script)) { + // P2WPKH uses the P2PKH template for prevoutScript when signing + const signingScript = payments.p2pkh({ hash: script.slice(2) }).output; + hash = unsignedTx.hashForWitnessV0( + inputIndex, + signingScript, + prevout.value, + sighashType, + ); + } else { + hash = unsignedTx.hashForSignature(inputIndex, script, sighashType); } - hash = unsignedTx.hashForSignature(inputIndex, script, sighashType); } else if (input.witnessUtxo) { let _script; // so we don't shadow the `let script` above if (input.redeemScript) { @@ -927,11 +943,14 @@ function getScriptFromInput(inputIndex, input, cache) { isP2SH: false, isP2WSH: false, }; - if (input.nonWitnessUtxo) { - if (input.redeemScript) { - res.isP2SH = true; - res.script = input.redeemScript; - } else { + res.isP2SH = !!input.redeemScript; + res.isP2WSH = !!input.witnessScript; + if (input.witnessScript) { + res.script = input.witnessScript; + } else if (input.redeemScript) { + res.script = input.redeemScript; + } else { + if (input.nonWitnessUtxo) { const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( cache, input, @@ -939,22 +958,12 @@ function getScriptFromInput(inputIndex, input, cache) { ); const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; + } else if (input.witnessUtxo) { + res.script = input.witnessUtxo.script; } - } else if (input.witnessUtxo) { + } + if (input.witnessScript || isP2WPKH(res.script)) { res.isSegwit = true; - res.isP2SH = !!input.redeemScript; - res.isP2WSH = !!input.witnessScript; - if (input.witnessScript) { - res.script = input.witnessScript; - } else if (input.redeemScript) { - res.script = payments.p2wpkh({ - hash: input.redeemScript.slice(2), - }).output; - } else { - res.script = payments.p2wpkh({ - hash: input.witnessUtxo.script.slice(2), - }).output; - } } return res; } diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 22655da..95c980e 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -513,7 +513,7 @@ { "type": "P2SH-P2WPKH", "psbt": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAAiAgKj88rhJwk3Zxm0p0Rp+xC/6cxmj+I741DHPWPWN7iA+0cwRAIgTRhd9WUpoHYl9tUVmoJ336fJAJInIjdYsoatvRiW8hgCIGOYMlpKRHiHA428Sfa2CdAIIGGQCGhuIgIzj2FN6USnAQEEFgAU4sZupXPxqhcsOB1ghJxBvH4XcesAAA==", - "result": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAABBxcWABTixm6lc/GqFyw4HWCEnEG8fhdx6wAA" + "result": "cHNidP8BAFUCAAAAATIK6DMTn8bbrG7ZdiiVU3/YAgzyk3dwa56En58YfXbDAAAAAAD/////AYA4AQAAAAAAGXapFNU4SHWUW9ZNz+BcxCiuU/7UtJoMiKwAAAAAAAEAvgIAAAABly2BCiYZ3slqurlLwE7b8UXINYKfrJ9sQlBovzBAwFsBAAAAa0gwRQIhAJJ+Hyniw+KneWomeQYrP1duH7cfQ3j8GN6/RshZCfuvAiBux7Uu/5QqmSmL+LjoWZY2b9TWdluY6zLTkQWIornmYwEhAvUo9Sy7Pu44z84ZZPrQMQxBPpDJyy9WlLQMGdGIuUy7/////wGQXwEAAAAAABepFPwojcWCH2oE9dUjMmLC3bdK/xeWhwAAAAABBxcWABTixm6lc/GqFyw4HWCEnEG8fhdx6wEIawJHMEQCIE0YXfVlKaB2JfbVFZqCd9+nyQCSJyI3WLKGrb0YlvIYAiBjmDJaSkR4hwONvEn2tgnQCCBhkAhobiICM49hTelEpwEhAqPzyuEnCTdnGbSnRGn7EL/pzGaP4jvjUMc9Y9Y3uID7AAA=" }, { "type": "P2WPKH", diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index 7b9d9c5..9d40ed0 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -282,6 +282,50 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); }); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input with nonWitnessUtxo', async () => { + const p2sh = createPayment('p2sh-p2wpkh'); + const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh'); + const inputData2 = await getInputData(5e4, p2sh.payment, false, 'p2sh'); + { + const { + hash, + index, + nonWitnessUtxo, + redeemScript, + } = inputData; + assert.deepStrictEqual( + { hash, index, nonWitnessUtxo, redeemScript }, + inputData, + ); + } + const keyPair = p2sh.keys[0]; + const outputData = { + script: p2sh.payment.output, // sending to myself for fun + value: 2e4, + }; + const outputData2 = { + script: p2sh.payment.output, // sending to myself for fun + value: 7e4, + }; + + const tx = new bitcoin.Psbt() + .addInputs([inputData, inputData2]) + .addOutputs([outputData, outputData2]) + .signAllInputs(keyPair) + .finalizeAllInputs() + .extractTransaction(); + + // build and broadcast to the Bitcoin RegTest network + await regtestUtils.broadcast(tx.toHex()); + + await regtestUtils.verify({ + txId: tx.getId(), + address: p2sh.payment.address, + vout: 0, + value: 2e4, + }); + }); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { // the only thing that changes is you don't give a redeemscript for input data @@ -316,6 +360,40 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); }); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input with nonWitnessUtxo', async () => { + // the only thing that changes is you don't give a redeemscript for input data + + const p2wpkh = createPayment('p2wpkh'); + const inputData = await getInputData(5e4, p2wpkh.payment, false, 'noredeem'); + { + const { hash, index, nonWitnessUtxo } = inputData; + assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData); + } + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2wpkh.keys[0]); + + assert.strictEqual(psbt.validateSignaturesOfInput(0), 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, + }); + }); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => { const p2wsh = createPayment('p2wsh-p2pk'); const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh'); @@ -356,6 +434,46 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); }); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input with nonWitnessUtxo', async () => { + const p2wsh = createPayment('p2wsh-p2pk'); + const inputData = await getInputData(5e4, p2wsh.payment, false, 'p2wsh'); + { + const { + hash, + index, + nonWitnessUtxo, + witnessScript, // NEW: A Buffer of the witnessScript + } = inputData; + assert.deepStrictEqual( + { hash, index, nonWitnessUtxo, witnessScript }, + inputData, + ); + } + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2wsh.keys[0]); + + assert.strictEqual(psbt.validateSignaturesOfInput(0), 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, + }); + }); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh'); @@ -406,6 +524,56 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); }); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo', async () => { + const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); + const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh-p2wsh'); + { + const { + hash, + index, + nonWitnessUtxo, + redeemScript, + witnessScript, + } = inputData; + assert.deepStrictEqual( + { hash, index, nonWitnessUtxo, redeemScript, witnessScript }, + inputData, + ); + } + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2sh.keys[0]) + .signInput(0, p2sh.keys[2]) + .signInput(0, p2sh.keys[3]); + + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual( + psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey), + true, + ); + assert.throws(() => { + psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey); + }, new RegExp('No signatures for this pubkey')); + 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, + }); + }); + 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; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index a83ced3..0431056 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1025,7 +1025,7 @@ function getHashForSig( } const prevoutIndex = unsignedTx.ins[inputIndex].index; - const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; + const prevout = nonWitnessUtxoTx.outs[prevoutIndex] as Output; if (input.redeemScript) { // If a redeemScript is provided, the scriptPubKey must be for that redeemScript @@ -1035,14 +1035,29 @@ function getHashForSig( script = prevout.script; } - if (isP2WPKH(script) || isP2WSHScript(script)) { - throw new Error( - `Input #${inputIndex} has nonWitnessUtxo but segwit script: ` + - `${script.toString('hex')}`, + if (isP2WSHScript(script)) { + if (!input.witnessScript) + throw new Error('Segwit input needs witnessScript if not P2WPKH'); + checkWitnessScript(inputIndex, script, input.witnessScript); + hash = unsignedTx.hashForWitnessV0( + inputIndex, + input.witnessScript, + prevout.value, + sighashType, ); + script = input.witnessScript; + } else if (isP2WPKH(script)) { + // P2WPKH uses the P2PKH template for prevoutScript when signing + const signingScript = payments.p2pkh({ hash: script.slice(2) }).output!; + hash = unsignedTx.hashForWitnessV0( + inputIndex, + signingScript, + prevout.value, + sighashType, + ); + } else { + hash = unsignedTx.hashForSignature(inputIndex, script, sighashType); } - - hash = unsignedTx.hashForSignature(inputIndex, script, sighashType); } else if (input.witnessUtxo) { let _script: Buffer; // so we don't shadow the `let script` above if (input.redeemScript) { @@ -1165,11 +1180,14 @@ function getScriptFromInput( isP2SH: false, isP2WSH: false, }; - if (input.nonWitnessUtxo) { - if (input.redeemScript) { - res.isP2SH = true; - res.script = input.redeemScript; - } else { + res.isP2SH = !!input.redeemScript; + res.isP2WSH = !!input.witnessScript; + if (input.witnessScript) { + res.script = input.witnessScript; + } else if (input.redeemScript) { + res.script = input.redeemScript; + } else { + if (input.nonWitnessUtxo) { const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( cache, input, @@ -1177,22 +1195,12 @@ function getScriptFromInput( ); const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; + } else if (input.witnessUtxo) { + res.script = input.witnessUtxo.script; } - } else if (input.witnessUtxo) { + } + if (input.witnessScript || isP2WPKH(res.script!)) { res.isSegwit = true; - res.isP2SH = !!input.redeemScript; - res.isP2WSH = !!input.witnessScript; - if (input.witnessScript) { - res.script = input.witnessScript; - } else if (input.redeemScript) { - res.script = payments.p2wpkh({ - hash: input.redeemScript.slice(2), - }).output!; - } else { - res.script = payments.p2wpkh({ - hash: input.witnessUtxo.script.slice(2), - }).output!; - } } return res; } From 4264bd631cf148c5da93b7598894cdf04df84d84 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 23 Aug 2019 14:46:40 +0900 Subject: [PATCH 444/568] Bump bip174 --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index d528ba3..3913198 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-1.0.0.tgz", - "integrity": "sha512-AaoWrkYtv6A2y8H+qzs6NvRWypzNbADT8PQGpM9rnP+jLzeol+uzhe3Myeuq/dwrHYtmsW8V71HmX2oXhQGagw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-1.0.1.tgz", + "integrity": "sha512-Mq2aFs1TdMfxBpYPg7uzjhsiXbAtoVq44TNjEWtvuZBiBgc3m7+n55orYMtTAxdg7jWbL4DtH0MKocJER4xERQ==" }, "bip32": { "version": "2.0.4", diff --git a/package.json b/package.json index be2321f..dda06b9 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "^1.0.0", + "bip174": "^1.0.1", "bip32": "^2.0.4", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", From a395af47fdb374ea14b6b1aff24fd065ae0ebe26 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 23 Aug 2019 15:49:24 +0900 Subject: [PATCH 445/568] Simplify nonWitnessUtxo integration tests to not confuse learners --- test/integration/transactions-psbt.js | 87 +++------------------------ 1 file changed, 10 insertions(+), 77 deletions(-) diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index 9d40ed0..9040fc2 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -283,41 +283,27 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input with nonWitnessUtxo', async () => { + // For learning purposes, ignore this test. + // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData const p2sh = createPayment('p2sh-p2wpkh'); const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh'); const inputData2 = await getInputData(5e4, p2sh.payment, false, 'p2sh'); - { - const { - hash, - index, - nonWitnessUtxo, - redeemScript, - } = inputData; - assert.deepStrictEqual( - { hash, index, nonWitnessUtxo, redeemScript }, - inputData, - ); - } const keyPair = p2sh.keys[0]; const outputData = { - script: p2sh.payment.output, // sending to myself for fun + script: p2sh.payment.output, value: 2e4, }; const outputData2 = { - script: p2sh.payment.output, // sending to myself for fun + script: p2sh.payment.output, value: 7e4, }; - const tx = new bitcoin.Psbt() .addInputs([inputData, inputData2]) .addOutputs([outputData, outputData2]) .signAllInputs(keyPair) .finalizeAllInputs() .extractTransaction(); - - // build and broadcast to the Bitcoin RegTest network await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ txId: tx.getId(), address: p2sh.payment.address, @@ -361,15 +347,10 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input with nonWitnessUtxo', async () => { - // the only thing that changes is you don't give a redeemscript for input data - + // For learning purposes, ignore this test. + // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData const p2wpkh = createPayment('p2wpkh'); const inputData = await getInputData(5e4, p2wpkh.payment, false, 'noredeem'); - { - const { hash, index, nonWitnessUtxo } = inputData; - assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData); - } - const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ @@ -377,15 +358,9 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { value: 2e4, }) .signInput(0, p2wpkh.keys[0]); - - assert.strictEqual(psbt.validateSignaturesOfInput(0), 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, @@ -435,21 +410,10 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input with nonWitnessUtxo', async () => { + // For learning purposes, ignore this test. + // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData const p2wsh = createPayment('p2wsh-p2pk'); const inputData = await getInputData(5e4, p2wsh.payment, false, 'p2wsh'); - { - const { - hash, - index, - nonWitnessUtxo, - witnessScript, // NEW: A Buffer of the witnessScript - } = inputData; - assert.deepStrictEqual( - { hash, index, nonWitnessUtxo, witnessScript }, - inputData, - ); - } - const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ @@ -457,15 +421,9 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { value: 2e4, }) .signInput(0, p2wsh.keys[0]); - - assert.strictEqual(psbt.validateSignaturesOfInput(0), 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, @@ -525,22 +483,10 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo', async () => { + // For learning purposes, ignore this test. + // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh-p2wsh'); - { - const { - hash, - index, - nonWitnessUtxo, - redeemScript, - witnessScript, - } = inputData; - assert.deepStrictEqual( - { hash, index, nonWitnessUtxo, redeemScript, witnessScript }, - inputData, - ); - } - const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ @@ -550,22 +496,9 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .signInput(0, p2sh.keys[0]) .signInput(0, p2sh.keys[2]) .signInput(0, p2sh.keys[3]); - - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); - assert.strictEqual( - psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey), - true, - ); - assert.throws(() => { - psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey); - }, new RegExp('No signatures for this pubkey')); 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, From 2389b5bf902fa87221052706ce889df3e3b85f29 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 23 Aug 2019 17:48:46 +0900 Subject: [PATCH 446/568] 5.1.4 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3913198..bb292ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.3", + "version": "5.1.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index dda06b9..07bb419 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.3", + "version": "5.1.4", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 66f9b0bbe07c79453120f5a3038b4df7eaa3f1f8 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 23 Aug 2019 18:10:38 +0900 Subject: [PATCH 447/568] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfac91..3e23430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 5.1.4 +__changed__ +- `Psbt` inputs using segwit scripts can now work with nonWitnessUtxo as well as the original witnessUtxo. The reasoning for this is that nonWitnessUtxo has all the information contained in the witnessUtxo, so rejecting signing even though we have all the info we need is unnecessary. Trying to sign a non-segwit script with a witnessUtxo will still throw an Error as it should. + # 5.1.3 __changed__ - TypeScript types: Made Signer and SignerAsync use any for network since we only check for equivalence. (#1448) From 139197e545b9622e35e72b5b75a0a3b0a632a28b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 26 Aug 2019 19:15:05 +0900 Subject: [PATCH 448/568] Add getFee and getVSize --- src/psbt.js | 56 ++++++++++++++++++++++++++++++----------- test/psbt.js | 10 ++++++++ ts_src/psbt.ts | 67 ++++++++++++++++++++++++++++++++++++++----------- types/psbt.d.ts | 2 ++ 4 files changed, 107 insertions(+), 28 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index e134774..d9ed3b1 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -153,6 +153,8 @@ class Psbt { if (input.nonWitnessUtxo) { addNonWitnessTxCache(this.__CACHE, input, inputIndex); } + c.__FEE = undefined; + c.__VSIZE = undefined; c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -171,6 +173,8 @@ class Psbt { } const c = this.__CACHE; this.data.addOutput(outputData); + c.__FEE = undefined; + c.__VSIZE = undefined; c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -187,20 +191,23 @@ class Psbt { return tx; } getFeeRate() { - if (!this.data.inputs.every(isFinalized)) - throw new Error('PSBT must be finalized to calculate fee rate'); - const c = this.__CACHE; - if (c.__FEE_RATE) return c.__FEE_RATE; - let tx; - let mustFinalize = true; - if (c.__EXTRACTED_TX) { - tx = c.__EXTRACTED_TX; - mustFinalize = false; - } else { - tx = c.__TX.clone(); - } - inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize); - return c.__FEE_RATE; + return getTxCacheValue( + '__FEE_RATE', + 'fee rate', + this.data.inputs, + this.__CACHE, + ); + } + getFee() { + return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE); + } + getVSize() { + return getTxCacheValue( + '__VSIZE', + 'virtual size', + this.data.inputs, + this.__CACHE, + ); } finalizeAllInputs() { utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one @@ -724,6 +731,25 @@ const checkWitnessScript = scriptCheckerFactory( payments.p2wsh, 'Witness script', ); +function getTxCacheValue(key, name, inputs, c) { + if (!inputs.every(isFinalized)) + throw new Error(`PSBT must be finalized to calculate ${name}`); + if (key === '__FEE_RATE' && c.__FEE_RATE) return c.__FEE_RATE; + if (key === '__FEE' && c.__FEE) return c.__FEE; + if (key === '__VSIZE' && c.__VSIZE) return c.__VSIZE; + let tx; + let mustFinalize = true; + if (c.__EXTRACTED_TX) { + tx = c.__EXTRACTED_TX; + mustFinalize = false; + } else { + tx = c.__TX.clone(); + } + inputFinalizeGetAmts(inputs, tx, c, mustFinalize); + if (key === '__FEE_RATE') return c.__FEE_RATE; + else if (key === '__FEE') return c.__FEE; + else if (key === '__VSIZE') return c.__VSIZE; +} function getFinalScripts( script, scriptType, @@ -1124,6 +1150,8 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) { throw new Error('Outputs are spending more than Inputs'); } const bytes = tx.virtualSize(); + cache.__VSIZE = bytes; + cache.__FEE = fee; cache.__EXTRACTED_TX = tx; cache.__FEE_RATE = Math.floor(fee / bytes); } diff --git a/test/psbt.js b/test/psbt.js index 467e426..4d4b3b5 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -149,6 +149,16 @@ describe(`Psbt`, () => { const fr1 = psbt5.getFeeRate() const fr2 = psbt5.getFeeRate() assert.strictEqual(fr1, fr2) + + const psbt6 = Psbt.fromBase64(f.psbt) + const f1 = psbt6.getFee() + const f2 = psbt6.getFee() + assert.strictEqual(f1, f2) + + const psbt7 = Psbt.fromBase64(f.psbt) + const vs1 = psbt7.getVSize() + const vs2 = psbt7.getVSize() + assert.strictEqual(vs1, vs2) }) }) }) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 0431056..333fa20 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -194,6 +194,8 @@ export class Psbt { if (input.nonWitnessUtxo) { addNonWitnessTxCache(this.__CACHE, input, inputIndex); } + c.__FEE = undefined; + c.__VSIZE = undefined; c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -214,6 +216,8 @@ export class Psbt { } const c = this.__CACHE; this.data.addOutput(outputData); + c.__FEE = undefined; + c.__VSIZE = undefined; c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -232,20 +236,25 @@ export class Psbt { } getFeeRate(): number { - if (!this.data.inputs.every(isFinalized)) - throw new Error('PSBT must be finalized to calculate fee rate'); - const c = this.__CACHE; - if (c.__FEE_RATE) return c.__FEE_RATE; - let tx: Transaction; - let mustFinalize = true; - if (c.__EXTRACTED_TX) { - tx = c.__EXTRACTED_TX; - mustFinalize = false; - } else { - tx = c.__TX.clone(); - } - inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize); - return c.__FEE_RATE!; + return getTxCacheValue( + '__FEE_RATE', + 'fee rate', + this.data.inputs, + this.__CACHE, + )!; + } + + getFee(): number { + return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE)!; + } + + getVSize(): number { + return getTxCacheValue( + '__VSIZE', + 'virtual size', + this.data.inputs, + this.__CACHE, + )!; } finalizeAllInputs(): this { @@ -610,6 +619,8 @@ interface PsbtCache { __TX_IN_CACHE: { [index: string]: number }; __TX: Transaction; __FEE_RATE?: number; + __FEE?: number; + __VSIZE?: number; __EXTRACTED_TX?: Transaction; } @@ -920,6 +931,32 @@ const checkWitnessScript = scriptCheckerFactory( 'Witness script', ); +type TxCacheNumberKey = '__FEE_RATE' | '__FEE' | '__VSIZE'; +function getTxCacheValue( + key: TxCacheNumberKey, + name: string, + inputs: PsbtInput[], + c: PsbtCache, +): number | undefined { + if (!inputs.every(isFinalized)) + throw new Error(`PSBT must be finalized to calculate ${name}`); + if (key === '__FEE_RATE' && c.__FEE_RATE) return c.__FEE_RATE; + if (key === '__FEE' && c.__FEE) return c.__FEE; + if (key === '__VSIZE' && c.__VSIZE) return c.__VSIZE; + let tx: Transaction; + let mustFinalize = true; + if (c.__EXTRACTED_TX) { + tx = c.__EXTRACTED_TX; + mustFinalize = false; + } else { + tx = c.__TX.clone(); + } + inputFinalizeGetAmts(inputs, tx, c, mustFinalize); + if (key === '__FEE_RATE') return c.__FEE_RATE!; + else if (key === '__FEE') return c.__FEE!; + else if (key === '__VSIZE') return c.__VSIZE!; +} + function getFinalScripts( script: Buffer, scriptType: string, @@ -1398,6 +1435,8 @@ function inputFinalizeGetAmts( throw new Error('Outputs are spending more than Inputs'); } const bytes = tx.virtualSize(); + cache.__VSIZE = bytes; + cache.__FEE = fee; cache.__EXTRACTED_TX = tx; cache.__FEE_RATE = Math.floor(fee / bytes); } diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 6a56636..dddedd7 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -57,6 +57,8 @@ export declare class Psbt { addOutput(outputData: PsbtOutputExtended): this; extractTransaction(disableFeeCheck?: boolean): Transaction; getFeeRate(): number; + getFee(): number; + getVSize(): number; finalizeAllInputs(): this; finalizeInput(inputIndex: number): this; validateSignaturesOfAllInputs(): boolean; From 14d10c74a53b2500294d4b7c0f4f701da2656bbc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 27 Aug 2019 10:06:43 +0900 Subject: [PATCH 449/568] Remove VSize, can get from Transaction --- src/psbt.js | 13 ------------- test/psbt.js | 5 ----- ts_src/psbt.ts | 17 +---------------- types/psbt.d.ts | 1 - 4 files changed, 1 insertion(+), 35 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index d9ed3b1..57a159c 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -154,7 +154,6 @@ class Psbt { addNonWitnessTxCache(this.__CACHE, input, inputIndex); } c.__FEE = undefined; - c.__VSIZE = undefined; c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -174,7 +173,6 @@ class Psbt { const c = this.__CACHE; this.data.addOutput(outputData); c.__FEE = undefined; - c.__VSIZE = undefined; c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -201,14 +199,6 @@ class Psbt { getFee() { return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE); } - getVSize() { - return getTxCacheValue( - '__VSIZE', - 'virtual size', - this.data.inputs, - this.__CACHE, - ); - } finalizeAllInputs() { utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx)); @@ -736,7 +726,6 @@ function getTxCacheValue(key, name, inputs, c) { throw new Error(`PSBT must be finalized to calculate ${name}`); if (key === '__FEE_RATE' && c.__FEE_RATE) return c.__FEE_RATE; if (key === '__FEE' && c.__FEE) return c.__FEE; - if (key === '__VSIZE' && c.__VSIZE) return c.__VSIZE; let tx; let mustFinalize = true; if (c.__EXTRACTED_TX) { @@ -748,7 +737,6 @@ function getTxCacheValue(key, name, inputs, c) { inputFinalizeGetAmts(inputs, tx, c, mustFinalize); if (key === '__FEE_RATE') return c.__FEE_RATE; else if (key === '__FEE') return c.__FEE; - else if (key === '__VSIZE') return c.__VSIZE; } function getFinalScripts( script, @@ -1150,7 +1138,6 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) { throw new Error('Outputs are spending more than Inputs'); } const bytes = tx.virtualSize(); - cache.__VSIZE = bytes; cache.__FEE = fee; cache.__EXTRACTED_TX = tx; cache.__FEE_RATE = Math.floor(fee / bytes); diff --git a/test/psbt.js b/test/psbt.js index 4d4b3b5..54eb5e4 100644 --- a/test/psbt.js +++ b/test/psbt.js @@ -154,11 +154,6 @@ describe(`Psbt`, () => { const f1 = psbt6.getFee() const f2 = psbt6.getFee() assert.strictEqual(f1, f2) - - const psbt7 = Psbt.fromBase64(f.psbt) - const vs1 = psbt7.getVSize() - const vs2 = psbt7.getVSize() - assert.strictEqual(vs1, vs2) }) }) }) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 333fa20..73f7cfc 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -195,7 +195,6 @@ export class Psbt { addNonWitnessTxCache(this.__CACHE, input, inputIndex); } c.__FEE = undefined; - c.__VSIZE = undefined; c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -217,7 +216,6 @@ export class Psbt { const c = this.__CACHE; this.data.addOutput(outputData); c.__FEE = undefined; - c.__VSIZE = undefined; c.__FEE_RATE = undefined; c.__EXTRACTED_TX = undefined; return this; @@ -248,15 +246,6 @@ export class Psbt { return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE)!; } - getVSize(): number { - return getTxCacheValue( - '__VSIZE', - 'virtual size', - this.data.inputs, - this.__CACHE, - )!; - } - finalizeAllInputs(): this { checkForInput(this.data.inputs, 0); // making sure we have at least one range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx)); @@ -620,7 +609,6 @@ interface PsbtCache { __TX: Transaction; __FEE_RATE?: number; __FEE?: number; - __VSIZE?: number; __EXTRACTED_TX?: Transaction; } @@ -931,7 +919,7 @@ const checkWitnessScript = scriptCheckerFactory( 'Witness script', ); -type TxCacheNumberKey = '__FEE_RATE' | '__FEE' | '__VSIZE'; +type TxCacheNumberKey = '__FEE_RATE' | '__FEE'; function getTxCacheValue( key: TxCacheNumberKey, name: string, @@ -942,7 +930,6 @@ function getTxCacheValue( throw new Error(`PSBT must be finalized to calculate ${name}`); if (key === '__FEE_RATE' && c.__FEE_RATE) return c.__FEE_RATE; if (key === '__FEE' && c.__FEE) return c.__FEE; - if (key === '__VSIZE' && c.__VSIZE) return c.__VSIZE; let tx: Transaction; let mustFinalize = true; if (c.__EXTRACTED_TX) { @@ -954,7 +941,6 @@ function getTxCacheValue( inputFinalizeGetAmts(inputs, tx, c, mustFinalize); if (key === '__FEE_RATE') return c.__FEE_RATE!; else if (key === '__FEE') return c.__FEE!; - else if (key === '__VSIZE') return c.__VSIZE!; } function getFinalScripts( @@ -1435,7 +1421,6 @@ function inputFinalizeGetAmts( throw new Error('Outputs are spending more than Inputs'); } const bytes = tx.virtualSize(); - cache.__VSIZE = bytes; cache.__FEE = fee; cache.__EXTRACTED_TX = tx; cache.__FEE_RATE = Math.floor(fee / bytes); diff --git a/types/psbt.d.ts b/types/psbt.d.ts index dddedd7..a0c669d 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -58,7 +58,6 @@ export declare class Psbt { extractTransaction(disableFeeCheck?: boolean): Transaction; getFeeRate(): number; getFee(): number; - getVSize(): number; finalizeAllInputs(): this; finalizeInput(inputIndex: number): this; validateSignaturesOfAllInputs(): boolean; From 77e068c388bdfe30a60b1a6b3fe3a7d60b494fe4 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 27 Aug 2019 10:13:18 +0900 Subject: [PATCH 450/568] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e23430..37e097f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 5.1.5 +__added__ +- `Psbt` now has `getFee(): number` for use when all inputs are finalized. It returns the satoshi fee of the transaction. Calling getFee, getFeeRate, or extractTransaction will cache these values so if you call one after the other, the second call will return immediately. + # 5.1.4 __changed__ - `Psbt` inputs using segwit scripts can now work with nonWitnessUtxo as well as the original witnessUtxo. The reasoning for this is that nonWitnessUtxo has all the information contained in the witnessUtxo, so rejecting signing even though we have all the info we need is unnecessary. Trying to sign a non-segwit script with a witnessUtxo will still throw an Error as it should. From 5bbf255b29dd1a0535b68790496b0dbd8b305e66 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 27 Aug 2019 10:13:24 +0900 Subject: [PATCH 451/568] 5.1.5 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index bb292ce..c2966f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.4", + "version": "5.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 07bb419..d701877 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.4", + "version": "5.1.5", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From d2d368b8cd169516b84deaae0d770f6420836029 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 2 Sep 2019 18:41:31 +0900 Subject: [PATCH 452/568] Fix PsbtOutputExtended address support --- ts_src/psbt.ts | 13 +++++++++++-- types/psbt.d.ts | 11 +++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 73f7cfc..d35fd4c 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -11,7 +11,6 @@ import { Transaction as ITransaction, TransactionFromBuffer, TransactionInput, - TransactionOutput, } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; import { toOutputScript } from './address'; @@ -624,7 +623,17 @@ interface PsbtOpts { interface PsbtInputExtended extends PsbtInput, TransactionInput {} -interface PsbtOutputExtended extends PsbtOutput, TransactionOutput {} +type PsbtOutputExtended = PsbtOutputExtendedAddress | PsbtOutputExtendedScript; + +interface PsbtOutputExtendedAddress extends PsbtOutput { + address: string; + value: number; +} + +interface PsbtOutputExtendedScript extends PsbtOutput { + script: Buffer; + value: number; +} interface HDSignerBase { /** diff --git a/types/psbt.d.ts b/types/psbt.d.ts index a0c669d..4db075c 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,6 +1,6 @@ /// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; -import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; +import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; @@ -87,7 +87,14 @@ interface PsbtOptsOptional { } interface PsbtInputExtended extends PsbtInput, TransactionInput { } -interface PsbtOutputExtended extends PsbtOutput, TransactionOutput { +declare type PsbtOutputExtended = PsbtOutputExtendedAddress | PsbtOutputExtendedScript; +interface PsbtOutputExtendedAddress extends PsbtOutput { + address: string; + value: number; +} +interface PsbtOutputExtendedScript extends PsbtOutput { + script: Buffer; + value: number; } interface HDSignerBase { /** From 893a85e2104b885f9daa2c09473355f34afe87ef Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 2 Sep 2019 18:47:52 +0900 Subject: [PATCH 453/568] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37e097f..f0f1e66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 5.1.6 +__fixed__ +- `PsbtOutputExtended` did not support using the address attribute properly. It is now fixed. + # 5.1.5 __added__ - `Psbt` now has `getFee(): number` for use when all inputs are finalized. It returns the satoshi fee of the transaction. Calling getFee, getFeeRate, or extractTransaction will cache these values so if you call one after the other, the second call will return immediately. From bc1a01987be968dd9600a92560f757b4f4c4bf2b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 2 Sep 2019 18:47:57 +0900 Subject: [PATCH 454/568] 5.1.6 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2966f8..7f92fcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.5", + "version": "5.1.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d701877..81ecd1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.5", + "version": "5.1.6", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 11e4a12caf71dc4ee9705a4250728b3a547e4276 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 4 Sep 2019 18:52:33 +0900 Subject: [PATCH 455/568] Change filenames for easy diff --- test/{address.js => address.spec.ts} | 0 test/{bitcoin.core.js => bitcoin.core.spec.ts} | 0 test/{block.js => block.spec.ts} | 0 test/{bufferutils.js => bufferutils.spec.ts} | 0 test/{classify.js => classify.spec.ts} | 0 test/{crypto.js => crypto.spec.ts} | 0 test/{ecpair.js => ecpair.spec.ts} | 0 test/integration/{_regtest.js => _regtest.spec.ts} | 0 test/integration/{addresses.js => addresses.spec.ts} | 0 test/integration/{bip32.js => bip32.spec.ts} | 0 test/integration/{blocks.js => blocks.spec.ts} | 0 test/integration/{cltv.js => cltv.spec.ts} | 0 test/integration/{csv.js => csv.spec.ts} | 0 test/integration/{payments.js => payments.spec.ts} | 0 .../{transactions-psbt.js => transactions-psbt.spec.ts} | 0 test/integration/{transactions.js => transactions.spec.ts} | 0 test/{payments.js => payments.spec.ts} | 0 test/{payments.utils.js => payments.utils.spec.ts} | 0 test/{psbt.js => psbt.spec.ts} | 0 test/{script.js => script.spec.ts} | 0 test/{script_number.js => script_number.spec.ts} | 0 test/{script_signature.js => script_signature.spec.ts} | 0 test/{transaction.js => transaction.spec.ts} | 0 test/{transaction_builder.js => transaction_builder.spec.ts} | 0 test/{types.js => types.spec.ts} | 0 25 files changed, 0 insertions(+), 0 deletions(-) rename test/{address.js => address.spec.ts} (100%) rename test/{bitcoin.core.js => bitcoin.core.spec.ts} (100%) rename test/{block.js => block.spec.ts} (100%) rename test/{bufferutils.js => bufferutils.spec.ts} (100%) rename test/{classify.js => classify.spec.ts} (100%) rename test/{crypto.js => crypto.spec.ts} (100%) rename test/{ecpair.js => ecpair.spec.ts} (100%) rename test/integration/{_regtest.js => _regtest.spec.ts} (100%) rename test/integration/{addresses.js => addresses.spec.ts} (100%) rename test/integration/{bip32.js => bip32.spec.ts} (100%) rename test/integration/{blocks.js => blocks.spec.ts} (100%) rename test/integration/{cltv.js => cltv.spec.ts} (100%) rename test/integration/{csv.js => csv.spec.ts} (100%) rename test/integration/{payments.js => payments.spec.ts} (100%) rename test/integration/{transactions-psbt.js => transactions-psbt.spec.ts} (100%) rename test/integration/{transactions.js => transactions.spec.ts} (100%) rename test/{payments.js => payments.spec.ts} (100%) rename test/{payments.utils.js => payments.utils.spec.ts} (100%) rename test/{psbt.js => psbt.spec.ts} (100%) rename test/{script.js => script.spec.ts} (100%) rename test/{script_number.js => script_number.spec.ts} (100%) rename test/{script_signature.js => script_signature.spec.ts} (100%) rename test/{transaction.js => transaction.spec.ts} (100%) rename test/{transaction_builder.js => transaction_builder.spec.ts} (100%) rename test/{types.js => types.spec.ts} (100%) diff --git a/test/address.js b/test/address.spec.ts similarity index 100% rename from test/address.js rename to test/address.spec.ts diff --git a/test/bitcoin.core.js b/test/bitcoin.core.spec.ts similarity index 100% rename from test/bitcoin.core.js rename to test/bitcoin.core.spec.ts diff --git a/test/block.js b/test/block.spec.ts similarity index 100% rename from test/block.js rename to test/block.spec.ts diff --git a/test/bufferutils.js b/test/bufferutils.spec.ts similarity index 100% rename from test/bufferutils.js rename to test/bufferutils.spec.ts diff --git a/test/classify.js b/test/classify.spec.ts similarity index 100% rename from test/classify.js rename to test/classify.spec.ts diff --git a/test/crypto.js b/test/crypto.spec.ts similarity index 100% rename from test/crypto.js rename to test/crypto.spec.ts diff --git a/test/ecpair.js b/test/ecpair.spec.ts similarity index 100% rename from test/ecpair.js rename to test/ecpair.spec.ts diff --git a/test/integration/_regtest.js b/test/integration/_regtest.spec.ts similarity index 100% rename from test/integration/_regtest.js rename to test/integration/_regtest.spec.ts diff --git a/test/integration/addresses.js b/test/integration/addresses.spec.ts similarity index 100% rename from test/integration/addresses.js rename to test/integration/addresses.spec.ts diff --git a/test/integration/bip32.js b/test/integration/bip32.spec.ts similarity index 100% rename from test/integration/bip32.js rename to test/integration/bip32.spec.ts diff --git a/test/integration/blocks.js b/test/integration/blocks.spec.ts similarity index 100% rename from test/integration/blocks.js rename to test/integration/blocks.spec.ts diff --git a/test/integration/cltv.js b/test/integration/cltv.spec.ts similarity index 100% rename from test/integration/cltv.js rename to test/integration/cltv.spec.ts diff --git a/test/integration/csv.js b/test/integration/csv.spec.ts similarity index 100% rename from test/integration/csv.js rename to test/integration/csv.spec.ts diff --git a/test/integration/payments.js b/test/integration/payments.spec.ts similarity index 100% rename from test/integration/payments.js rename to test/integration/payments.spec.ts diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.spec.ts similarity index 100% rename from test/integration/transactions-psbt.js rename to test/integration/transactions-psbt.spec.ts diff --git a/test/integration/transactions.js b/test/integration/transactions.spec.ts similarity index 100% rename from test/integration/transactions.js rename to test/integration/transactions.spec.ts diff --git a/test/payments.js b/test/payments.spec.ts similarity index 100% rename from test/payments.js rename to test/payments.spec.ts diff --git a/test/payments.utils.js b/test/payments.utils.spec.ts similarity index 100% rename from test/payments.utils.js rename to test/payments.utils.spec.ts diff --git a/test/psbt.js b/test/psbt.spec.ts similarity index 100% rename from test/psbt.js rename to test/psbt.spec.ts diff --git a/test/script.js b/test/script.spec.ts similarity index 100% rename from test/script.js rename to test/script.spec.ts diff --git a/test/script_number.js b/test/script_number.spec.ts similarity index 100% rename from test/script_number.js rename to test/script_number.spec.ts diff --git a/test/script_signature.js b/test/script_signature.spec.ts similarity index 100% rename from test/script_signature.js rename to test/script_signature.spec.ts diff --git a/test/transaction.js b/test/transaction.spec.ts similarity index 100% rename from test/transaction.js rename to test/transaction.spec.ts diff --git a/test/transaction_builder.js b/test/transaction_builder.spec.ts similarity index 100% rename from test/transaction_builder.js rename to test/transaction_builder.spec.ts diff --git a/test/types.js b/test/types.spec.ts similarity index 100% rename from test/types.js rename to test/types.spec.ts From 6c08a0be40a4d382f64b7385531407372a7e3b91 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 7 Sep 2019 13:42:03 +0900 Subject: [PATCH 456/568] Move tests to TypeScript (coverage is still JS based) --- .gitignore | 3 + package-lock.json | 712 +++++++++++--- package.json | 19 +- test/address.spec.ts | 176 ++-- test/bitcoin.core.spec.ts | 291 +++--- test/block.spec.ts | 183 ++-- test/bufferutils.spec.ts | 54 +- test/classify.spec.ts | 200 ++-- test/crypto.spec.ts | 29 +- test/ecpair.spec.ts | 410 ++++---- test/integration/_regtest.spec.ts | 8 - test/integration/_regtest.ts | 6 + test/integration/addresses.spec.ts | 136 +-- test/integration/bip32.spec.ts | 170 ++-- test/integration/blocks.spec.ts | 29 +- test/integration/cltv.spec.ts | 237 ++--- test/integration/csv.spec.ts | 334 ++++--- test/integration/payments.spec.ts | 148 ++- test/integration/transactions-psbt.spec.ts | 66 +- test/integration/transactions.spec.ts | 410 ++++---- test/payments.spec.ts | 143 +-- test/payments.utils.spec.ts | 134 --- test/payments.utils.ts | 169 ++++ test/psbt.spec.ts | 733 +++++++------- test/script.spec.ts | 189 ++-- test/script_number.spec.ts | 30 +- test/script_signature.spec.ts | 71 +- test/transaction.spec.ts | 386 ++++---- test/transaction_builder.spec.ts | 1018 +++++++++++--------- test/ts-node-register.js | 5 + test/tsconfig.json | 40 + test/types.spec.ts | 58 +- ts_src/block.ts | 6 +- ts_src/classify.ts | 4 +- ts_src/index.ts | 8 +- ts_src/payments/index.ts | 2 + tslint.json | 2 +- types/block.d.ts | 6 +- types/classify.d.ts | 4 +- types/index.d.ts | 2 +- types/payments/index.d.ts | 1 + 41 files changed, 3920 insertions(+), 2712 deletions(-) delete mode 100644 test/integration/_regtest.spec.ts create mode 100644 test/integration/_regtest.ts delete mode 100644 test/payments.utils.spec.ts create mode 100644 test/payments.utils.ts create mode 100644 test/ts-node-register.js create mode 100644 test/tsconfig.json diff --git a/.gitignore b/.gitignore index a6c0ab8..d6ceb6b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ coverage node_modules .nyc_output npm-debug.log +test/*.js +test/integration/*.js +!test/ts-node-register.js diff --git a/package-lock.json b/package-lock.json index 7f92fcd..5812365 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,14 +14,14 @@ } }, "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", + "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", "dev": true, "requires": { - "@babel/types": "^7.4.4", + "@babel/types": "^7.5.5", "jsesc": "^2.5.1", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "source-map": "^0.5.0", "trim-right": "^1.0.1" } @@ -67,9 +67,9 @@ } }, "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", "dev": true }, "@babel/template": { @@ -84,22 +84,31 @@ } }, "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", + "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.5.5", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", + "@babel/parser": "^7.5.5", + "@babel/types": "^7.5.5", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.11" + "lodash": "^4.17.13" }, "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -108,35 +117,65 @@ "requires": { "ms": "^2.1.1" } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true } } }, "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", "dev": true, "requires": { "esutils": "^2.0.2", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } }, + "@types/base-x": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/base-x/-/base-x-3.0.0.tgz", + "integrity": "sha512-vnqSlpsv9uFX5/z8GyKWAfWHhLGJDBkrgRRsnxlsX23DHOlNyqP/eHQiv4TwnYcZULzQIxaWA/xRWU9Dyy4qzw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/bs58": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.0.tgz", + "integrity": "sha512-gYX+MHD4G/R+YGYwdhG5gbJj4LsEQGr3Vg6gVDAbe7xC5Bn8dNNG2Lpo6uDX/rT5dE7VBj0rGEFuV8L0AEx4Rg==", + "dev": true, + "requires": { + "@types/base-x": "*" + } + }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, "@types/node": { "version": "10.12.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" }, + "@types/proxyquire": { + "version": "1.3.28", + "resolved": "https://registry.npmjs.org/@types/proxyquire/-/proxyquire-1.3.28.tgz", + "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==", + "dev": true + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, "ansi-styles": { @@ -163,6 +202,12 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "arg": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz", + "integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -219,16 +264,23 @@ } }, "bip39": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-2.5.0.tgz", - "integrity": "sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz", + "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==", "dev": true, "requires": { + "@types/node": "11.11.6", "create-hash": "^1.1.0", "pbkdf2": "^3.0.9", - "randombytes": "^2.0.1", - "safe-buffer": "^5.0.1", - "unorm": "^1.3.3" + "randombytes": "^2.0.1" + }, + "dependencies": { + "@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==", + "dev": true + } } }, "bip65": { @@ -300,6 +352,12 @@ "safe-buffer": "^5.1.2" } }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -345,16 +403,22 @@ } }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -436,22 +500,25 @@ } }, "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "lru-cache": "^4.0.1", + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "decamelize": { @@ -469,6 +536,15 @@ "strip-bom": "^3.0.0" } }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "dhttp": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dhttp/-/dhttp-3.0.3.tgz", @@ -522,6 +598,31 @@ "is-arrayish": "^0.2.1" } }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -559,21 +660,6 @@ "p-finally": "^1.0.0", "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } } }, "file-uri-to-path": { @@ -611,6 +697,15 @@ "locate-path": "^3.0.0" } }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, "foreground-child": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", @@ -619,6 +714,18 @@ "requires": { "cross-spawn": "^4", "signal-exit": "^3.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + } } }, "fs.realpath": { @@ -627,6 +734,12 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -663,9 +776,9 @@ "dev": true }, "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", "dev": true }, "growl": { @@ -694,12 +807,27 @@ } } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, "hash-base": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", @@ -728,9 +856,9 @@ } }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "hmac-drbg": { @@ -750,9 +878,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", "dev": true }, "imurmurhash": { @@ -788,6 +916,24 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -800,12 +946,30 @@ "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", "dev": true }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -843,9 +1007,9 @@ }, "dependencies": { "semver": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", - "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -894,12 +1058,6 @@ "ms": "^2.1.1" } }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -996,6 +1154,15 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -1016,6 +1183,12 @@ "semver": "^5.6.0" } }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, "map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", @@ -1125,22 +1298,59 @@ } }, "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", + "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", "dev": true, "requires": { + "ansi-colors": "3.2.3", "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", + "debug": "3.2.6", "diff": "3.5.0", "escape-string-regexp": "1.0.5", - "glob": "7.1.2", + "find-up": "3.0.0", + "glob": "7.1.3", "growl": "1.10.5", - "he": "1.1.1", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", "minimatch": "3.0.4", "mkdirp": "0.5.1", - "supports-color": "5.4.0" + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.2.2", + "yargs-parser": "13.0.0", + "yargs-unparser": "1.5.0" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "module-not-found-error": { @@ -1150,9 +1360,9 @@ "dev": true }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "nan": { @@ -1178,6 +1388,16 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1191,9 +1411,9 @@ }, "dependencies": { "resolve": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", - "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -1210,6 +1430,12 @@ "path-key": "^2.0.0" } }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, "nyc": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", @@ -1259,6 +1485,34 @@ } } }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1314,9 +1568,9 @@ "dev": true }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -1632,10 +1886,28 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "spawn-wrap": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", - "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", + "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", "dev": true, "requires": { "foreground-child": "^1.5.6", @@ -1673,9 +1945,9 @@ } }, "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "sprintf-js": { @@ -1691,23 +1963,22 @@ "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "strip-ansi": "^4.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^3.0.0" } }, "strip-bom": { @@ -1722,6 +1993,12 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", @@ -1783,6 +2060,27 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "ts-node": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", + "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + } + } + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -1831,9 +2129,9 @@ "dev": true }, "uglify-js": { - "version": "3.5.14", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.14.tgz", - "integrity": "sha512-dgyjIw8KFK6AyVl5vm2tEqPewv5TKGEiiVFLI1LbF+oHua/Njd8tZk3lIbF1AWU1rNdEg7scaceADb4zqCcWXg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", "dev": true, "optional": true, "requires": { @@ -1857,16 +2155,10 @@ } } }, - "unorm": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.5.0.tgz", - "integrity": "sha512-sMfSWoiRaXXeDZSXC+YRZ23H4xchQpwxjpw1tmfR+kgbBCaOgln4NI0LXejJIhnBuKINrB3WRn+ZI8IWssirVw==", - "dev": true - }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", "dev": true }, "validate-npm-package-license": { @@ -1902,6 +2194,15 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, "wif": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", @@ -1917,14 +2218,50 @@ "dev": true }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "wrappy": { @@ -1934,9 +2271,9 @@ "dev": true }, "write-file-atomic": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", - "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -1957,12 +2294,12 @@ "dev": true }, "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", "dev": true, "requires": { - "cliui": "^5.0.0", + "cliui": "^4.0.0", "find-up": "^3.0.0", "get-caller-file": "^2.0.1", "os-locale": "^3.1.0", @@ -1972,18 +2309,107 @@ "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "yargs-parser": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.0.tgz", - "integrity": "sha512-Yq+32PrijHRri0vVKQEm+ys8mbqWjLiwQkMFNXEENutzLPP0bE4Lcd4iA3OQY5HF+GD3xXxf0MEHb8E4/SA3AA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + }, + "dependencies": { + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 81ecd1b..700b7fd 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ ], "scripts": { "build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs", + "build:tests": "npm run clean:jstests && tsc -p ./test/tsconfig.json", "clean": "rimraf src types", + "clean:jstests": "rimraf 'test/**/!(ts-node-register)*.js'", "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", @@ -26,12 +28,13 @@ "gitdiff:ci": "npm run build && git diff --exit-code", "integration": "npm run build && npm run nobuild:integration", "lint": "tslint -p tsconfig.json -c tslint.json", + "mocha:ts": "mocha --recursive --require test/ts-node-register", "nobuild:coverage-report": "nyc report --reporter=lcov", "nobuild:coverage-html": "nyc report --reporter=html", - "nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha", - "nobuild:integration": "mocha --timeout 50000 test/integration/", - "nobuild:unit": "mocha", - "prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore", + "nobuild:coverage": "npm run build:tests && nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha && npm run clean:jstests", + "nobuild:integration": "npm run mocha:ts -- --timeout 50000 'test/integration/*.ts'", + "nobuild:unit": "npm run mocha:ts -- 'test/*.ts'", + "prettier": "prettier 'ts_src/**/*.ts' 'test/**/*.ts' --ignore-path ./.prettierignore", "prettierjs": "prettier 'src/**/*.js' --ignore-path ./.prettierignore", "test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" @@ -63,7 +66,10 @@ "wif": "^2.0.1" }, "devDependencies": { - "bip39": "^2.3.0", + "@types/bs58": "^4.0.0", + "@types/mocha": "^5.2.7", + "@types/proxyquire": "^1.3.28", + "bip39": "^3.0.2", "bip65": "^1.0.1", "bip68": "^1.0.3", "bn.js": "^4.11.8", @@ -71,12 +77,13 @@ "dhttp": "^3.0.0", "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", - "mocha": "^5.2.0", + "mocha": "^6.2.0", "nyc": "^14.1.1", "prettier": "1.16.4", "proxyquire": "^2.0.1", "regtest-client": "0.2.0", "rimraf": "^2.6.3", + "ts-node": "^8.3.0", "tslint": "^5.16.0", "typescript": "3.2.2" }, diff --git a/test/address.spec.ts b/test/address.spec.ts index be16879..b1304c3 100644 --- a/test/address.spec.ts +++ b/test/address.spec.ts @@ -1,134 +1,148 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const baddress = require('../src/address') -const bscript = require('../src/script') -const fixtures = require('./fixtures/address.json') -const NETWORKS = Object.assign({ - litecoin: { - messagePrefix: '\x19Litecoin Signed Message:\n', - bip32: { - public: 0x019da462, - private: 0x019d9cfe +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as baddress from '../src/address'; +import * as bscript from '../src/script'; +import * as fixtures from './fixtures/address.json'; + +const NETWORKS = Object.assign( + { + litecoin: { + messagePrefix: '\x19Litecoin Signed Message:\n', + bip32: { + public: 0x019da462, + private: 0x019d9cfe, + }, + pubKeyHash: 0x30, + scriptHash: 0x32, + wif: 0xb0, }, - pubKeyHash: 0x30, - scriptHash: 0x32, - wif: 0xb0 - } -}, require('../src/networks')) + }, + require('../src/networks'), +); describe('address', () => { describe('fromBase58Check', () => { fixtures.standard.forEach(f => { - if (!f.base58check) return + if (!f.base58check) return; it('decodes ' + f.base58check, () => { - const decode = baddress.fromBase58Check(f.base58check) + const decode = baddress.fromBase58Check(f.base58check); - assert.strictEqual(decode.version, f.version) - assert.strictEqual(decode.hash.toString('hex'), f.hash) - }) - }) + assert.strictEqual(decode.version, f.version); + assert.strictEqual(decode.hash.toString('hex'), f.hash); + }); + }); fixtures.invalid.fromBase58Check.forEach(f => { it('throws on ' + f.exception, () => { assert.throws(() => { - baddress.fromBase58Check(f.address) - }, new RegExp(f.address + ' ' + f.exception)) - }) - }) - }) + baddress.fromBase58Check(f.address); + }, new RegExp(f.address + ' ' + f.exception)); + }); + }); + }); describe('fromBech32', () => { fixtures.standard.forEach(f => { - if (!f.bech32) return + if (!f.bech32) return; it('decodes ' + f.bech32, () => { - const actual = baddress.fromBech32(f.bech32) + const actual = baddress.fromBech32(f.bech32); - assert.strictEqual(actual.version, f.version) - assert.strictEqual(actual.prefix, NETWORKS[f.network].bech32) - assert.strictEqual(actual.data.toString('hex'), f.data) - }) - }) + assert.strictEqual(actual.version, f.version); + assert.strictEqual(actual.prefix, NETWORKS[f.network].bech32); + assert.strictEqual(actual.data.toString('hex'), f.data); + }); + }); - fixtures.invalid.bech32.forEach((f, i) => { - it('decode fails for ' + f.bech32 + '(' + f.exception + ')', () => { + fixtures.invalid.bech32.forEach(f => { + it('decode fails for ' + f.address + '(' + f.exception + ')', () => { assert.throws(() => { - baddress.fromBech32(f.address) - }, new RegExp(f.exception)) - }) - }) - }) + baddress.fromBech32(f.address); + }, new RegExp(f.exception)); + }); + }); + }); describe('fromOutputScript', () => { fixtures.standard.forEach(f => { it('encodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', () => { - const script = bscript.fromASM(f.script) - const address = baddress.fromOutputScript(script, NETWORKS[f.network]) + const script = bscript.fromASM(f.script); + const address = baddress.fromOutputScript(script, NETWORKS[f.network]); - assert.strictEqual(address, f.base58check || f.bech32.toLowerCase()) - }) - }) + assert.strictEqual(address, f.base58check || f.bech32!.toLowerCase()); + }); + }); fixtures.invalid.fromOutputScript.forEach(f => { it('throws when ' + f.script.slice(0, 30) + '... ' + f.exception, () => { - const script = bscript.fromASM(f.script) + const script = bscript.fromASM(f.script); assert.throws(() => { - baddress.fromOutputScript(script) - }, new RegExp(f.exception)) - }) - }) - }) + baddress.fromOutputScript(script); + }, new RegExp(f.exception)); + }); + }); + }); describe('toBase58Check', () => { fixtures.standard.forEach(f => { - if (!f.base58check) return + if (!f.base58check) return; it('encodes ' + f.hash + ' (' + f.network + ')', () => { - const address = baddress.toBase58Check(Buffer.from(f.hash, 'hex'), f.version) + const address = baddress.toBase58Check( + Buffer.from(f.hash, 'hex'), + f.version, + ); - assert.strictEqual(address, f.base58check) - }) - }) - }) + assert.strictEqual(address, f.base58check); + }); + }); + }); describe('toBech32', () => { - fixtures.bech32.forEach((f, i) => { - if (!f.bech32) return - const data = Buffer.from(f.data, 'hex') + fixtures.bech32.forEach(f => { + if (!f.address) return; + const data = Buffer.from(f.data, 'hex'); it('encode ' + f.address, () => { - assert.deepStrictEqual(baddress.toBech32(data, f.version, f.prefix), f.address) - }) - }) + assert.deepStrictEqual( + baddress.toBech32(data, f.version, f.prefix), + f.address.toLowerCase(), + ); + }); + }); - fixtures.invalid.bech32.forEach((f, i) => { - if (!f.prefix || f.version === undefined || f.data === undefined) return + // TODO: These fixtures (according to TypeScript) have none of the data used below + fixtures.invalid.bech32.forEach((f: any) => { + if (!f.prefix || f.version === undefined || f.data === undefined) return; it('encode fails (' + f.exception, () => { assert.throws(() => { - baddress.toBech32(Buffer.from(f.data, 'hex'), f.version, f.prefix) - }, new RegExp(f.exception)) - }) - }) - }) + baddress.toBech32(Buffer.from(f.data, 'hex'), f.version, f.prefix); + }, new RegExp(f.exception)); + }); + }); + }); describe('toOutputScript', () => { fixtures.standard.forEach(f => { it('decodes ' + f.script.slice(0, 30) + '... (' + f.network + ')', () => { - const script = baddress.toOutputScript(f.base58check || f.bech32, NETWORKS[f.network]) + const script = baddress.toOutputScript( + (f.base58check || f.bech32)!, + NETWORKS[f.network], + ); - assert.strictEqual(bscript.toASM(script), f.script) - }) - }) + assert.strictEqual(bscript.toASM(script), f.script); + }); + }); fixtures.invalid.toOutputScript.forEach(f => { it('throws when ' + f.exception, () => { assert.throws(() => { - baddress.toOutputScript(f.address, f.network) - }, new RegExp(f.address + ' ' + f.exception)) - }) - }) - }) -}) + baddress.toOutputScript(f.address, f.network as any); + }, new RegExp(f.address + ' ' + f.exception)); + }); + }); + }); +}); diff --git a/test/bitcoin.core.spec.ts b/test/bitcoin.core.spec.ts index 734c9a9..55b16ac 100644 --- a/test/bitcoin.core.spec.ts +++ b/test/bitcoin.core.spec.ts @@ -1,226 +1,251 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const base58 = require('bs58') -const bitcoin = require('../') - -const base58EncodeDecode = require('./fixtures/core/base58_encode_decode.json') -const base58KeysInvalid = require('./fixtures/core/base58_keys_invalid.json') -const base58KeysValid = require('./fixtures/core/base58_keys_valid.json') -const blocksValid = require('./fixtures/core/blocks.json') -const sigCanonical = require('./fixtures/core/sig_canonical.json') -const sigHash = require('./fixtures/core/sighash.json') -const sigNoncanonical = require('./fixtures/core/sig_noncanonical.json') -const txValid = require('./fixtures/core/tx_valid.json') +import * as assert from 'assert'; +import * as base58 from 'bs58'; +import { describe, it } from 'mocha'; +import * as bitcoin from '..'; +import * as base58EncodeDecode from './fixtures/core/base58_encode_decode.json'; +import * as base58KeysInvalid from './fixtures/core/base58_keys_invalid.json'; +import * as base58KeysValid from './fixtures/core/base58_keys_valid.json'; +import * as blocksValid from './fixtures/core/blocks.json'; +import * as sigCanonical from './fixtures/core/sig_canonical.json'; +import * as sigHash from './fixtures/core/sighash.json'; +import * as sigNoncanonical from './fixtures/core/sig_noncanonical.json'; +import * as txValid from './fixtures/core/tx_valid.json'; describe('Bitcoin-core', () => { // base58EncodeDecode describe('base58', () => { base58EncodeDecode.forEach(f => { - const fhex = f[0] - const fb58 = f[1] + const fhex = f[0]; + const fb58 = f[1]; it('can decode ' + fb58, () => { - const buffer = base58.decode(fb58) - const actual = buffer.toString('hex') + const buffer = base58.decode(fb58); + const actual = buffer.toString('hex'); - assert.strictEqual(actual, fhex) - }) + assert.strictEqual(actual, fhex); + }); it('can encode ' + fhex, () => { - const buffer = Buffer.from(fhex, 'hex') - const actual = base58.encode(buffer) + const buffer = Buffer.from(fhex, 'hex'); + const actual = base58.encode(buffer); - assert.strictEqual(actual, fb58) - }) - }) - }) + assert.strictEqual(actual, fb58); + }); + }); + }); // base58KeysValid describe('address.toBase58Check', () => { - const typeMap = { - 'pubkey': 'pubKeyHash', - 'script': 'scriptHash' - } + const typeMap: any = { + pubkey: 'pubKeyHash', + script: 'scriptHash', + }; base58KeysValid.forEach(f => { - const expected = f[0] - const hash = Buffer.from(f[1], 'hex') - const params = f[2] + const expected = f[0]; + const hash = Buffer.from(f[1] as any, 'hex'); + const params = f[2] as any; - if (params.isPrivkey) return + if (params.isPrivkey) return; - const network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin - const version = network[typeMap[params.addrType]] + const network: any = params.isTestnet + ? bitcoin.networks.testnet + : bitcoin.networks.bitcoin; + const version = network[typeMap[params.addrType]]; it('can export ' + expected, () => { - assert.strictEqual(bitcoin.address.toBase58Check(hash, version), expected) - }) - }) - }) + assert.strictEqual( + bitcoin.address.toBase58Check(hash, version), + expected, + ); + }); + }); + }); // base58KeysInvalid describe('address.fromBase58Check', () => { const allowedNetworks = [ - bitcoin.networks.bitcoin.pubkeyhash, - bitcoin.networks.bitcoin.scripthash, - bitcoin.networks.testnet.pubkeyhash, - bitcoin.networks.testnet.scripthash - ] + bitcoin.networks.bitcoin.pubKeyHash, + bitcoin.networks.bitcoin.scriptHash, + bitcoin.networks.testnet.pubKeyHash, + bitcoin.networks.testnet.scriptHash, + ]; base58KeysInvalid.forEach(f => { - const string = f[0] + const string = f[0]; it('throws on ' + string, () => { assert.throws(() => { - const address = bitcoin.address.fromBase58Check(string) + const address = bitcoin.address.fromBase58Check(string); - assert.notStrictEqual(allowedNetworks.indexOf(address.version), -1, 'Invalid network') - }, /(Invalid (checksum|network))|(too (short|long))/) - }) - }) - }) + assert.notStrictEqual( + allowedNetworks.indexOf(address.version), + -1, + 'Invalid network', + ); + }, /(Invalid (checksum|network))|(too (short|long))/); + }); + }); + }); // base58KeysValid describe('ECPair', () => { base58KeysValid.forEach(f => { - const string = f[0] - const hex = f[1] - const params = f[2] + const strng = f[0] as string; + const hex = f[1]; + const params = f[2] as any; - if (!params.isPrivkey) return + if (!params.isPrivkey) return; - const network = params.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin - const keyPair = bitcoin.ECPair.fromWIF(string, network) + const network = params.isTestnet + ? bitcoin.networks.testnet + : bitcoin.networks.bitcoin; + const keyPair = bitcoin.ECPair.fromWIF(strng, network); - it('fromWIF imports ' + string, () => { - assert.strictEqual(keyPair.privateKey.toString('hex'), hex) - assert.strictEqual(keyPair.compressed, params.isCompressed) - }) + it('fromWIF imports ' + strng, () => { + assert.strictEqual(keyPair.privateKey!.toString('hex'), hex); + assert.strictEqual(keyPair.compressed, params.isCompressed); + }); - it('toWIF exports ' + hex + ' to ' + string, () => { - assert.strictEqual(keyPair.toWIF(), string) - }) - }) - }) + it('toWIF exports ' + hex + ' to ' + strng, () => { + assert.strictEqual(keyPair.toWIF(), strng); + }); + }); + }); // base58KeysInvalid describe('ECPair.fromWIF', () => { const allowedNetworks = [ bitcoin.networks.bitcoin, - bitcoin.networks.testnet - ] + bitcoin.networks.testnet, + ]; base58KeysInvalid.forEach(f => { - const string = f[0] + const string = f[0]; it('throws on ' + string, () => { assert.throws(() => { - bitcoin.ECPair.fromWIF(string, allowedNetworks) - }, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/) - }) - }) - }) + bitcoin.ECPair.fromWIF(string, allowedNetworks); + }, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/); + }); + }); + }); describe('Block.fromHex', () => { blocksValid.forEach(f => { it('can parse ' + f.id, () => { - const block = bitcoin.Block.fromHex(f.hex) + const block = bitcoin.Block.fromHex(f.hex); - assert.strictEqual(block.getId(), f.id) - assert.strictEqual(block.transactions.length, f.transactions) - }) - }) - }) + assert.strictEqual(block.getId(), f.id); + assert.strictEqual(block.transactions!.length, f.transactions); + }); + }); + }); // txValid describe('Transaction.fromHex', () => { txValid.forEach(f => { // Objects that are only a single string are ignored - if (f.length === 1) return + if (f.length === 1) return; - const inputs = f[0] - const fhex = f[1] + const inputs = f[0]; + const fhex = f[1]; // const verifyFlags = f[2] // TODO: do we need to test this? it('can decode ' + fhex, () => { - const transaction = bitcoin.Transaction.fromHex(fhex) + const transaction = bitcoin.Transaction.fromHex(fhex as string); transaction.ins.forEach((txIn, i) => { - const input = inputs[i] + const input = inputs[i]; // reverse because test data is reversed - const prevOutHash = Buffer.from(input[0], 'hex').reverse() - const prevOutIndex = input[1] + const prevOutHash = Buffer.from(input[0] as string, 'hex').reverse(); + const prevOutIndex = input[1]; - assert.deepStrictEqual(txIn.hash, prevOutHash) + assert.deepStrictEqual(txIn.hash, prevOutHash); // we read UInt32, not Int32 - assert.strictEqual(txIn.index & 0xffffffff, prevOutIndex) - }) - }) - }) - }) + assert.strictEqual(txIn.index & 0xffffffff, prevOutIndex); + }); + }); + }); + }); // sighash describe('Transaction', () => { sigHash.forEach(f => { // Objects that are only a single string are ignored - if (f.length === 1) return + if (f.length === 1) return; - const txHex = f[0] - const scriptHex = f[1] - const inIndex = f[2] - const hashType = f[3] - const expectedHash = f[4] + const txHex = f[0] as string; + const scriptHex = f[1] as string; + const inIndex = f[2] as number; + const hashType = f[3] as number; + const expectedHash = f[4]; - const hashTypes = [] - if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_NONE) hashTypes.push('SIGHASH_NONE') - else if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_SINGLE) hashTypes.push('SIGHASH_SINGLE') - else hashTypes.push('SIGHASH_ALL') - if (hashType & bitcoin.Transaction.SIGHASH_ANYONECANPAY) hashTypes.push('SIGHASH_ANYONECANPAY') + const hashTypes = []; + if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_NONE) + hashTypes.push('SIGHASH_NONE'); + else if ((hashType & 0x1f) === bitcoin.Transaction.SIGHASH_SINGLE) + hashTypes.push('SIGHASH_SINGLE'); + else hashTypes.push('SIGHASH_ALL'); + if (hashType & bitcoin.Transaction.SIGHASH_ANYONECANPAY) + hashTypes.push('SIGHASH_ANYONECANPAY'); - const hashTypeName = hashTypes.join(' | ') + const hashTypeName = hashTypes.join(' | '); - it('should hash ' + txHex.slice(0, 40) + '... (' + hashTypeName + ')', () => { - const transaction = bitcoin.Transaction.fromHex(txHex) - assert.strictEqual(transaction.toHex(), txHex) + it( + 'should hash ' + txHex.slice(0, 40) + '... (' + hashTypeName + ')', + () => { + const transaction = bitcoin.Transaction.fromHex(txHex); + assert.strictEqual(transaction.toHex(), txHex); - const script = Buffer.from(scriptHex, 'hex') - const scriptChunks = bitcoin.script.decompile(script) - assert.strictEqual(bitcoin.script.compile(scriptChunks).toString('hex'), scriptHex) + const script = Buffer.from(scriptHex, 'hex'); + const scriptChunks = bitcoin.script.decompile(script); + assert.strictEqual( + bitcoin.script.compile(scriptChunks!).toString('hex'), + scriptHex, + ); - const hash = transaction.hashForSignature(inIndex, script, hashType) + const hash = transaction.hashForSignature(inIndex, script, hashType); - // reverse because test data is reversed - assert.strictEqual(hash.reverse().toString('hex'), expectedHash) - }) - }) - }) + // reverse because test data is reversed + assert.strictEqual( + (hash.reverse() as Buffer).toString('hex'), + expectedHash, + ); + }, + ); + }); + }); describe('script.signature.decode', () => { sigCanonical.forEach(hex => { - const buffer = Buffer.from(hex, 'hex') + const buffer = Buffer.from(hex, 'hex'); it('can parse ' + hex, () => { - const parsed = bitcoin.script.signature.decode(buffer) - const actual = bitcoin.script.signature.encode(parsed.signature, parsed.hashType) + const parsed = bitcoin.script.signature.decode(buffer); + const actual = bitcoin.script.signature.encode( + parsed.signature, + parsed.hashType, + ); - assert.strictEqual(actual.toString('hex'), hex) - }) - }) + assert.strictEqual(actual.toString('hex'), hex); + }); + }); sigNoncanonical.forEach((hex, i) => { - if (i === 0) return - if (i % 2 !== 0) return + if (i === 0) return; + if (i % 2 !== 0) return; - const description = sigNoncanonical[i - 1].slice(0, -1) - const buffer = Buffer.from(hex, 'hex') + const description = sigNoncanonical[i - 1].slice(0, -1); + const buffer = Buffer.from(hex, 'hex'); it('throws on ' + description, () => { assert.throws(() => { - bitcoin.script.signature.decode(buffer) - }, /Expected DER (integer|sequence)|(R|S) value (excessively padded|is negative)|(R|S|DER sequence) length is (zero|too short|too long|invalid)|Invalid hashType/) - }) - }) - }) -}) + bitcoin.script.signature.decode(buffer); + }, /Expected DER (integer|sequence)|(R|S) value (excessively padded|is negative)|(R|S|DER sequence) length is (zero|too short|too long|invalid)|Invalid hashType/); + }); + }); + }); +}); diff --git a/test/block.spec.ts b/test/block.spec.ts index d5e41af..6f8ed03 100644 --- a/test/block.spec.ts +++ b/test/block.spec.ts @@ -1,157 +1,172 @@ -const { describe, it, beforeEach } = require('mocha') -const assert = require('assert') -const Block = require('..').Block +import * as assert from 'assert'; +import { beforeEach, describe, it } from 'mocha'; +import { Block } from '..'; -const fixtures = require('./fixtures/block') +import * as fixtures from './fixtures/block.json'; describe('Block', () => { describe('version', () => { it('should be interpreted as an int32le', () => { - const blockHex = 'ffffffff0000000000000000000000000000000000000000000000000000000000000000414141414141414141414141414141414141414141414141414141414141414101000000020000000300000000' - const block = Block.fromHex(blockHex) - assert.strictEqual(-1, block.version) - assert.strictEqual(1, block.timestamp) - }) - }) + const blockHex = + 'ffffffff0000000000000000000000000000000000000000000000000000000000000000414141414141414141414141414141414141414141414141414141414141414101000000020000000300000000'; + const block = Block.fromHex(blockHex); + assert.strictEqual(-1, block.version); + assert.strictEqual(1, block.timestamp); + }); + }); describe('calculateTarget', () => { fixtures.targets.forEach(f => { it('returns ' + f.expected + ' for 0x' + f.bits, () => { - const bits = parseInt(f.bits, 16) + const bits = parseInt(f.bits, 16); - assert.strictEqual(Block.calculateTarget(bits).toString('hex'), f.expected) - }) - }) - }) + assert.strictEqual( + Block.calculateTarget(bits).toString('hex'), + f.expected, + ); + }); + }); + }); describe('fromBuffer/fromHex', () => { fixtures.valid.forEach(f => { it('imports ' + f.description, () => { - const block = Block.fromHex(f.hex) + const block = Block.fromHex(f.hex); - assert.strictEqual(block.version, f.version) - assert.strictEqual(block.prevHash.toString('hex'), f.prevHash) - assert.strictEqual(block.merkleRoot.toString('hex'), f.merkleRoot) + assert.strictEqual(block.version, f.version); + assert.strictEqual(block.prevHash!.toString('hex'), f.prevHash); + assert.strictEqual(block.merkleRoot!.toString('hex'), f.merkleRoot); if (block.witnessCommit) { - assert.strictEqual(block.witnessCommit.toString('hex'), f.witnessCommit) + assert.strictEqual( + block.witnessCommit.toString('hex'), + f.witnessCommit, + ); } - assert.strictEqual(block.timestamp, f.timestamp) - assert.strictEqual(block.bits, f.bits) - assert.strictEqual(block.nonce, f.nonce) - assert.strictEqual(!block.transactions, f.hex.length === 160) - }) - }) + assert.strictEqual(block.timestamp, f.timestamp); + assert.strictEqual(block.bits, f.bits); + assert.strictEqual(block.nonce, f.nonce); + assert.strictEqual(!block.transactions, f.hex.length === 160); + }); + }); fixtures.invalid.forEach(f => { it('throws on ' + f.exception, () => { assert.throws(() => { - Block.fromHex(f.hex) - }, new RegExp(f.exception)) - }) - }) - }) + Block.fromHex(f.hex); + }, new RegExp(f.exception)); + }); + }); + }); describe('toBuffer/toHex', () => { fixtures.valid.forEach(f => { - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('exports ' + f.description, () => { - assert.strictEqual(block.toHex(true), f.hex.slice(0, 160)) - assert.strictEqual(block.toHex(), f.hex) - }) - }) - }) + assert.strictEqual(block.toHex(true), f.hex.slice(0, 160)); + assert.strictEqual(block.toHex(), f.hex); + }); + }); + }); describe('getHash/getId', () => { fixtures.valid.forEach(f => { - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns ' + f.id + ' for ' + f.description, () => { - assert.strictEqual(block.getHash().toString('hex'), f.hash) - assert.strictEqual(block.getId(), f.id) - }) - }) - }) + assert.strictEqual(block.getHash().toString('hex'), f.hash); + assert.strictEqual(block.getId(), f.id); + }); + }); + }); describe('getUTCDate', () => { fixtures.valid.forEach(f => { - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns UTC date of ' + f.id, () => { - const utcDate = block.getUTCDate().getTime() + const utcDate = block.getUTCDate().getTime(); - assert.strictEqual(utcDate, f.timestamp * 1e3) - }) - }) - }) + assert.strictEqual(utcDate, f.timestamp * 1e3); + }); + }); + }); describe('calculateMerkleRoot', () => { it('should throw on zero-length transaction array', () => { assert.throws(() => { - Block.calculateMerkleRoot([]) - }, /Cannot compute merkle root for zero transactions/) - }) + Block.calculateMerkleRoot([]); + }, /Cannot compute merkle root for zero transactions/); + }); fixtures.valid.forEach(f => { - if (f.hex.length === 160) return + if (f.hex.length === 160) return; - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns ' + f.merkleRoot + ' for ' + f.id, () => { - assert.strictEqual(Block.calculateMerkleRoot(block.transactions).toString('hex'), f.merkleRoot) - }) + assert.strictEqual( + Block.calculateMerkleRoot(block.transactions!).toString('hex'), + f.merkleRoot, + ); + }); if (f.witnessCommit) { it('returns witness commit ' + f.witnessCommit + ' for ' + f.id, () => { - assert.strictEqual(Block.calculateMerkleRoot(block.transactions, true).toString('hex'), f.witnessCommit) - }) + assert.strictEqual( + Block.calculateMerkleRoot(block.transactions!, true).toString( + 'hex', + ), + f.witnessCommit, + ); + }); } - }) - }) + }); + }); describe('checkTxRoots', () => { fixtures.valid.forEach(f => { - if (f.hex.length === 160) return + if (f.hex.length === 160) return; - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns ' + f.valid + ' for ' + f.id, () => { - assert.strictEqual(block.checkTxRoots(), true) - }) - }) - }) + assert.strictEqual(block.checkTxRoots(), true); + }); + }); + }); describe('checkProofOfWork', () => { fixtures.valid.forEach(f => { - let block + let block: Block; beforeEach(() => { - block = Block.fromHex(f.hex) - }) + block = Block.fromHex(f.hex); + }); it('returns ' + f.valid + ' for ' + f.id, () => { - assert.strictEqual(block.checkProofOfWork(), f.valid) - }) - }) - }) -}) + assert.strictEqual(block.checkProofOfWork(), f.valid); + }); + }); + }); +}); diff --git a/test/bufferutils.spec.ts b/test/bufferutils.spec.ts index 1a38406..4308af9 100644 --- a/test/bufferutils.spec.ts +++ b/test/bufferutils.spec.ts @@ -1,49 +1,49 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bufferutils = require('../src/bufferutils') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bufferutils from '../src/bufferutils'; -const fixtures = require('./fixtures/bufferutils.json') +import * as fixtures from './fixtures/bufferutils.json'; describe('bufferutils', () => { describe('readUInt64LE', () => { fixtures.valid.forEach(f => { it('decodes ' + f.hex, () => { - const buffer = Buffer.from(f.hex, 'hex') - const number = bufferutils.readUInt64LE(buffer, 0) + const buffer = Buffer.from(f.hex, 'hex'); + const number = bufferutils.readUInt64LE(buffer, 0); - assert.strictEqual(number, f.dec) - }) - }) + assert.strictEqual(number, f.dec); + }); + }); fixtures.invalid.readUInt64LE.forEach(f => { it('throws on ' + f.description, () => { - const buffer = Buffer.from(f.hex, 'hex') + const buffer = Buffer.from(f.hex, 'hex'); assert.throws(() => { - bufferutils.readUInt64LE(buffer, 0) - }, new RegExp(f.exception)) - }) - }) - }) + bufferutils.readUInt64LE(buffer, 0); + }, new RegExp(f.exception)); + }); + }); + }); describe('writeUInt64LE', () => { fixtures.valid.forEach(f => { it('encodes ' + f.dec, () => { - const buffer = Buffer.alloc(8, 0) + const buffer = Buffer.alloc(8, 0); - bufferutils.writeUInt64LE(buffer, f.dec, 0) - assert.strictEqual(buffer.toString('hex'), f.hex) - }) - }) + bufferutils.writeUInt64LE(buffer, f.dec, 0); + assert.strictEqual(buffer.toString('hex'), f.hex); + }); + }); fixtures.invalid.readUInt64LE.forEach(f => { it('throws on ' + f.description, () => { - const buffer = Buffer.alloc(8, 0) + const buffer = Buffer.alloc(8, 0); assert.throws(() => { - bufferutils.writeUInt64LE(buffer, f.dec, 0) - }, new RegExp(f.exception)) - }) - }) - }) -}) + bufferutils.writeUInt64LE(buffer, f.dec, 0); + }, new RegExp(f.exception)); + }); + }); + }); +}); diff --git a/test/classify.spec.ts b/test/classify.spec.ts index 86da74d..b2464c0 100644 --- a/test/classify.spec.ts +++ b/test/classify.spec.ts @@ -1,18 +1,18 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bscript = require('../src/script') -const classify = require('../src/classify') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bscript from '../src/script'; +import * as classify from '../src/classify'; -const fixtures = require('./fixtures/templates.json') +import * as fixtures from './fixtures/templates.json'; -const multisig = require('../src/templates/multisig') -const nullData = require('../src/templates/nulldata') -const pubKey = require('../src/templates/pubkey') -const pubKeyHash = require('../src/templates/pubkeyhash') -const scriptHash = require('../src/templates/scripthash') -const witnessPubKeyHash = require('../src/templates/witnesspubkeyhash') -const witnessScriptHash = require('../src/templates/witnessscripthash') -const witnessCommitment = require('../src/templates/witnesscommitment') +import * as multisig from '../src/templates/multisig'; +import * as nullData from '../src/templates/nulldata'; +import * as pubKey from '../src/templates/pubkey'; +import * as pubKeyHash from '../src/templates/pubkeyhash'; +import * as scriptHash from '../src/templates/scripthash'; +import * as witnessPubKeyHash from '../src/templates/witnesspubkeyhash'; +import * as witnessScriptHash from '../src/templates/witnessscripthash'; +import * as witnessCommitment from '../src/templates/witnesscommitment'; const tmap = { pubKey, @@ -22,49 +22,48 @@ const tmap = { witnessScriptHash, multisig, nullData, - witnessCommitment -} + witnessCommitment, +}; describe('classify', () => { describe('input', () => { fixtures.valid.forEach(f => { - if (!f.input) return + if (!f.input) return; it('classifies ' + f.input + ' as ' + f.type, () => { - const input = bscript.fromASM(f.input) - const type = classify.input(input) + const input = bscript.fromASM(f.input); + const type = classify.input(input); - assert.strictEqual(type, f.type) - }) - }) + assert.strictEqual(type, f.type); + }); + }); fixtures.valid.forEach(f => { - if (!f.input) return - if (!f.typeIncomplete) return + if (!f.input) return; + if (!f.typeIncomplete) return; it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, () => { - const input = bscript.fromASM(f.input) - const type = classify.input(input, true) + const input = bscript.fromASM(f.input); + const type = classify.input(input, true); - assert.strictEqual(type, f.typeIncomplete) - }) - }) - }) + assert.strictEqual(type, f.typeIncomplete); + }); + }); + }); describe('classifyOutput', () => { fixtures.valid.forEach(f => { - if (!f.output) return + if (!f.output) return; it('classifies ' + f.output + ' as ' + f.type, () => { - const output = bscript.fromASM(f.output) - const type = classify.output(output) + const output = bscript.fromASM(f.output); + const type = classify.output(output); - assert.strictEqual(type, f.type) - }) - }) - }) - - ;[ + assert.strictEqual(type, f.type); + }); + }); + }); + [ 'pubKey', 'pubKeyHash', 'scriptHash', @@ -72,85 +71,110 @@ describe('classify', () => { 'witnessScriptHash', 'multisig', 'nullData', - 'witnessCommitment' + 'witnessCommitment', ].forEach(name => { - const inputType = tmap[name].input - const outputType = tmap[name].output + const inputType = (tmap as any)[name].input; + const outputType = (tmap as any)[name].output; describe(name + '.input.check', () => { fixtures.valid.forEach(f => { - if (name.toLowerCase() === classify.types.P2WPKH) return - if (name.toLowerCase() === classify.types.P2WSH) return - const expected = name.toLowerCase() === f.type.toLowerCase() + if (name.toLowerCase() === classify.types.P2WPKH) return; + if (name.toLowerCase() === classify.types.P2WSH) return; + const expected = name.toLowerCase() === f.type.toLowerCase(); if (inputType && f.input) { - const input = bscript.fromASM(f.input) + const input = bscript.fromASM(f.input); it('returns ' + expected + ' for ' + f.input, () => { - assert.strictEqual(inputType.check(input), expected) - }) + assert.strictEqual(inputType.check(input), expected); + }); if (f.typeIncomplete) { - const expectedIncomplete = name.toLowerCase() === f.typeIncomplete + const expectedIncomplete = name.toLowerCase() === f.typeIncomplete; it('returns ' + expected + ' for ' + f.input, () => { - assert.strictEqual(inputType.check(input, true), expectedIncomplete) - }) + assert.strictEqual( + inputType.check(input, true), + expectedIncomplete, + ); + }); } } - }) + }); - if (!(fixtures.invalid[name])) return + if (!(fixtures.invalid as any)[name]) return; - fixtures.invalid[name].inputs.forEach(f => { - if (!f.input && !f.inputHex) return + (fixtures.invalid as any)[name].inputs.forEach((f: any) => { + if (!f.input && !f.inputHex) return; - it('returns false for ' + f.description + ' (' + (f.input || f.inputHex) + ')', () => { - let input + it( + 'returns false for ' + + f.description + + ' (' + + (f.input || f.inputHex) + + ')', + () => { + let input; - if (f.input) { - input = bscript.fromASM(f.input) - } else { - input = Buffer.from(f.inputHex, 'hex') - } + if (f.input) { + input = bscript.fromASM(f.input); + } else { + input = Buffer.from(f.inputHex, 'hex'); + } - assert.strictEqual(inputType.check(input), false) - }) - }) - }) + assert.strictEqual(inputType.check(input), false); + }, + ); + }); + }); describe(name + '.output.check', () => { fixtures.valid.forEach(f => { - const expected = name.toLowerCase() === f.type + const expected = name.toLowerCase() === f.type; if (outputType && f.output) { it('returns ' + expected + ' for ' + f.output, () => { - const output = bscript.fromASM(f.output) + const output = bscript.fromASM(f.output); - if (name.toLowerCase() === 'nulldata' && f.type === classify.types.WITNESS_COMMITMENT) return - if (name.toLowerCase() === 'witnesscommitment' && f.type === classify.types.NULLDATA) return - assert.strictEqual(outputType.check(output), expected) - }) + if ( + name.toLowerCase() === 'nulldata' && + f.type === classify.types.WITNESS_COMMITMENT + ) + return; + if ( + name.toLowerCase() === 'witnesscommitment' && + f.type === classify.types.NULLDATA + ) + return; + assert.strictEqual(outputType.check(output), expected); + }); } - }) + }); - if (!(fixtures.invalid[name])) return + if (!(fixtures.invalid as any)[name]) return; - fixtures.invalid[name].outputs.forEach(f => { - if (!f.output && !f.outputHex) return + (fixtures.invalid as any)[name].outputs.forEach((f: any) => { + if (!f.output && !f.outputHex) return; - it('returns false for ' + f.description + ' (' + (f.output || f.outputHex) + ')', () => { - let output + it( + 'returns false for ' + + f.description + + ' (' + + (f.output || f.outputHex) + + ')', + () => { + let output; - if (f.output) { - output = bscript.fromASM(f.output) - } else { - output = Buffer.from(f.outputHex, 'hex') - } + if (f.output) { + output = bscript.fromASM(f.output); + } else { + output = Buffer.from(f.outputHex, 'hex'); + } - assert.strictEqual(outputType.check(output), false) - }) - }) - }) - }) -}) + assert.strictEqual(outputType.check(output), false); + }, + ); + }); + }); + }); +}); diff --git a/test/crypto.spec.ts b/test/crypto.spec.ts index 14b4f33..89ffabb 100644 --- a/test/crypto.spec.ts +++ b/test/crypto.spec.ts @@ -1,23 +1,22 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bcrypto = require('../src/crypto') - -const fixtures = require('./fixtures/crypto') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import { crypto as bcrypto } from '..'; +import * as fixtures from './fixtures/crypto.json'; describe('crypto', () => { ['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(algorithm => { describe(algorithm, () => { fixtures.forEach(f => { - const fn = bcrypto[algorithm] - const expected = f[algorithm] + const fn = (bcrypto as any)[algorithm]; + const expected = (f as any)[algorithm]; it('returns ' + expected + ' for ' + f.hex, () => { - const data = Buffer.from(f.hex, 'hex') - const actual = fn(data).toString('hex') + const data = Buffer.from(f.hex, 'hex'); + const actual = fn(data).toString('hex'); - assert.strictEqual(actual, expected) - }) - }) - }) - }) -}) + assert.strictEqual(actual, expected); + }); + }); + }); + }); +}); diff --git a/test/ecpair.spec.ts b/test/ecpair.spec.ts index e067ddd..b9b8538 100644 --- a/test/ecpair.spec.ts +++ b/test/ecpair.spec.ts @@ -1,284 +1,334 @@ -const { describe, it, beforeEach } = require('mocha') -const assert = require('assert') -const proxyquire = require('proxyquire') -const hoodwink = require('hoodwink') +import * as assert from 'assert'; +import { beforeEach, describe, it } from 'mocha'; +import * as proxyquire from 'proxyquire'; +import { ECPair, ECPairInterface, networks as NETWORKS } from '..'; +import * as fixtures from './fixtures/ecpair.json'; +const hoodwink = require('hoodwink'); +const tinysecp = require('tiny-secp256k1'); -const ECPair = require('../src/ecpair') -const tinysecp = require('tiny-secp256k1') - -const fixtures = require('./fixtures/ecpair.json') - -const NETWORKS = require('../src/networks') -const NETWORKS_LIST = [] // Object.values(NETWORKS) -for (let networkName in NETWORKS) { - NETWORKS_LIST.push(NETWORKS[networkName]) -} - -const ZERO = Buffer.alloc(32, 0) -const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') -const GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 'hex') -const GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 'hex') +const NETWORKS_LIST = Object.values(NETWORKS); +const ZERO = Buffer.alloc(32, 0); +const ONE = Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000001', + 'hex', +); +const GROUP_ORDER = Buffer.from( + 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', + 'hex', +); +const GROUP_ORDER_LESS_1 = Buffer.from( + 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', + 'hex', +); describe('ECPair', () => { describe('getPublicKey', () => { - let keyPair + let keyPair: ECPairInterface; beforeEach(() => { - keyPair = ECPair.fromPrivateKey(ONE) - }) + keyPair = ECPair.fromPrivateKey(ONE); + }); - it('calls pointFromScalar lazily', hoodwink(() => { - assert.strictEqual(keyPair.__Q, undefined) + it( + 'calls pointFromScalar lazily', + hoodwink(() => { + assert.strictEqual((keyPair as any).__Q, undefined); - // .publicKey forces the memoization - assert.strictEqual(keyPair.publicKey.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') - assert.strictEqual(keyPair.__Q.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798') - })) - }) + // .publicKey forces the memoization + assert.strictEqual( + keyPair.publicKey.toString('hex'), + '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', + ); + assert.strictEqual( + (keyPair as any).__Q.toString('hex'), + '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', + ); + }), + ); + }); describe('fromPrivateKey', () => { it('defaults to compressed', () => { - const keyPair = ECPair.fromPrivateKey(ONE) + const keyPair = ECPair.fromPrivateKey(ONE); - assert.strictEqual(keyPair.compressed, true) - }) + assert.strictEqual(keyPair.compressed, true); + }); it('supports the uncompressed option', () => { const keyPair = ECPair.fromPrivateKey(ONE, { - compressed: false - }) + compressed: false, + }); - assert.strictEqual(keyPair.compressed, false) - }) + assert.strictEqual(keyPair.compressed, false); + }); it('supports the network option', () => { const keyPair = ECPair.fromPrivateKey(ONE, { compressed: false, - network: NETWORKS.testnet - }) + network: NETWORKS.testnet, + }); - assert.strictEqual(keyPair.network, NETWORKS.testnet) - }) + assert.strictEqual(keyPair.network, NETWORKS.testnet); + }); fixtures.valid.forEach(f => { it('derives public key for ' + f.WIF, () => { - const d = Buffer.from(f.d, 'hex') + const d = Buffer.from(f.d, 'hex'); const keyPair = ECPair.fromPrivateKey(d, { - compressed: f.compressed - }) + compressed: f.compressed, + }); - assert.strictEqual(keyPair.publicKey.toString('hex'), f.Q) - }) - }) + assert.strictEqual(keyPair.publicKey.toString('hex'), f.Q); + }); + }); fixtures.invalid.fromPrivateKey.forEach(f => { it('throws ' + f.exception, () => { - const d = Buffer.from(f.d, 'hex') + const d = Buffer.from(f.d, 'hex'); assert.throws(() => { - ECPair.fromPrivateKey(d, f.options) - }, new RegExp(f.exception)) - }) - }) - }) + ECPair.fromPrivateKey(d, (f as any).options); + }, new RegExp(f.exception)); + }); + }); + }); describe('fromPublicKey', () => { fixtures.invalid.fromPublicKey.forEach(f => { it('throws ' + f.exception, () => { - const Q = Buffer.from(f.Q, 'hex') + const Q = Buffer.from(f.Q, 'hex'); assert.throws(() => { - ECPair.fromPublicKey(Q, f.options) - }, new RegExp(f.exception)) - }) - }) - }) + ECPair.fromPublicKey(Q, (f as any).options); + }, new RegExp(f.exception)); + }); + }); + }); describe('fromWIF', () => { fixtures.valid.forEach(f => { it('imports ' + f.WIF + ' (' + f.network + ')', () => { - const network = NETWORKS[f.network] - const keyPair = ECPair.fromWIF(f.WIF, network) + const network = (NETWORKS as any)[f.network]; + const keyPair = ECPair.fromWIF(f.WIF, network); - assert.strictEqual(keyPair.privateKey.toString('hex'), f.d) - assert.strictEqual(keyPair.compressed, f.compressed) - assert.strictEqual(keyPair.network, network) - }) - }) + assert.strictEqual(keyPair.privateKey!.toString('hex'), f.d); + assert.strictEqual(keyPair.compressed, f.compressed); + assert.strictEqual(keyPair.network, network); + }); + }); fixtures.valid.forEach(f => { it('imports ' + f.WIF + ' (via list of networks)', () => { - const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST); - assert.strictEqual(keyPair.privateKey.toString('hex'), f.d) - assert.strictEqual(keyPair.compressed, f.compressed) - assert.strictEqual(keyPair.network, NETWORKS[f.network]) - }) - }) + assert.strictEqual(keyPair.privateKey!.toString('hex'), f.d); + assert.strictEqual(keyPair.compressed, f.compressed); + assert.strictEqual(keyPair.network, (NETWORKS as any)[f.network]); + }); + }); fixtures.invalid.fromWIF.forEach(f => { it('throws on ' + f.WIF, () => { assert.throws(() => { - const networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST + const networks = f.network + ? (NETWORKS as any)[f.network] + : NETWORKS_LIST; - ECPair.fromWIF(f.WIF, networks) - }, new RegExp(f.exception)) - }) - }) - }) + ECPair.fromWIF(f.WIF, networks); + }, new RegExp(f.exception)); + }); + }); + }); describe('toWIF', () => { fixtures.valid.forEach(f => { it('exports ' + f.WIF, () => { - const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) - const result = keyPair.toWIF() - assert.strictEqual(result, f.WIF) - }) - }) - }) + const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST); + const result = keyPair.toWIF(); + assert.strictEqual(result, f.WIF); + }); + }); + }); describe('makeRandom', () => { - const d = Buffer.alloc(32, 4) - const exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' + const d = Buffer.alloc(32, 4); + const exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv'; describe('uses randombytes RNG', () => { it('generates a ECPair', () => { - const stub = { randombytes: () => { return d } } - const ProxiedECPair = proxyquire('../src/ecpair', stub) + const stub = { + randombytes: (): Buffer => { + return d; + }, + }; + const ProxiedECPair = proxyquire('../src/ecpair', stub); - const keyPair = ProxiedECPair.makeRandom() - assert.strictEqual(keyPair.toWIF(), exWIF) - }) - }) + const keyPair = ProxiedECPair.makeRandom(); + assert.strictEqual(keyPair.toWIF(), exWIF); + }); + }); it('allows a custom RNG to be used', () => { const keyPair = ECPair.makeRandom({ - rng: size => { return d.slice(0, size) } - }) + rng: (size): Buffer => { + return d.slice(0, size); + }, + }); - assert.strictEqual(keyPair.toWIF(), exWIF) - }) + assert.strictEqual(keyPair.toWIF(), exWIF); + }); it('retains the same defaults as ECPair constructor', () => { - const keyPair = ECPair.makeRandom() + const keyPair = ECPair.makeRandom(); - assert.strictEqual(keyPair.compressed, true) - assert.strictEqual(keyPair.network, NETWORKS.bitcoin) - }) + assert.strictEqual(keyPair.compressed, true); + assert.strictEqual(keyPair.network, NETWORKS.bitcoin); + }); it('supports the options parameter', () => { const keyPair = ECPair.makeRandom({ compressed: false, - network: NETWORKS.testnet - }) + network: NETWORKS.testnet, + }); - assert.strictEqual(keyPair.compressed, false) - assert.strictEqual(keyPair.network, NETWORKS.testnet) - }) + assert.strictEqual(keyPair.compressed, false); + assert.strictEqual(keyPair.network, NETWORKS.testnet); + }); it('throws if d is bad length', () => { - function rng () { - return Buffer.alloc(28) + function rng(): Buffer { + return Buffer.alloc(28); } assert.throws(() => { - ECPair.makeRandom({ rng: rng }) - }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/) - }) + ECPair.makeRandom({ rng }); + }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/); + }); - it('loops until d is within interval [1, n) : 1', hoodwink(function () { - const rng = this.stub(() => { - if (rng.calls === 0) return ZERO // 0 - return ONE // >0 - }, 2) + it( + 'loops until d is within interval [1, n) : 1', + hoodwink(function(this: any): void { + const rng = this.stub(() => { + if (rng.calls === 0) return ZERO; // 0 + return ONE; // >0 + }, 2); - ECPair.makeRandom({ rng: rng }) - })) + ECPair.makeRandom({ rng }); + }), + ); - it('loops until d is within interval [1, n) : n - 1', hoodwink(function () { - const rng = this.stub(() => { - if (rng.calls === 0) return ZERO // <1 - if (rng.calls === 1) return GROUP_ORDER // >n-1 - return GROUP_ORDER_LESS_1 // n-1 - }, 3) + it( + 'loops until d is within interval [1, n) : n - 1', + hoodwink(function(this: any): void { + const rng = this.stub(() => { + if (rng.calls === 0) return ZERO; // <1 + if (rng.calls === 1) return GROUP_ORDER; // >n-1 + return GROUP_ORDER_LESS_1; // n-1 + }, 3); - ECPair.makeRandom({ rng: rng }) - })) - }) + ECPair.makeRandom({ rng }); + }), + ); + }); describe('.network', () => { fixtures.valid.forEach(f => { it('returns ' + f.network + ' for ' + f.WIF, () => { - const network = NETWORKS[f.network] - const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) + const network = (NETWORKS as any)[f.network]; + const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST); - assert.strictEqual(keyPair.network, network) - }) - }) - }) + assert.strictEqual(keyPair.network, network); + }); + }); + }); describe('tinysecp wrappers', () => { - let keyPair - let hash - let signature + let keyPair: ECPairInterface; + let hash: Buffer; + let signature: Buffer; beforeEach(() => { - keyPair = ECPair.makeRandom() - hash = ZERO - signature = Buffer.alloc(64, 1) - }) + keyPair = ECPair.makeRandom(); + hash = ZERO; + signature = Buffer.alloc(64, 1); + }); describe('signing', () => { - it('wraps tinysecp.sign', hoodwink(function () { - this.mock(tinysecp, 'sign', (h, d) => { - assert.strictEqual(h, hash) - assert.strictEqual(d, keyPair.privateKey) - return signature - }, 1) + it( + 'wraps tinysecp.sign', + hoodwink(function(this: any): void { + this.mock( + tinysecp, + 'sign', + (h: any, d: any) => { + assert.strictEqual(h, hash); + assert.strictEqual(d, keyPair.privateKey); + return signature; + }, + 1, + ); - assert.strictEqual(keyPair.sign(hash), signature) - })) + assert.strictEqual(keyPair.sign(hash), signature); + }), + ); it('throws if no private key is found', () => { - delete keyPair.__D + delete (keyPair as any).__D; assert.throws(() => { - keyPair.sign(hash) - }, /Missing private key/) - }) - }) + keyPair.sign(hash); + }, /Missing private key/); + }); + }); describe('verify', () => { - it('wraps tinysecp.verify', hoodwink(function () { - this.mock(tinysecp, 'verify', (h, q, s) => { - assert.strictEqual(h, hash) - assert.strictEqual(q, keyPair.publicKey) - assert.strictEqual(s, signature) - return true - }, 1) + it( + 'wraps tinysecp.verify', + hoodwink(function(this: any): void { + this.mock( + tinysecp, + 'verify', + (h: any, q: any, s: any) => { + assert.strictEqual(h, hash); + assert.strictEqual(q, keyPair.publicKey); + assert.strictEqual(s, signature); + return true; + }, + 1, + ); - assert.strictEqual(keyPair.verify(hash, signature), true) - })) - }) - }) + assert.strictEqual(keyPair.verify(hash, signature), true); + }), + ); + }); + }); describe('optional low R signing', () => { - const sig = Buffer.from('95a6619140fca3366f1d3b013b0367c4f86e39508a50fdce' + - 'e5245fbb8bd60aa6086449e28cf15387cf9f85100bfd0838624ca96759e59f65c10a00' + - '16b86f5229', 'hex') - const sigLowR = Buffer.from('6a2660c226e8055afad317eeba918a304be79208d505' + - '3bc5ea4a5e4c5892b4a061c717c5284ae5202d721c0e49b4717b79966280906b1d3b52' + - '95d1fdde963c35', 'hex') - const lowRKeyPair = ECPair.fromWIF('L3nThUzbAwpUiBAjR5zCu66ybXSPMr2zZ3ikp' + - 'ScpTPiYTxBynfZu') - const dataToSign = Buffer.from('b6c5c548a7f6164c8aa7af5350901626ebd69f9ae' + - '2c1ecf8871f5088ec204cfe', 'hex') + const sig = Buffer.from( + '95a6619140fca3366f1d3b013b0367c4f86e39508a50fdce' + + 'e5245fbb8bd60aa6086449e28cf15387cf9f85100bfd0838624ca96759e59f65c10a00' + + '16b86f5229', + 'hex', + ); + const sigLowR = Buffer.from( + '6a2660c226e8055afad317eeba918a304be79208d505' + + '3bc5ea4a5e4c5892b4a061c717c5284ae5202d721c0e49b4717b79966280906b1d3b52' + + '95d1fdde963c35', + 'hex', + ); + const lowRKeyPair = ECPair.fromWIF( + 'L3nThUzbAwpUiBAjR5zCu66ybXSPMr2zZ3ikp' + 'ScpTPiYTxBynfZu', + ); + const dataToSign = Buffer.from( + 'b6c5c548a7f6164c8aa7af5350901626ebd69f9ae' + '2c1ecf8871f5088ec204cfe', + 'hex', + ); it('signs with normal R by default', () => { - const signed = lowRKeyPair.sign(dataToSign) - assert.deepStrictEqual(sig, signed) - }) + const signed = lowRKeyPair.sign(dataToSign); + assert.deepStrictEqual(sig, signed); + }); it('signs with low R when true is passed', () => { - const signed = lowRKeyPair.sign(dataToSign, true) - assert.deepStrictEqual(sigLowR, signed) - }) - }) -}) + const signed = lowRKeyPair.sign(dataToSign, true); + assert.deepStrictEqual(sigLowR, signed); + }); + }); +}); diff --git a/test/integration/_regtest.spec.ts b/test/integration/_regtest.spec.ts deleted file mode 100644 index c50ef2f..0000000 --- a/test/integration/_regtest.spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -const { RegtestUtils } = require('regtest-client') - -const APIPASS = process.env.APIPASS || 'satoshi' -const APIURL = process.env.APIURL || 'https://regtest.bitbank.cc/1' - -const regtestUtils = new RegtestUtils({ APIPASS, APIURL }) - -module.exports = regtestUtils; diff --git a/test/integration/_regtest.ts b/test/integration/_regtest.ts new file mode 100644 index 0000000..52a6be8 --- /dev/null +++ b/test/integration/_regtest.ts @@ -0,0 +1,6 @@ +import { RegtestUtils } from 'regtest-client'; + +const APIPASS = process.env.APIPASS || 'satoshi'; +const APIURL = process.env.APIURL || 'https://regtest.bitbank.cc/1'; + +export const regtestUtils = new RegtestUtils({ APIPASS, APIURL }); diff --git a/test/integration/addresses.spec.ts b/test/integration/addresses.spec.ts index 1d28020..49dc578 100644 --- a/test/integration/addresses.spec.ts +++ b/test/integration/addresses.spec.ts @@ -1,117 +1,137 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const dhttp = require('./_regtest').dhttp -const TESTNET = bitcoin.networks.testnet +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const dhttp = regtestUtils.dhttp; +const TESTNET = bitcoin.networks.testnet; describe('bitcoinjs-lib (addresses)', () => { it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', async () => { - const keyPair = bitcoin.ECPair.makeRandom() - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) + const keyPair = bitcoin.ECPair.makeRandom(); + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); // bitcoin P2PKH addresses start with a '1' - assert.strictEqual(address.startsWith('1'), true) + assert.strictEqual(address!.startsWith('1'), true); const result = await dhttp({ method: 'GET', - url: 'https://blockchain.info/rawaddr/' + address - }) + url: 'https://blockchain.info/rawaddr/' + address, + }); // random private keys [probably!] have no transactions - assert.strictEqual(result.n_tx, 0) - assert.strictEqual(result.total_received, 0) - assert.strictEqual(result.total_sent, 0) - }) + assert.strictEqual((result as any).n_tx, 0); + assert.strictEqual((result as any).total_received, 0); + assert.strictEqual((result as any).total_sent, 0); + }); it('can import an address via WIF', () => { - const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }) + const keyPair = bitcoin.ECPair.fromWIF( + 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', + ); + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); - assert.strictEqual(address, '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH') - }) + assert.strictEqual(address, '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH'); + }); it('can generate a P2SH, pay-to-multisig (2-of-3) address', () => { const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', - '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9' - ].map((hex) => Buffer.from(hex, 'hex')) + '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9', + ].map(hex => Buffer.from(hex, 'hex')); const { address } = bitcoin.payments.p2sh({ - redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }) - }) + redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }), + }); - assert.strictEqual(address, '36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7') - }) + assert.strictEqual(address, '36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7'); + }); it('can generate a SegWit address', () => { - const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') - const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) + const keyPair = bitcoin.ECPair.fromWIF( + 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', + ); + const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }); - assert.strictEqual(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4') - }) + assert.strictEqual(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'); + }); it('can generate a SegWit address (via P2SH)', () => { - const keyPair = bitcoin.ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn') + const keyPair = bitcoin.ECPair.fromWIF( + 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', + ); const { address } = bitcoin.payments.p2sh({ - redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }) - }) + redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }), + }); - assert.strictEqual(address, '3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN') - }) + assert.strictEqual(address, '3JvL6Ymt8MVWiCNHC7oWU6nLeHNJKLZGLN'); + }); it('can generate a P2WSH (SegWit), pay-to-multisig (3-of-4) address', () => { const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', '023e4740d0ba639e28963f3476157b7cf2fb7c6fdf4254f97099cf8670b505ea59', - '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9' - ].map((hex) => Buffer.from(hex, 'hex')) + '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9', + ].map(hex => Buffer.from(hex, 'hex')); const { address } = bitcoin.payments.p2wsh({ - redeem: bitcoin.payments.p2ms({ m: 3, pubkeys }) - }) + redeem: bitcoin.payments.p2ms({ m: 3, pubkeys }), + }); - assert.strictEqual(address, 'bc1q75f6dv4q8ug7zhujrsp5t0hzf33lllnr3fe7e2pra3v24mzl8rrqtp3qul') - }) + assert.strictEqual( + address, + 'bc1q75f6dv4q8ug7zhujrsp5t0hzf33lllnr3fe7e2pra3v24mzl8rrqtp3qul', + ); + }); it('can generate a P2SH(P2WSH(...)), pay-to-multisig (2-of-2) address', () => { const pubkeys = [ '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', - '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9' - ].map((hex) => Buffer.from(hex, 'hex')) + '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', + ].map(hex => Buffer.from(hex, 'hex')); const { address } = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wsh({ - redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }) - }) - }) + redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }), + }), + }); - assert.strictEqual(address, '3P4mrxQfmExfhxqjLnR2Ah4WES5EB1KBrN') - }) + assert.strictEqual(address, '3P4mrxQfmExfhxqjLnR2Ah4WES5EB1KBrN'); + }); // examples using other network information it('can generate a Testnet address', () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET }) - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: TESTNET }) + const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET }); + const { address } = bitcoin.payments.p2pkh({ + pubkey: keyPair.publicKey, + network: TESTNET, + }); // bitcoin testnet P2PKH addresses start with a 'm' or 'n' - assert.strictEqual(address.startsWith('m') || address.startsWith('n'), true) - }) + assert.strictEqual( + address!.startsWith('m') || address!.startsWith('n'), + true, + ); + }); it('can generate a Litecoin address', () => { // WARNING: although possible, bitcoinjs is NOT necessarily compatible with Litecoin const LITECOIN = { messagePrefix: '\x19Litecoin Signed Message:\n', + bech32: 'ltc', bip32: { public: 0x019da462, - private: 0x019d9cfe + private: 0x019d9cfe, }, pubKeyHash: 0x30, scriptHash: 0x32, - wif: 0xb0 - } + wif: 0xb0, + }; - const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }) - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: LITECOIN }) + const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }); + const { address } = bitcoin.payments.p2pkh({ + pubkey: keyPair.publicKey, + network: LITECOIN, + }); - assert.strictEqual(address.startsWith('L'), true) - }) -}) + assert.strictEqual(address!.startsWith('L'), true); + }); +}); diff --git a/test/integration/bip32.spec.ts b/test/integration/bip32.spec.ts index 255669c..1279d78 100644 --- a/test/integration/bip32.spec.ts +++ b/test/integration/bip32.spec.ts @@ -1,101 +1,151 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bip32 = require('bip32') -const bip39 = require('bip39') -const bitcoin = require('../../') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bip32 from 'bip32'; +import * as bip39 from 'bip39'; +import * as bitcoin from '../..'; -function getAddress (node, network) { - return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address +function getAddress(node: any, network?: any): string { + return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address!; } describe('bitcoinjs-lib (BIP32)', () => { it('can import a BIP32 testnet xpriv and export to WIF', () => { - const xpriv = 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK' - const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet) + const xpriv = + 'tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK'; + const node = bip32.fromBase58(xpriv, bitcoin.networks.testnet); - assert.strictEqual(node.toWIF(), 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7') - }) + assert.strictEqual( + node.toWIF(), + 'cQfoY67cetFNunmBUX5wJiw3VNoYx3gG9U9CAofKE6BfiV1fSRw7', + ); + }); it('can export a BIP32 xpriv, then import it', () => { - const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' - const seed = bip39.mnemonicToSeed(mnemonic) - const node = bip32.fromSeed(seed) - const string = node.toBase58() - const restored = bip32.fromBase58(string) + const mnemonic = + 'praise you muffin lion enable neck grocery crumble super myself license ghost'; + const seed = bip39.mnemonicToSeedSync(mnemonic); + const node = bip32.fromSeed(seed); + const string = node.toBase58(); + const restored = bip32.fromBase58(string); - assert.strictEqual(getAddress(node), getAddress(restored)) // same public key - assert.strictEqual(node.toWIF(), restored.toWIF()) // same private key - }) + assert.strictEqual(getAddress(node), getAddress(restored)); // same public key + assert.strictEqual(node.toWIF(), restored.toWIF()); // same private key + }); it('can export a BIP32 xpub', () => { - const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' - const seed = bip39.mnemonicToSeed(mnemonic) - const node = bip32.fromSeed(seed) - const string = node.neutered().toBase58() + const mnemonic = + 'praise you muffin lion enable neck grocery crumble super myself license ghost'; + const seed = bip39.mnemonicToSeedSync(mnemonic); + const node = bip32.fromSeed(seed); + const string = node.neutered().toBase58(); - assert.strictEqual(string, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n') - }) + assert.strictEqual( + string, + 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n', + ); + }); it('can create a BIP32, bitcoin, account 0, external address', () => { - const path = "m/0'/0/0" - const root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) + const path = "m/0'/0/0"; + const root = bip32.fromSeed( + Buffer.from( + 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', + 'hex', + ), + ); - const child1 = root.derivePath(path) + const child1 = root.derivePath(path); // option 2, manually - const child1b = root.deriveHardened(0) - .derive(0) + const child1b = root + .deriveHardened(0) .derive(0) + .derive(0); - assert.strictEqual(getAddress(child1), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') - assert.strictEqual(getAddress(child1b), '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7') - }) + assert.strictEqual( + getAddress(child1), + '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7', + ); + assert.strictEqual( + getAddress(child1b), + '1JHyB1oPXufr4FXkfitsjgNB5yRY9jAaa7', + ); + }); it('can create a BIP44, bitcoin, account 0, external address', () => { - const root = bip32.fromSeed(Buffer.from('dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', 'hex')) + const root = bip32.fromSeed( + Buffer.from( + 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', + 'hex', + ), + ); - const child1 = root.derivePath("m/44'/0'/0'/0/0") + const child1 = root.derivePath("m/44'/0'/0'/0/0"); // option 2, manually - const child1b = root.deriveHardened(44) + const child1b = root + .deriveHardened(44) .deriveHardened(0) .deriveHardened(0) .derive(0) - .derive(0) + .derive(0); - assert.strictEqual(getAddress(child1), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') - assert.strictEqual(getAddress(child1b), '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au') - }) + assert.strictEqual( + getAddress(child1), + '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au', + ); + assert.strictEqual( + getAddress(child1b), + '12Tyvr1U8A3ped6zwMEU5M8cx3G38sP5Au', + ); + }); it('can create a BIP49, bitcoin testnet, account 0, external address', () => { - const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about' - const seed = bip39.mnemonicToSeed(mnemonic) - const root = bip32.fromSeed(seed) + const mnemonic = + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; + const seed = bip39.mnemonicToSeedSync(mnemonic); + const root = bip32.fromSeed(seed); - const path = "m/49'/1'/0'/0/0" - const child = root.derivePath(path) + const path = "m/49'/1'/0'/0/0"; + const child = root.derivePath(path); const { address } = bitcoin.payments.p2sh({ - redeem: bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network: bitcoin.networks.testnet }), - network: bitcoin.networks.testnet - }) - assert.strictEqual(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2') - }) + redeem: bitcoin.payments.p2wpkh({ + pubkey: child.publicKey, + network: bitcoin.networks.testnet, + }), + network: bitcoin.networks.testnet, + }); + assert.strictEqual(address, '2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2'); + }); it('can use BIP39 to generate BIP32 addresses', () => { // var mnemonic = bip39.generateMnemonic() - const mnemonic = 'praise you muffin lion enable neck grocery crumble super myself license ghost' - assert(bip39.validateMnemonic(mnemonic)) + const mnemonic = + 'praise you muffin lion enable neck grocery crumble super myself license ghost'; + assert(bip39.validateMnemonic(mnemonic)); - const seed = bip39.mnemonicToSeed(mnemonic) - const root = bip32.fromSeed(seed) + const seed = bip39.mnemonicToSeedSync(mnemonic); + const root = bip32.fromSeed(seed); // receive addresses - assert.strictEqual(getAddress(root.derivePath("m/0'/0/0")), '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC') - assert.strictEqual(getAddress(root.derivePath("m/0'/0/1")), '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL') + assert.strictEqual( + getAddress(root.derivePath("m/0'/0/0")), + '1AVQHbGuES57wD68AJi7Gcobc3RZrfYWTC', + ); + assert.strictEqual( + getAddress(root.derivePath("m/0'/0/1")), + '1Ad6nsmqDzbQo5a822C9bkvAfrYv9mc1JL', + ); // change addresses - assert.strictEqual(getAddress(root.derivePath("m/0'/1/0")), '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn') - assert.strictEqual(getAddress(root.derivePath("m/0'/1/1")), '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6') - }) -}) + assert.strictEqual( + getAddress(root.derivePath("m/0'/1/0")), + '1349KVc5NgedaK7DvuD4xDFxL86QN1Hvdn', + ); + assert.strictEqual( + getAddress(root.derivePath("m/0'/1/1")), + '1EAvj4edpsWcSer3duybAd4KiR4bCJW5J6', + ); + }); +}); diff --git a/test/integration/blocks.spec.ts b/test/integration/blocks.spec.ts index 044862d..5eed0fc 100644 --- a/test/integration/blocks.spec.ts +++ b/test/integration/blocks.spec.ts @@ -1,22 +1,21 @@ -'use strict' - -const { describe, it } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; describe('bitcoinjs-lib (blocks)', () => { it('can extract a height from a CoinBase transaction', () => { // from 00000000000000000097669cdca131f24d40c4cc7d80eaa65967a2d09acf6ce6 - const txHex = '010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98fd16761d220400000000000000aa340000d49f0000ffffffff02b07fc366000000001976a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12bf1e400120000000000000000000000000000000000000000000000000000000000000000000000000' - const tx = bitcoin.Transaction.fromHex(txHex) + const txHex = + '010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98fd16761d220400000000000000aa340000d49f0000ffffffff02b07fc366000000001976a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12bf1e400120000000000000000000000000000000000000000000000000000000000000000000000000'; + const tx = bitcoin.Transaction.fromHex(txHex); - assert.strictEqual(tx.ins.length, 1) - const script = tx.ins[0].script + assert.strictEqual(tx.ins.length, 1); + const script = tx.ins[0].script; // bitcoin.script.decompile(script) // returns [] :( - assert.strictEqual(script[0], 0x03) - const heightBuffer = script.slice(1, 4) - const height = bitcoin.script.number.decode(heightBuffer) - assert.strictEqual(height, 498303) - }) -}) + assert.strictEqual(script[0], 0x03); + const heightBuffer = script.slice(1, 4); + const height = bitcoin.script.number.decode(heightBuffer); + assert.strictEqual(height, 498303); + }); +}); diff --git a/test/integration/cltv.spec.ts b/test/integration/cltv.spec.ts index e123652..afdcaa5 100644 --- a/test/integration/cltv.spec.ts +++ b/test/integration/cltv.spec.ts @@ -1,198 +1,219 @@ -const { describe, it, before } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const regtestUtils = require('./_regtest') -const regtest = regtestUtils.network -const bip65 = require('bip65') +import * as assert from 'assert'; +import { before, describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const regtest = regtestUtils.network; +const bip65 = require('bip65'); -const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) -const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) -console.warn = () => {} // Silence the Deprecation Warning +const alice = bitcoin.ECPair.fromWIF( + 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', + regtest, +); +const bob = bitcoin.ECPair.fromWIF( + 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', + regtest, +); +console.warn = () => {}; // Silence the Deprecation Warning describe('bitcoinjs-lib (transactions w/ CLTV)', () => { // force update MTP before(async () => { - await regtestUtils.mine(11) - }) + await regtestUtils.mine(11); + }); - const hashType = bitcoin.Transaction.SIGHASH_ALL + const hashType = bitcoin.Transaction.SIGHASH_ALL; - function cltvCheckSigOutput (aQ, bQ, lockTime) { - return bitcoin.script.compile([ - bitcoin.opcodes.OP_IF, - bitcoin.script.number.encode(lockTime), - bitcoin.opcodes.OP_CHECKLOCKTIMEVERIFY, - bitcoin.opcodes.OP_DROP, - - bitcoin.opcodes.OP_ELSE, - bQ.publicKey, - bitcoin.opcodes.OP_CHECKSIGVERIFY, - bitcoin.opcodes.OP_ENDIF, - - aQ.publicKey, - bitcoin.opcodes.OP_CHECKSIG - ]) + type keyPair = { publicKey: Buffer }; + function cltvCheckSigOutput(aQ: keyPair, bQ: keyPair, lockTime: number) { + return bitcoin.script.fromASM( + ` + OP_IF + ${bitcoin.script.number.encode(lockTime).toString('hex')} + OP_CHECKLOCKTIMEVERIFY + OP_DROP + OP_ELSE + ${bQ.publicKey.toString('hex')} + OP_CHECKSIGVERIFY + OP_ENDIF + ${aQ.publicKey.toString('hex')} + OP_CHECKSIG + ` + .trim() + .replace(/\s+/g, ' '), + ); } - function utcNow () { - return Math.floor(Date.now() / 1000) + function utcNow() { + return Math.floor(Date.now() / 1000); } // expiry past, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', async () => { // 3 hours ago - const lockTime = bip65.encode({ utc: utcNow() - (3600 * 3) }) - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) + const lockTime = bip65.encode({ utc: utcNow() - 3600 * 3 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address, 1e5) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) + const unspent = await regtestUtils.faucet(address!, 1e5); + const txb = new bitcoin.TransactionBuilder(regtest); + txb.setLockTime(lockTime); // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE + bitcoin.opcodes.OP_TRUE, ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // expiry will pass, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async () => { - const height = await regtestUtils.height() + const height = await regtestUtils.height(); // 5 blocks from now - const lockTime = bip65.encode({ blocks: height + 5 }) - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) + const lockTime = bip65.encode({ blocks: height + 5 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address, 1e5) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) + const unspent = await regtestUtils.faucet(address!, 1e5); + const txb = new bitcoin.TransactionBuilder(regtest); + txb.setLockTime(lockTime); // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE + bitcoin.opcodes.OP_TRUE, ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently // ... // into the future! - await regtestUtils.mine(5) - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.mine(5); + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', async () => { // two hours ago - const lockTime = bip65.encode({ utc: utcNow() - (3600 * 2) }) - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) + const lockTime = bip65.encode({ utc: utcNow() - 3600 * 2 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address, 2e5) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) + const unspent = await regtestUtils.faucet(address!, 2e5); + const txb = new bitcoin.TransactionBuilder(regtest); + txb.setLockTime(lockTime); // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4) + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4); // {Alice's signature} {Bob's signature} OP_FALSE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_FALSE + bitcoin.opcodes.OP_FALSE, ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 8e4 - }) - }) + value: 8e4, + }); + }); // expiry in the future, {Alice's signature} OP_TRUE it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async () => { // two hours from now - const lockTime = bip65.encode({ utc: utcNow() + (3600 * 2) }) - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime) - const { address } = bitcoin.payments.p2sh({ redeem: { output: redeemScript, network: regtest }, network: regtest }) + const lockTime = bip65.encode({ utc: utcNow() + 3600 * 2 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address, 2e4) - const txb = new bitcoin.TransactionBuilder(regtest) - txb.setLockTime(lockTime) + const unspent = await regtestUtils.faucet(address!, 2e4); + const txb = new bitcoin.TransactionBuilder(regtest); + txb.setLockTime(lockTime); // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, redeemScript, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE + bitcoin.opcodes.OP_TRUE, ]), - output: redeemScript - } - }).input - tx.setInputScript(0, redeemScriptSig) + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); await regtestUtils.broadcast(tx.toHex()).catch(err => { assert.throws(() => { - if (err) throw err - }, /Error: non-final \(code 64\)/) - }) - }) -}) + if (err) throw err; + }, /Error: non-final \(code 64\)/); + }); + }); +}); diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts index 2e133f0..0bfb970 100644 --- a/test/integration/csv.spec.ts +++ b/test/integration/csv.spec.ts @@ -1,27 +1,41 @@ -const { describe, it, before } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const regtestUtils = require('./_regtest') -const regtest = regtestUtils.network -const bip68 = require('bip68') +import * as assert from 'assert'; +import { before, describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const regtest = regtestUtils.network; +const bip68 = require('bip68'); -const alice = bitcoin.ECPair.fromWIF('cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest) -const bob = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest) -const charles = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf', regtest) -const dave = bitcoin.ECPair.fromWIF('cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', regtest) -console.warn = () => {} // Silence the Deprecation Warning +const alice = bitcoin.ECPair.fromWIF( + 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', + regtest, +); +const bob = bitcoin.ECPair.fromWIF( + 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', + regtest, +); +const charles = bitcoin.ECPair.fromWIF( + 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf', + regtest, +); +const dave = bitcoin.ECPair.fromWIF( + 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', + regtest, +); +console.warn = () => {}; // Silence the Deprecation Warning describe('bitcoinjs-lib (transactions w/ CSV)', () => { // force update MTP before(async () => { - await regtestUtils.mine(11) - }) + await regtestUtils.mine(11); + }); - const hashType = bitcoin.Transaction.SIGHASH_ALL + const hashType = bitcoin.Transaction.SIGHASH_ALL; + type keyPair = { publicKey: Buffer }; // IF MTP (from when confirmed) > seconds, _alice can redeem - function csvCheckSigOutput (_alice, _bob, sequence) { - return bitcoin.script.fromASM(` + function csvCheckSigOutput(_alice: keyPair, _bob: keyPair, sequence: number) { + return bitcoin.script.fromASM( + ` OP_IF ${bitcoin.script.number.encode(sequence).toString('hex')} OP_CHECKSEQUENCEVERIFY @@ -32,7 +46,10 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { OP_ENDIF ${_alice.publicKey.toString('hex')} OP_CHECKSIG - `.trim().replace(/\s+/g, ' ')) + ` + .trim() + .replace(/\s+/g, ' '), + ); } // 2 of 3 multisig of _bob, _charles, _dave, @@ -41,8 +58,16 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example // Note: bitcoinjs-lib will not offer specific support for problems with // advanced script usages such as below. Use at your own risk. - function complexCsvOutput (_alice, _bob, _charles, _dave, sequence1, sequence2) { - return bitcoin.script.fromASM(` + function complexCsvOutput( + _alice: keyPair, + _bob: keyPair, + _charles: keyPair, + _dave: keyPair, + sequence1: number, + sequence2: number, + ) { + return bitcoin.script.fromASM( + ` OP_IF OP_IF OP_2 @@ -66,253 +91,294 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { ${_alice.publicKey.toString('hex')} OP_CHECKSIG OP_ENDIF - `.trim().replace(/\s+/g, ' ')) + ` + .trim() + .replace(/\s+/g, ' '), + ); } // expiry will pass, {Alice's signature} OP_TRUE it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)', async () => { // 5 blocks from now - const sequence = bip68.encode({ blocks: 5 }) + const sequence = bip68.encode({ blocks: 5 }); const p2sh = bitcoin.payments.p2sh({ redeem: { - output: csvCheckSigOutput(alice, bob, sequence) + output: csvCheckSigOutput(alice, bob, sequence), }, - network: regtest - }) + network: regtest, + }); // fund the P2SH(CSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 1e5) + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, sequence); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently // ... // into the future! - await regtestUtils.mine(10) + await regtestUtils.mine(10); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // expiry in the future, {Alice's signature} OP_TRUE it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)', async () => { // two hours after confirmation - const sequence = bip68.encode({ seconds: 7168 }) + const sequence = bip68.encode({ seconds: 7168 }); const p2sh = bitcoin.payments.p2sh({ network: regtest, redeem: { - output: csvCheckSigOutput(alice, bob, sequence) - } - }) + output: csvCheckSigOutput(alice, bob, sequence), + }, + }); // fund the P2SH(CSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 2e4) + const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, sequence); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); await regtestUtils.broadcast(tx.toHex()).catch(err => { assert.throws(() => { - if (err) throw err - }, /Error: non-BIP68-final \(code 64\)/) - }) - }) + if (err) throw err; + }, /Error: non-BIP68-final \(code 64\)/); + }); + }); // Check first combination of complex CSV, 2 of 3 it('can create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)', async () => { - const height = await regtestUtils.height() - // 2 blocks from now - const sequence1 = bip68.encode({ blocks: 2 }) + const sequence1 = bip68.encode({ blocks: 2 }); // 5 blocks from now - const sequence2 = bip68.encode({ blocks: 5 }) + const sequence2 = bip68.encode({ blocks: 5 }); const p2sh = bitcoin.payments.p2sh({ redeem: { - output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) + output: complexCsvOutput( + alice, + bob, + charles, + dave, + sequence1, + sequence2, + ), }, - network: regtest - }) + network: regtest, + }); // fund the P2SH(CCSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 1e5) + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.opcodes.OP_0, bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.script.signature.encode(charles.sign(signatureHash), hashType), + bitcoin.script.signature.encode( + charles.sign(signatureHash), + hashType, + ), bitcoin.opcodes.OP_TRUE, - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // Check first combination of complex CSV, mediator + 1 of 3 after 2 blocks it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)', async () => { - const height = await regtestUtils.height() - // 2 blocks from now - const sequence1 = bip68.encode({ blocks: 2 }) + const sequence1 = bip68.encode({ blocks: 2 }); // 5 blocks from now - const sequence2 = bip68.encode({ blocks: 5 }) + const sequence2 = bip68.encode({ blocks: 5 }); const p2sh = bitcoin.payments.p2sh({ redeem: { - output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) + output: complexCsvOutput( + alice, + bob, + charles, + dave, + sequence1, + sequence2, + ), }, - network: regtest - }) + network: regtest, + }); // fund the P2SH(CCSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 1e5) + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence1) // Set sequence1 for input - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, sequence1); // Set sequence1 for input + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.opcodes.OP_0, bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), bitcoin.opcodes.OP_0, - bitcoin.opcodes.OP_TRUE - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); // Wait 2 blocks - await regtestUtils.mine(2) + await regtestUtils.mine(2); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) + value: 7e4, + }); + }); // Check first combination of complex CSV, mediator after 5 blocks it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)', async () => { - const height = await regtestUtils.height() - // 2 blocks from now - const sequence1 = bip68.encode({ blocks: 2 }) + const sequence1 = bip68.encode({ blocks: 2 }); // 5 blocks from now - const sequence2 = bip68.encode({ blocks: 5 }) + const sequence2 = bip68.encode({ blocks: 5 }); const p2sh = bitcoin.payments.p2sh({ redeem: { - output: complexCsvOutput(alice, bob, charles, dave, sequence1, sequence2) + output: complexCsvOutput( + alice, + bob, + charles, + dave, + sequence1, + sequence2, + ), }, - network: regtest - }) + network: regtest, + }); // fund the P2SH(CCSV) address - const unspent = await regtestUtils.faucet(p2sh.address, 1e5) + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, sequence2) // Set sequence2 for input - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, sequence2); // Set sequence2 for input + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); // {Alice mediator sig} OP_FALSE - const tx = txb.buildIncomplete() - const signatureHash = tx.hashForSignature(0, p2sh.redeem.output, hashType) + const tx = txb.buildIncomplete(); + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); const redeemScriptSig = bitcoin.payments.p2sh({ network: regtest, redeem: { network: regtest, - output: p2sh.redeem.output, + output: p2sh.redeem!.output, input: bitcoin.script.compile([ bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_0 - ]) - } - }).input - tx.setInputScript(0, redeemScriptSig) + bitcoin.opcodes.OP_0, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); // Wait 5 blocks - await regtestUtils.mine(5) + await regtestUtils.mine(5); - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 7e4 - }) - }) -}) + value: 7e4, + }); + }); +}); diff --git a/test/integration/payments.spec.ts b/test/integration/payments.spec.ts index 905eda8..6592c2c 100644 --- a/test/integration/payments.spec.ts +++ b/test/integration/payments.spec.ts @@ -1,23 +1,27 @@ -const bitcoin = require('../../') - -const { describe, it } = require('mocha') -const regtestUtils = require('./_regtest') -const NETWORK = regtestUtils.network +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const NETWORK = regtestUtils.network; const keyPairs = [ bitcoin.ECPair.makeRandom({ network: NETWORK }), - bitcoin.ECPair.makeRandom({ network: NETWORK }) -] -console.warn = () => {} // Silence the Deprecation Warning + bitcoin.ECPair.makeRandom({ network: NETWORK }), +]; +console.warn = () => {}; // Silence the Deprecation Warning -async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { - const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4) +async function buildAndSign( + depends: any, + prevOutput: any, + redeemScript: any, + witnessScript: any, +) { + const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4); - const txb = new bitcoin.TransactionBuilder(NETWORK) - txb.addInput(unspent.txId, unspent.vout, null, prevOutput) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const txb = new bitcoin.TransactionBuilder(NETWORK); + txb.addInput(unspent.txId, unspent.vout, undefined, prevOutput); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); - const posType = depends.prevOutScriptType - const needsValue = !!witnessScript || posType.slice(-6) === 'p2wpkh' + const posType = depends.prevOutScriptType; + const needsValue = !!witnessScript || posType.slice(-6) === 'p2wpkh'; if (depends.signatures) { keyPairs.forEach(keyPair => { @@ -28,8 +32,8 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { redeemScript, witnessValue: needsValue ? unspent.value : undefined, witnessScript, - }) - }) + }); + }); } else if (depends.signature) { txb.sign({ prevOutScriptType: posType, @@ -38,52 +42,94 @@ async function buildAndSign (depends, prevOutput, redeemScript, witnessScript) { redeemScript, witnessValue: needsValue ? unspent.value : undefined, witnessScript, - }) + }); } - return regtestUtils.broadcast(txb.build().toHex()) + return regtestUtils.broadcast(txb.build().toHex()); } -;['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => { - const fixtures = require('../fixtures/' + k) - const { depends } = fixtures.dynamic - const fn = bitcoin.payments[k] +['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => { + const fixtures = require('../fixtures/' + k); + const { depends } = fixtures.dynamic; + const fn: any = (bitcoin.payments as any)[k]; - const base = {} - if (depends.pubkey) base.pubkey = keyPairs[0].publicKey - if (depends.pubkeys) base.pubkeys = keyPairs.map(x => x.publicKey) - if (depends.m) base.m = base.pubkeys.length + const base: any = {}; + if (depends.pubkey) base.pubkey = keyPairs[0].publicKey; + if (depends.pubkeys) base.pubkeys = keyPairs.map(x => x.publicKey); + if (depends.m) base.m = base.pubkeys.length; - const { output } = fn(base) - if (!output) throw new TypeError('Missing output') + const { output } = fn(base); + if (!output) throw new TypeError('Missing output'); describe('bitcoinjs-lib (payments - ' + k + ')', () => { it('can broadcast as an output, and be spent as an input', async () => { - Object.assign(depends, { prevOutScriptType: k }) - await buildAndSign(depends, output, undefined, undefined) - }) + Object.assign(depends, { prevOutScriptType: k }); + await buildAndSign(depends, output, undefined, undefined); + }); - it('can (as P2SH(' + k + ')) broadcast as an output, and be spent as an input', async () => { - const p2sh = bitcoin.payments.p2sh({ redeem: { output }, network: NETWORK }) - Object.assign(depends, { prevOutScriptType: 'p2sh-' + k }) - await buildAndSign(depends, p2sh.output, p2sh.redeem.output, undefined) - }) + it( + 'can (as P2SH(' + + k + + ')) broadcast as an output, and be spent as an input', + async () => { + const p2sh = bitcoin.payments.p2sh({ + redeem: { output }, + network: NETWORK, + }); + Object.assign(depends, { prevOutScriptType: 'p2sh-' + k }); + await buildAndSign( + depends, + p2sh.output, + p2sh.redeem!.output, + undefined, + ); + }, + ); // NOTE: P2WPKH cannot be wrapped in P2WSH, consensus fail - if (k === 'p2wpkh') return + if (k === 'p2wpkh') return; - it('can (as P2WSH(' + k + ')) broadcast as an output, and be spent as an input', async () => { - const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) - Object.assign(depends, { prevOutScriptType: 'p2wsh-' + k }) - await buildAndSign(depends, p2wsh.output, undefined, p2wsh.redeem.output) - }) + it( + 'can (as P2WSH(' + + k + + ')) broadcast as an output, and be spent as an input', + async () => { + const p2wsh = bitcoin.payments.p2wsh({ + redeem: { output }, + network: NETWORK, + }); + Object.assign(depends, { prevOutScriptType: 'p2wsh-' + k }); + await buildAndSign( + depends, + p2wsh.output, + undefined, + p2wsh.redeem!.output, + ); + }, + ); - it('can (as P2SH(P2WSH(' + k + '))) broadcast as an output, and be spent as an input', async () => { - const p2wsh = bitcoin.payments.p2wsh({ redeem: { output }, network: NETWORK }) - const p2sh = bitcoin.payments.p2sh({ redeem: { output: p2wsh.output }, network: NETWORK }) + it( + 'can (as P2SH(P2WSH(' + + k + + '))) broadcast as an output, and be spent as an input', + async () => { + const p2wsh = bitcoin.payments.p2wsh({ + redeem: { output }, + network: NETWORK, + }); + const p2sh = bitcoin.payments.p2sh({ + redeem: { output: p2wsh.output }, + network: NETWORK, + }); - Object.assign(depends, { prevOutScriptType: 'p2sh-p2wsh-' + k }) - await buildAndSign(depends, p2sh.output, p2sh.redeem.output, p2wsh.redeem.output) - }) - }) -}) + Object.assign(depends, { prevOutScriptType: 'p2sh-p2wsh-' + k }); + await buildAndSign( + depends, + p2sh.output, + p2sh.redeem!.output, + p2wsh.redeem!.output, + ); + }, + ); + }); +}); diff --git a/test/integration/transactions-psbt.spec.ts b/test/integration/transactions-psbt.spec.ts index 9040fc2..a98407d 100644 --- a/test/integration/transactions-psbt.spec.ts +++ b/test/integration/transactions-psbt.spec.ts @@ -1,9 +1,9 @@ -const { describe, it } = require('mocha'); -const assert = require('assert'); -const bitcoin = require('../../'); -const bip32 = require('bip32'); +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +import * as bip32 from 'bip32'; const rng = require('randombytes'); -const regtestUtils = require('./_regtest'); const regtest = regtestUtils.network; // See bottom of file for some helper functions used to make the payment objects needed. @@ -174,7 +174,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData1) .addOutput({ - script: embed.output, + script: embed.output!, value: 1000, }) .addOutput({ @@ -350,7 +350,12 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // For learning purposes, ignore this test. // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData const p2wpkh = createPayment('p2wpkh'); - const inputData = await getInputData(5e4, p2wpkh.payment, false, 'noredeem'); + const inputData = await getInputData( + 5e4, + p2wpkh.payment, + false, + 'noredeem', + ); const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ @@ -486,7 +491,12 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // For learning purposes, ignore this test. // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); - const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh-p2wsh'); + const inputData = await getInputData( + 5e4, + p2sh.payment, + false, + 'p2sh-p2wsh', + ); const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) .addOutput({ @@ -528,9 +538,9 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { masterFingerprint, path, pubkey, - } - ] - } + }, + ], + }; const p2wpkh = createPayment('p2wpkh', [childNode]); const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem'); { @@ -539,7 +549,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { } // You can add extra attributes for updateData into the addInput(s) object(s) - Object.assign(inputData, updateData) + Object.assign(inputData, updateData); const psbt = new bitcoin.Psbt({ network: regtest }) .addInput(inputData) @@ -551,7 +561,10 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .signInputHD(0, hdRoot); // must sign with root!!! assert.strictEqual(psbt.validateSignaturesOfInput(0), true); - assert.strictEqual(psbt.validateSignaturesOfInput(0, childNode.publicKey), true); + assert.strictEqual( + psbt.validateSignaturesOfInput(0, childNode.publicKey), + true, + ); psbt.finalizeAllInputs(); const tx = psbt.extractTransaction(); @@ -568,18 +581,18 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); }); -function createPayment(_type, myKeys, network) { +function createPayment(_type: string, myKeys?: any[], network?: any) { network = network || regtest; const splitType = _type.split('-').reverse(); const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; const keys = myKeys || []; - let m; + let m: number | undefined; if (isMultisig) { const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/); - m = parseInt(match[1]); - let n = parseInt(match[2]); + m = parseInt(match![1]); + let n = parseInt(match![2]); if (keys.length > 0 && keys.length !== n) { - throw new Error('Need n keys for multisig') + throw new Error('Need n keys for multisig'); } while (!myKeys && n > 1) { keys.push(bitcoin.ECPair.makeRandom({ network })); @@ -588,7 +601,7 @@ function createPayment(_type, myKeys, network) { } if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network })); - let payment; + let payment: any; splitType.forEach(type => { if (type.slice(0, 4) === 'p2ms') { payment = bitcoin.payments.p2ms({ @@ -597,12 +610,12 @@ function createPayment(_type, myKeys, network) { network, }); } else if (['p2sh', 'p2wsh'].indexOf(type) > -1) { - payment = bitcoin.payments[type]({ + payment = (bitcoin.payments as any)[type]({ redeem: payment, network, }); } else { - payment = bitcoin.payments[type]({ + payment = (bitcoin.payments as any)[type]({ pubkey: keys[0].publicKey, network, }); @@ -615,13 +628,18 @@ function createPayment(_type, myKeys, network) { }; } -function getWitnessUtxo(out) { +function getWitnessUtxo(out: any) { delete out.address; out.script = Buffer.from(out.script, 'hex'); return out; } -async function getInputData(amount, payment, isSegwit, redeemType) { +async function getInputData( + amount: number, + payment: any, + isSegwit: boolean, + redeemType: string, +) { const unspent = await regtestUtils.faucetComplex(payment.output, amount); const utx = await regtestUtils.fetch(unspent.txId); // for non segwit inputs, you must pass the full transaction buffer @@ -629,7 +647,7 @@ async function getInputData(amount, payment, isSegwit, redeemType) { // for segwit inputs, you only need the output script and value as an object. const witnessUtxo = getWitnessUtxo(utx.outs[unspent.vout]); const mixin = isSegwit ? { witnessUtxo } : { nonWitnessUtxo }; - const mixin2 = {}; + const mixin2: any = {}; switch (redeemType) { case 'p2sh': mixin2.redeemScript = payment.redeem.output; diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts index 173fc4a..8a313f2 100644 --- a/test/integration/transactions.spec.ts +++ b/test/integration/transactions.spec.ts @@ -1,361 +1,421 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bitcoin = require('../../') -const regtestUtils = require('./_regtest') -const regtest = regtestUtils.network -console.warn = () => {} // Silence the Deprecation Warning +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const regtest = regtestUtils.network; +console.warn = () => {}; // Silence the Deprecation Warning -function rng () { - return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64') +function rng() { + return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64'); } describe('bitcoinjs-lib (transactions)', () => { it('can create a 1-to-1 Transaction', () => { - const alice = bitcoin.ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') - const txb = new bitcoin.TransactionBuilder() + const alice = bitcoin.ECPair.fromWIF( + 'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy', + ); + const txb = new bitcoin.TransactionBuilder(); - txb.setVersion(1) - txb.addInput('61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', 0) // Alice's previous transaction output, has 15000 satoshis - txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000) + txb.setVersion(1); + txb.addInput( + '61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', + 0, + ); // Alice's previous transaction output, has 15000 satoshis + txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000); // (in)15000 - (out)12000 = (fee)3000, this is the miner fee txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, - keyPair: alice - }) + keyPair: alice, + }); // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below - assert.strictEqual(txb.build().toHex(), '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000') - }) + assert.strictEqual( + txb.build().toHex(), + '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000', + ); + }); it('can create a 2-to-2 Transaction', () => { - const alice = bitcoin.ECPair.fromWIF('L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1') - const bob = bitcoin.ECPair.fromWIF('KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z') + const alice = bitcoin.ECPair.fromWIF( + 'L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1', + ); + const bob = bitcoin.ECPair.fromWIF( + 'KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z', + ); - const txb = new bitcoin.TransactionBuilder() - txb.setVersion(1) - txb.addInput('b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c', 6) // Alice's previous transaction output, has 200000 satoshis - txb.addInput('7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730', 0) // Bob's previous transaction output, has 300000 satoshis - txb.addOutput('1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb', 180000) - txb.addOutput('1JtK9CQw1syfWj1WtFMWomrYdV3W2tWBF9', 170000) + const txb = new bitcoin.TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c', + 6, + ); // Alice's previous transaction output, has 200000 satoshis + txb.addInput( + '7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730', + 0, + ); // Bob's previous transaction output, has 300000 satoshis + txb.addOutput('1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb', 180000); + txb.addOutput('1JtK9CQw1syfWj1WtFMWomrYdV3W2tWBF9', 170000); // (in)(200000 + 300000) - (out)(180000 + 170000) = (fee)150000, this is the miner fee txb.sign({ prevOutScriptType: 'p2pkh', vin: 1, - keyPair: bob - }) // Bob signs his input, which was the second input (1th) + keyPair: bob, + }); // Bob signs his input, which was the second input (1th) txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, - keyPair: alice - }) // Alice signs her input, which was the first input (0th) + keyPair: alice, + }); // Alice signs her input, which was the first input (0th) // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below - assert.strictEqual(txb.build().toHex(), '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000') - }) + assert.strictEqual( + txb.build().toHex(), + '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000', + ); + }); it('can create (and broadcast via 3PBP) a typical Transaction', async () => { - const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }) - const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }) - const aliceChange = bitcoin.ECPair.makeRandom({ network: regtest, rng: rng }) + const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }); + const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }); + const aliceChange = bitcoin.ECPair.makeRandom({ + network: regtest, + rng: rng, + }); - const alice1pkh = bitcoin.payments.p2pkh({ pubkey: alice1.publicKey, network: regtest }) - const alice2pkh = bitcoin.payments.p2pkh({ pubkey: alice2.publicKey, network: regtest }) - const aliceCpkh = bitcoin.payments.p2pkh({ pubkey: aliceChange.publicKey, network: regtest }) + const alice1pkh = bitcoin.payments.p2pkh({ + pubkey: alice1.publicKey, + network: regtest, + }); + const alice2pkh = bitcoin.payments.p2pkh({ + pubkey: alice2.publicKey, + network: regtest, + }); + const aliceCpkh = bitcoin.payments.p2pkh({ + pubkey: aliceChange.publicKey, + network: regtest, + }); // give Alice 2 unspent outputs - const unspent0 = await regtestUtils.faucet(alice1pkh.address, 5e4) + const unspent0 = await regtestUtils.faucet(alice1pkh.address!, 5e4); - const unspent1 = await regtestUtils.faucet(alice2pkh.address, 7e4) + const unspent1 = await regtestUtils.faucet(alice2pkh.address!, 7e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent0.txId, unspent0.vout) // alice1 unspent - txb.addInput(unspent1.txId, unspent1.vout) // alice2 unspent - txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4) // the actual "spend" - txb.addOutput(aliceCpkh.address, 1e4) // Alice's change + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent0.txId, unspent0.vout); // alice1 unspent + txb.addInput(unspent1.txId, unspent1.vout); // alice2 unspent + txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4); // the actual "spend" + txb.addOutput(aliceCpkh.address!, 1e4); // Alice's change // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee // Alice signs each input with the respective private keys txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, - keyPair: alice1 - }) + keyPair: alice1, + }); txb.sign({ prevOutScriptType: 'p2pkh', vin: 1, - keyPair: alice2 - }) + keyPair: alice2, + }); // build and broadcast our RegTest network - await regtestUtils.broadcast(txb.build().toHex()) + await regtestUtils.broadcast(txb.build().toHex()); // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 - }) + }); it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: regtest }) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); + const p2pkh = bitcoin.payments.p2pkh({ + pubkey: keyPair.publicKey, + network: regtest, + }); - const unspent = await regtestUtils.faucet(p2pkh.address, 2e5) + const unspent = await regtestUtils.faucet(p2pkh.address!, 2e5); - const txb = new bitcoin.TransactionBuilder(regtest) - const data = Buffer.from('bitcoinjs-lib', 'utf8') - const embed = bitcoin.payments.embed({ data: [data] }) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(embed.output, 1000) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5) + const txb = new bitcoin.TransactionBuilder(regtest); + const data = Buffer.from('bitcoinjs-lib', 'utf8'); + const embed = bitcoin.payments.embed({ data: [data] }); + txb.addInput(unspent.txId, unspent.vout); + txb.addOutput(embed.output!, 1000); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); // build and broadcast to the RegTest network - await regtestUtils.broadcast(txb.build().toHex()) - }) + await regtestUtils.broadcast(txb.build().toHex()); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => { const keyPairs = [ bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }) - ] - const pubkeys = keyPairs.map(x => x.publicKey) - const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys: pubkeys, network: regtest }) - const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest }) + bitcoin.ECPair.makeRandom({ network: regtest }), + ]; + const pubkeys = keyPairs.map(x => x.publicKey); + const p2ms = bitcoin.payments.p2ms({ + m: 2, + pubkeys: pubkeys, + network: regtest, + }); + const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest }); - const unspent = await regtestUtils.faucet(p2sh.address, 2e4) + const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); txb.sign({ prevOutScriptType: 'p2sh-p2ms', vin: 0, keyPair: keyPairs[0], - redeemScript: p2sh.redeem.output, - }) + redeemScript: p2sh.redeem!.output, + }); txb.sign({ prevOutScriptType: 'p2sh-p2ms', vin: 0, keyPair: keyPairs[2], - redeemScript: p2sh.redeem.output, - }) - const tx = txb.build() + redeemScript: p2sh.redeem!.output, + }); + const tx = txb.build(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 1e4 - }) - }) + value: 1e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) - const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); + const p2wpkh = bitcoin.payments.p2wpkh({ + pubkey: keyPair.publicKey, + network: regtest, + }); + const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }); - const unspent = await regtestUtils.faucet(p2sh.address, 5e4) + const unspent = await regtestUtils.faucet(p2sh.address!, 5e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); txb.sign({ prevOutScriptType: 'p2sh-p2wpkh', vin: 0, keyPair: keyPair, - redeemScript: p2sh.redeem.output, + redeemScript: p2sh.redeem!.output, witnessValue: unspent.value, - }) + }); - const tx = txb.build() + const tx = txb.build(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 2e4 - }) - }) + value: 2e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: regtest }) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); + const p2wpkh = bitcoin.payments.p2wpkh({ + pubkey: keyPair.publicKey, + network: regtest, + }); - const unspent = await regtestUtils.faucetComplex(p2wpkh.output, 5e4) + const unspent = await regtestUtils.faucetComplex(p2wpkh.output!, 5e4); // XXX: build the Transaction w/ a P2WPKH input - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, null, p2wpkh.output) // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, undefined, p2wpkh.output); // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); txb.sign({ prevOutScriptType: 'p2wpkh', vin: 0, keyPair: keyPair, witnessValue: unspent.value, - }) // NOTE: no redeem script - const tx = txb.build() + }); // NOTE: no redeem script + const tx = txb.build(); // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 2e4 - }) - }) + value: 2e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }) - const p2pk = bitcoin.payments.p2pk({ pubkey: keyPair.publicKey, network: regtest }) - const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }) + const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); + const p2pk = bitcoin.payments.p2pk({ + pubkey: keyPair.publicKey, + network: regtest, + }); + const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }); - const unspent = await regtestUtils.faucetComplex(p2wsh.output, 5e4) + const unspent = await regtestUtils.faucetComplex(p2wsh.output!, 5e4); // XXX: build the Transaction w/ a P2WSH input - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, null, p2wsh.output) // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, undefined, p2wsh.output); // NOTE: provide the prevOutScript! + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); txb.sign({ prevOutScriptType: 'p2wsh-p2pk', vin: 0, keyPair: keyPair, witnessValue: 5e4, - witnessScript: p2wsh.redeem.output, - }) // NOTE: provide a witnessScript! - const tx = txb.build() + witnessScript: p2wsh.redeem!.output, + }); // NOTE: provide a witnessScript! + const tx = txb.build(); // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 2e4 - }) - }) + value: 2e4, + }); + }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { const keyPairs = [ bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }) - ] - const pubkeys = keyPairs.map(x => x.publicKey) + bitcoin.ECPair.makeRandom({ network: regtest }), + ]; + const pubkeys = keyPairs.map(x => x.publicKey); - const p2ms = bitcoin.payments.p2ms({ m: 3, pubkeys, network: regtest }) - const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest }) - const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest }) + const p2ms = bitcoin.payments.p2ms({ m: 3, pubkeys, network: regtest }); + const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest }); + const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest }); - const unspent = await regtestUtils.faucet(p2sh.address, 6e4) + const unspent = await regtestUtils.faucet(p2sh.address!, 6e4); - const txb = new bitcoin.TransactionBuilder(regtest) - txb.addInput(unspent.txId, unspent.vout, null, p2sh.output) - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4) + const txb = new bitcoin.TransactionBuilder(regtest); + txb.addInput(unspent.txId, unspent.vout, undefined, p2sh.output); + txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4); txb.sign({ prevOutScriptType: 'p2sh-p2wsh-p2ms', vin: 0, keyPair: keyPairs[0], - redeemScript: p2sh.redeem.output, + redeemScript: p2sh.redeem!.output, witnessValue: unspent.value, - witnessScript: p2wsh.redeem.output, - }) + witnessScript: p2wsh.redeem!.output, + }); txb.sign({ prevOutScriptType: 'p2sh-p2wsh-p2ms', vin: 0, keyPair: keyPairs[2], - redeemScript: p2sh.redeem.output, + redeemScript: p2sh.redeem!.output, witnessValue: unspent.value, - witnessScript: p2wsh.redeem.output, - }) + witnessScript: p2wsh.redeem!.output, + }); txb.sign({ prevOutScriptType: 'p2sh-p2wsh-p2ms', vin: 0, keyPair: keyPairs[3], - redeemScript: p2sh.redeem.output, + redeemScript: p2sh.redeem!.output, witnessValue: unspent.value, - witnessScript: p2wsh.redeem.output, - }) + witnessScript: p2wsh.redeem!.output, + }); - const tx = txb.build() + const tx = txb.build(); // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()) + await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 3e4 - }) - }) + value: 3e4, + }); + }); it('can verify Transaction (P2PKH) signatures', () => { - const txHex = '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700' + const txHex = + '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700'; const keyPairs = [ '032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d', '0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a', - '039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f' - ].map(q => { return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')) }) + '039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f', + ].map(q => { + return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')); + }); - const tx = bitcoin.Transaction.fromHex(txHex) + const tx = bitcoin.Transaction.fromHex(txHex); tx.ins.forEach((input, i) => { - const keyPair = keyPairs[i] + const keyPair = keyPairs[i]; const p2pkh = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, - input: input.script - }) + input: input.script, + }); - const ss = bitcoin.script.signature.decode(p2pkh.signature) - const hash = tx.hashForSignature(i, p2pkh.output, ss.hashType) + const ss = bitcoin.script.signature.decode(p2pkh.signature!); + const hash = tx.hashForSignature(i, p2pkh.output!, ss.hashType); - assert.strictEqual(keyPair.verify(hash, ss.signature), true) - }) - }) + assert.strictEqual(keyPair.verify(hash, ss.signature), true); + }); + }); it('can verify Transaction (P2SH(P2WPKH)) signatures', () => { const utxos = { 'f72d1d83ac40fcedd01415751556a905844ab5f44bbb7728565ebb91b1590109:0': { - value: 50000 - } - } + value: 50000, + }, + }; - const txHex = '02000000000101090159b191bb5e562877bb4bf4b54a8405a95615751514d0edfc40ac831d2df7000000001716001435a179e5516947a39ae9c8a25e9fe62c0fc598edffffffff01204e0000000000001976a91431d43308d3c886d53e9ae8a45728370571ff456988ac0247304402206ec41f685b997a51f325b07ee852e82a535f6b52ef54485cc133e05168aa052a022070bafa86108acb51c77b2b259ae8fb7fd1efa10fef804fcfe9b13c2db719acf5012103fb03e9d0a9af86cbed94225dbb8bb70f6b82109bce0a61ddcf41dab6cbb4871100000000' - const tx = bitcoin.Transaction.fromHex(txHex) + const txHex = + '02000000000101090159b191bb5e562877bb4bf4b54a8405a95615751514d0edfc40ac831d2df7000000001716001435a179e5516947a39ae9c8a25e9fe62c0fc598edffffffff01204e0000000000001976a91431d43308d3c886d53e9ae8a45728370571ff456988ac0247304402206ec41f685b997a51f325b07ee852e82a535f6b52ef54485cc133e05168aa052a022070bafa86108acb51c77b2b259ae8fb7fd1efa10fef804fcfe9b13c2db719acf5012103fb03e9d0a9af86cbed94225dbb8bb70f6b82109bce0a61ddcf41dab6cbb4871100000000'; + const tx = bitcoin.Transaction.fromHex(txHex); tx.ins.forEach((input, i) => { - const txId = Buffer.from(input.hash).reverse().toString('hex') - const utxo = utxos[`${txId}:${i}`] - if (!utxo) throw new Error('Missing utxo') + const txId = (Buffer.from(input.hash).reverse() as Buffer).toString( + 'hex', + ); + const utxo = (utxos as any)[`${txId}:${i}`]; + if (!utxo) throw new Error('Missing utxo'); const p2sh = bitcoin.payments.p2sh({ input: input.script, - witness: input.witness - }) - const p2wpkh = bitcoin.payments.p2wpkh(p2sh.redeem) - const p2pkh = bitcoin.payments.p2pkh({ pubkey: p2wpkh.pubkey }) // because P2WPKH is annoying + witness: input.witness, + }); + const p2wpkh = bitcoin.payments.p2wpkh(p2sh.redeem!); + const p2pkh = bitcoin.payments.p2pkh({ pubkey: p2wpkh.pubkey }); // because P2WPKH is annoying - const ss = bitcoin.script.signature.decode(p2wpkh.signature) - const hash = tx.hashForWitnessV0(i, p2pkh.output, utxo.value, ss.hashType) - const keyPair = bitcoin.ECPair.fromPublicKey(p2wpkh.pubkey) // aka, cQ3EtF4mApRcogNGSeyPTKbmfxxn3Yfb1wecfKSws9a8bnYuxoAk + const ss = bitcoin.script.signature.decode(p2wpkh.signature!); + const hash = tx.hashForWitnessV0( + i, + p2pkh.output!, + utxo.value, + ss.hashType, + ); + const keyPair = bitcoin.ECPair.fromPublicKey(p2wpkh.pubkey!); // aka, cQ3EtF4mApRcogNGSeyPTKbmfxxn3Yfb1wecfKSws9a8bnYuxoAk - assert.strictEqual(keyPair.verify(hash, ss.signature), true) - }) - }) -}) + assert.strictEqual(keyPair.verify(hash, ss.signature), true); + }); + }); +}); diff --git a/test/payments.spec.ts b/test/payments.spec.ts index 1386ea4..051cc04 100644 --- a/test/payments.spec.ts +++ b/test/payments.spec.ts @@ -1,49 +1,55 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const u = require('./payments.utils') - -;['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(p => { +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as u from './payments.utils'; +import { PaymentCreator } from '../src/payments'; +['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(p => { describe(p, () => { - let fn - let payment = require('../src/payments/' + p) + let fn: PaymentCreator; + let payment = require('../src/payments/' + p); if (p === 'embed') { - fn = payment.p2data + fn = payment.p2data; } else { - fn = payment[p] + fn = payment[p]; } - const fixtures = require('./fixtures/' + p) + const fixtures = require('./fixtures/' + p); - fixtures.valid.forEach((f, i) => { + fixtures.valid.forEach((f: any) => { it(f.description + ' as expected', () => { - const args = u.preform(f.arguments) - const actual = fn(args, f.options) + const args = u.preform(f.arguments); + const actual = fn(args, f.options); - u.equate(actual, f.expected, f.arguments) - }) + u.equate(actual, f.expected, f.arguments); + }); it(f.description + ' as expected (no validation)', () => { - const args = u.preform(f.arguments) - const actual = fn(args, Object.assign({}, f.options, { - validate: false - })) + const args = u.preform(f.arguments); + const actual = fn( + args, + Object.assign({}, f.options, { + validate: false, + }), + ); - u.equate(actual, f.expected, f.arguments) - }) - }) + u.equate(actual, f.expected, f.arguments); + }); + }); - fixtures.invalid.forEach(f => { - it('throws ' + f.exception + (f.description ? ('for ' + f.description) : ''), () => { - const args = u.preform(f.arguments) + fixtures.invalid.forEach((f: any) => { + it( + 'throws ' + f.exception + (f.description ? 'for ' + f.description : ''), + () => { + const args = u.preform(f.arguments); - assert.throws(() => { - fn(args, f.options) - }, new RegExp(f.exception)) - }) - }) + assert.throws(() => { + fn(args, f.options); + }, new RegExp(f.exception)); + }, + ); + }); if (p === 'p2sh') { - const p2wsh = require('../src/payments/p2wsh').p2wsh - const p2pk = require('../src/payments/p2pk').p2pk + const p2wsh = require('../src/payments/p2wsh').p2wsh; + const p2pk = require('../src/payments/p2pk').p2pk; it('properly assembles nested p2wsh with names', () => { const actual = fn({ redeem: p2wsh({ @@ -51,42 +57,57 @@ const u = require('./payments.utils') pubkey: Buffer.from( '03e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058', 'hex', - ) - }) - }) - }) - assert.strictEqual(actual.address, '3MGbrbye4ttNUXM8WAvBFRKry4fkS9fjuw') - assert.strictEqual(actual.name, 'p2sh-p2wsh-p2pk') - assert.strictEqual(actual.redeem.name, 'p2wsh-p2pk') - assert.strictEqual(actual.redeem.redeem.name, 'p2pk') - }) + ), + }), + }), + }); + assert.strictEqual( + actual.address, + '3MGbrbye4ttNUXM8WAvBFRKry4fkS9fjuw', + ); + assert.strictEqual(actual.name, 'p2sh-p2wsh-p2pk'); + assert.strictEqual(actual.redeem!.name, 'p2wsh-p2pk'); + assert.strictEqual(actual.redeem!.redeem!.name, 'p2pk'); + }); } // cross-verify dynamically too - if (!fixtures.dynamic) return - const { depends, details } = fixtures.dynamic + if (!fixtures.dynamic) return; + const { depends, details } = fixtures.dynamic; - details.forEach(f => { - const detail = u.preform(f) - const disabled = {} - if (f.disabled) f.disabled.forEach(k => { disabled[k] = true }) + details.forEach((f: any) => { + const detail = u.preform(f); + const disabled: any = {}; + if (f.disabled) + f.disabled.forEach((k: string) => { + disabled[k] = true; + }); for (let key in depends) { - if (key in disabled) continue - const dependencies = depends[key] + if (key in disabled) continue; + const dependencies = depends[key]; - dependencies.forEach(dependency => { - if (!Array.isArray(dependency)) dependency = [dependency] + dependencies.forEach((dependency: any) => { + if (!Array.isArray(dependency)) dependency = [dependency]; - const args = {} - dependency.forEach(d => { u.from(d, detail, args) }) - const expected = u.from(key, detail) + const args = {}; + dependency.forEach((d: any) => { + u.from(d, detail, args); + }); + const expected = u.from(key, detail); - it(f.description + ', ' + key + ' derives from ' + JSON.stringify(dependency), () => { - u.equate(fn(args), expected) - }) - }) + it( + f.description + + ', ' + + key + + ' derives from ' + + JSON.stringify(dependency), + () => { + u.equate(fn(args), expected); + }, + ); + }); } - }) - }) -}) + }); + }); +}); diff --git a/test/payments.utils.spec.ts b/test/payments.utils.spec.ts deleted file mode 100644 index 15414c4..0000000 --- a/test/payments.utils.spec.ts +++ /dev/null @@ -1,134 +0,0 @@ -const t = require('assert') -const bscript = require('../src/script') -const BNETWORKS = require('../src/networks') - -function tryHex (x) { - if (Buffer.isBuffer(x)) return x.toString('hex') - if (Array.isArray(x)) return x.map(tryHex) - return x -} - -function fromHex (x) { - if (typeof x === 'string') return Buffer.from(x, 'hex') - if (Array.isArray(x)) return x.map(fromHex) - return x -} -function tryASM (x) { - if (Buffer.isBuffer(x)) return bscript.toASM(x) - return x -} -function asmToBuffer (x) { - if (x === '') return Buffer.alloc(0) - return bscript.fromASM(x) -} -function carryOver (a, b) { - for (let k in b) { - if (k in a && k === 'redeem') { - carryOver(a[k], b[k]) - continue - } - - // don't, the value was specified - if (k in a) continue - - // otherwise, expect match - a[k] = b[k] - } -} - -function equateBase (a, b, context) { - if ('output' in b) t.strictEqual(tryASM(a.output), tryASM(b.output), `Inequal ${context}output`) - if ('input' in b) t.strictEqual(tryASM(a.input), tryASM(b.input), `Inequal ${context}input`) - if ('witness' in b) t.deepStrictEqual(tryHex(a.witness), tryHex(b.witness), `Inequal ${context}witness`) -} - -function equate (a, b, args) { - b = Object.assign({}, b) - carryOver(b, args) - - // by null, we mean 'undefined', but JSON - if (b.input === null) b.input = undefined - if (b.output === null) b.output = undefined - if (b.witness === null) b.witness = undefined - if (b.redeem) { - if (b.redeem.input === null) b.redeem.input = undefined - if (b.redeem.output === null) b.redeem.output = undefined - if (b.redeem.witness === null) b.redeem.witness = undefined - } - - equateBase(a, b, '') - if (b.redeem) equateBase(a.redeem, b.redeem, 'redeem.') - if (b.network) t.deepStrictEqual(a.network, BNETWORKS[b.network], 'Inequal *.network') - - // contextual - if (b.signature === null) b.signature = undefined - if (b.signatures === null) b.signatures = undefined - if ('address' in b) t.strictEqual(a.address, b.address, 'Inequal *.address') - if ('hash' in b) t.strictEqual(tryHex(a.hash), tryHex(b.hash), 'Inequal *.hash') - if ('pubkey' in b) t.strictEqual(tryHex(a.pubkey), tryHex(b.pubkey), 'Inequal *.pubkey') - if ('signature' in b) t.strictEqual(tryHex(a.signature), tryHex(b.signature), 'Inequal signature') - if ('m' in b) t.strictEqual(a.m, b.m, 'Inequal *.m') - if ('n' in b) t.strictEqual(a.n, b.n, 'Inequal *.n') - if ('pubkeys' in b) t.deepStrictEqual(tryHex(a.pubkeys), tryHex(b.pubkeys), 'Inequal *.pubkeys') - if ('signatures' in b) t.deepStrictEqual(tryHex(a.signatures), tryHex(b.signatures), 'Inequal *.signatures') - if ('data' in b) t.deepStrictEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data') -} - -function preform (x) { - x = Object.assign({}, x) - - if (x.network) x.network = BNETWORKS[x.network] - if (typeof x.inputHex === 'string') { - x.input = Buffer.from(x.inputHex, 'hex') - delete x.inputHex - } - if (typeof x.outputHex === 'string') { - x.output = Buffer.from(x.outputHex, 'hex') - delete x.outputHex - } - if (typeof x.output === 'string') x.output = asmToBuffer(x.output) - if (typeof x.input === 'string') x.input = asmToBuffer(x.input) - if (Array.isArray(x.witness)) x.witness = x.witness.map(fromHex) - - if (x.data) x.data = x.data.map(fromHex) - if (x.hash) x.hash = Buffer.from(x.hash, 'hex') - if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex') - if (x.signature) x.signature = Buffer.from(x.signature, 'hex') - if (x.pubkeys) x.pubkeys = x.pubkeys.map(fromHex) - if (x.signatures) x.signatures = x.signatures.map(y => { return Number.isFinite(y) ? y : Buffer.from(y, 'hex') }) - if (x.redeem) { - x.redeem = Object.assign({}, x.redeem) - if (typeof x.redeem.input === 'string') x.redeem.input = asmToBuffer(x.redeem.input) - if (typeof x.redeem.output === 'string') x.redeem.output = asmToBuffer(x.redeem.output) - if (Array.isArray(x.redeem.witness)) x.redeem.witness = x.redeem.witness.map(fromHex) - if (x.redeem.network) x.redeem.network = BNETWORKS[x.redeem.network] - } - - return x -} - -function from (path, object, result) { - path = path.split('.') - result = result || {} - - let r = result - path.forEach((k, i) => { - if (i < path.length - 1) { - r[k] = r[k] || {} - - // recurse - r = r[k] - object = object[k] - } else { - r[k] = object[k] - } - }) - - return result -} - -module.exports = { - from, - equate, - preform -} diff --git a/test/payments.utils.ts b/test/payments.utils.ts new file mode 100644 index 0000000..bcf79c7 --- /dev/null +++ b/test/payments.utils.ts @@ -0,0 +1,169 @@ +import * as t from 'assert'; +import * as bscript from '../src/script'; +import * as BNETWORKS from '../src/networks'; + +function tryHex(x: Buffer | Buffer[]): string | string[] { + if (Buffer.isBuffer(x)) return x.toString('hex'); + if (Array.isArray(x)) return x.map(tryHex) as string[]; + return x; +} + +function fromHex(x: string | string[]): Buffer | Buffer[] { + if (typeof x === 'string') return Buffer.from(x, 'hex'); + if (Array.isArray(x)) return x.map(fromHex) as Buffer[]; + return x; +} +function tryASM(x: Buffer): string { + if (Buffer.isBuffer(x)) return bscript.toASM(x); + return x; +} +function asmToBuffer(x: string) { + if (x === '') return Buffer.alloc(0); + return bscript.fromASM(x); +} +function carryOver(a: any, b: any) { + for (let k in b) { + if (k in a && k === 'redeem') { + carryOver(a[k], b[k]); + continue; + } + + // don't, the value was specified + if (k in a) continue; + + // otherwise, expect match + a[k] = b[k]; + } +} + +function equateBase(a: any, b: any, context: string) { + if ('output' in b) + t.strictEqual( + tryASM(a.output), + tryASM(b.output), + `Inequal ${context}output`, + ); + if ('input' in b) + t.strictEqual(tryASM(a.input), tryASM(b.input), `Inequal ${context}input`); + if ('witness' in b) + t.deepStrictEqual( + tryHex(a.witness), + tryHex(b.witness), + `Inequal ${context}witness`, + ); +} + +export function equate(a: any, b: any, args?: any) { + b = Object.assign({}, b); + carryOver(b, args); + + // by null, we mean 'undefined', but JSON + if (b.input === null) b.input = undefined; + if (b.output === null) b.output = undefined; + if (b.witness === null) b.witness = undefined; + if (b.redeem) { + if (b.redeem.input === null) b.redeem.input = undefined; + if (b.redeem.output === null) b.redeem.output = undefined; + if (b.redeem.witness === null) b.redeem.witness = undefined; + } + + equateBase(a, b, ''); + if (b.redeem) equateBase(a.redeem, b.redeem, 'redeem.'); + if (b.network) + t.deepStrictEqual( + a.network, + (BNETWORKS as any)[b.network], + 'Inequal *.network', + ); + + // contextual + if (b.signature === null) b.signature = undefined; + if (b.signatures === null) b.signatures = undefined; + if ('address' in b) t.strictEqual(a.address, b.address, 'Inequal *.address'); + if ('hash' in b) + t.strictEqual(tryHex(a.hash), tryHex(b.hash), 'Inequal *.hash'); + if ('pubkey' in b) + t.strictEqual(tryHex(a.pubkey), tryHex(b.pubkey), 'Inequal *.pubkey'); + if ('signature' in b) + t.strictEqual( + tryHex(a.signature), + tryHex(b.signature), + 'Inequal signature', + ); + if ('m' in b) t.strictEqual(a.m, b.m, 'Inequal *.m'); + if ('n' in b) t.strictEqual(a.n, b.n, 'Inequal *.n'); + if ('pubkeys' in b) + t.deepStrictEqual( + tryHex(a.pubkeys), + tryHex(b.pubkeys), + 'Inequal *.pubkeys', + ); + if ('signatures' in b) + t.deepStrictEqual( + tryHex(a.signatures), + tryHex(b.signatures), + 'Inequal *.signatures', + ); + if ('data' in b) + t.deepStrictEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data'); +} + +export function preform(x: any) { + x = Object.assign({}, x); + + if (x.network) x.network = (BNETWORKS as any)[x.network]; + if (typeof x.inputHex === 'string') { + x.input = Buffer.from(x.inputHex, 'hex'); + delete x.inputHex; + } + if (typeof x.outputHex === 'string') { + x.output = Buffer.from(x.outputHex, 'hex'); + delete x.outputHex; + } + if (typeof x.output === 'string') x.output = asmToBuffer(x.output); + if (typeof x.input === 'string') x.input = asmToBuffer(x.input); + if (Array.isArray(x.witness)) x.witness = x.witness.map(fromHex); + + if (x.data) x.data = x.data.map(fromHex); + if (x.hash) x.hash = Buffer.from(x.hash, 'hex'); + if (x.pubkey) x.pubkey = Buffer.from(x.pubkey, 'hex'); + if (x.signature) x.signature = Buffer.from(x.signature, 'hex'); + if (x.pubkeys) x.pubkeys = x.pubkeys.map(fromHex); + if (x.signatures) + x.signatures = x.signatures.map((y: any) => { + return Number.isFinite(y) ? y : Buffer.from(y, 'hex'); + }); + if (x.redeem) { + x.redeem = Object.assign({}, x.redeem); + if (typeof x.redeem.input === 'string') + x.redeem.input = asmToBuffer(x.redeem.input); + if (typeof x.redeem.output === 'string') + x.redeem.output = asmToBuffer(x.redeem.output); + if (Array.isArray(x.redeem.witness)) + x.redeem.witness = x.redeem.witness.map(fromHex); + if (x.redeem.network) + x.redeem.network = (BNETWORKS as any)[x.redeem.network]; + } + + return x; +} + +export function from(path: string, object: any, result?: any) { + const paths = path.split('.'); + result = result || {}; + + let r = result; + paths.forEach((k, i) => { + if (i < paths.length - 1) { + r[k] = r[k] || {}; + + // recurse + r = r[k]; + object = object[k]; + } else { + r[k] = object[k]; + } + }); + + return result; +} diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 54eb5e4..02a420d 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,688 +1,713 @@ -const { describe, it } = require('mocha') -const assert = require('assert') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; -const bip32 = require('bip32') -const ECPair = require('../src/ecpair') -const Psbt = require('..').Psbt -const NETWORKS = require('../src/networks') +import { bip32, ECPair, networks as NETWORKS, Psbt } from '..'; -const initBuffers = object => JSON.parse(JSON.stringify(object), (key, value) => { - const regex = new RegExp(/^Buffer.from\(['"](.*)['"], ['"](.*)['"]\)$/) - const result = regex.exec(value) - if (!result) return value +import * as preFixtures from './fixtures/psbt.json'; - const data = result[1] - const encoding = result[2] +const initBuffers = (object: any): typeof preFixtures => + JSON.parse(JSON.stringify(object), (_, value) => { + const regex = new RegExp(/^Buffer.from\(['"](.*)['"], ['"](.*)['"]\)$/); + const result = regex.exec(value); + if (!result) return value; - return Buffer.from(data, encoding) -}) + const data = result[1]; + const encoding = result[2]; -const fixtures = initBuffers(require('./fixtures/psbt')) + return Buffer.from(data, encoding); + }); -const upperCaseFirstLetter = str => str.replace(/^./, s => s.toUpperCase()) +const fixtures = initBuffers(preFixtures); -const b = hex => Buffer.from(hex, 'hex'); +const upperCaseFirstLetter = (str: string) => + str.replace(/^./, s => s.toUpperCase()); + +// const b = (hex: string) => Buffer.from(hex, 'hex'); describe(`Psbt`, () => { describe('BIP174 Test Vectors', () => { fixtures.bip174.invalid.forEach(f => { it(`Invalid: ${f.description}`, () => { assert.throws(() => { - Psbt.fromBase64(f.psbt) - }, new RegExp(f.errorMessage)) - }) - }) + Psbt.fromBase64(f.psbt); + }, new RegExp(f.errorMessage)); + }); + }); fixtures.bip174.valid.forEach(f => { it(`Valid: ${f.description}`, () => { assert.doesNotThrow(() => { - Psbt.fromBase64(f.psbt) - }) - }) - }) + Psbt.fromBase64(f.psbt); + }); + }); + }); fixtures.bip174.failSignChecks.forEach(f => { - const keyPair = ECPair.makeRandom() + const keyPair = ECPair.makeRandom(); it(`Fails Signer checks: ${f.description}`, () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); assert.throws(() => { - psbt.signInput(f.inputToCheck, keyPair) - }, new RegExp(f.errorMessage)) - }) - }) + psbt.signInput(f.inputToCheck, keyPair); + }, new RegExp(f.errorMessage)); + }); + }); fixtures.bip174.creator.forEach(f => { it('Creates expected PSBT', () => { - const psbt = new Psbt() + const psbt = new Psbt(); for (const input of f.inputs) { - psbt.addInput(input) + psbt.addInput(input); } for (const output of f.outputs) { const script = Buffer.from(output.script, 'hex'); - psbt.addOutput({...output, script}) + psbt.addOutput({ ...output, script }); } - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); fixtures.bip174.updater.forEach(f => { it('Updates PSBT to the expected result', () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); for (const inputOrOutput of ['input', 'output']) { - const fixtureData = f[`${inputOrOutput}Data`] + const fixtureData = (f as any)[`${inputOrOutput}Data`]; if (fixtureData) { for (const [i, data] of fixtureData.entries()) { - const txt = upperCaseFirstLetter(inputOrOutput) - psbt[`update${txt}`](i, data) + const txt = upperCaseFirstLetter(inputOrOutput); + (psbt as any)[`update${txt}`](i, data); } } } - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); fixtures.bip174.signer.forEach(f => { it('Signs PSBT to the expected result', () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); - f.keys.forEach(({inputToSign, WIF}) => { + f.keys.forEach(({ inputToSign, WIF }) => { const keyPair = ECPair.fromWIF(WIF, NETWORKS.testnet); psbt.signInput(inputToSign, keyPair); - }) + }); - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); fixtures.bip174.combiner.forEach(f => { it('Combines two PSBTs to the expected result', () => { - const psbts = f.psbts.map(psbt => Psbt.fromBase64(psbt)) + const psbts = f.psbts.map(psbt => Psbt.fromBase64(psbt)); - psbts[0].combine(psbts[1]) + psbts[0].combine(psbts[1]); // Produces a different Base64 string due to implemetation specific key-value ordering. // That means this test will fail: // assert.strictEqual(psbts[0].toBase64(), f.result) // However, if we compare the actual PSBT properties we can see they are logically identical: - assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result)) - }) - }) + assert.deepStrictEqual(psbts[0], Psbt.fromBase64(f.result)); + }); + }); fixtures.bip174.finalizer.forEach(f => { - it("Finalizes inputs and gives the expected PSBT", () => { - const psbt = Psbt.fromBase64(f.psbt) + it('Finalizes inputs and gives the expected PSBT', () => { + const psbt = Psbt.fromBase64(f.psbt); - psbt.finalizeAllInputs() + psbt.finalizeAllInputs(); - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); fixtures.bip174.extractor.forEach(f => { it('Extracts the expected transaction from a PSBT', () => { - const psbt1 = Psbt.fromBase64(f.psbt) - const transaction1 = psbt1.extractTransaction(true).toHex() + const psbt1 = Psbt.fromBase64(f.psbt); + const transaction1 = psbt1.extractTransaction(true).toHex(); - const psbt2 = Psbt.fromBase64(f.psbt) - const transaction2 = psbt2.extractTransaction().toHex() + const psbt2 = Psbt.fromBase64(f.psbt); + const transaction2 = psbt2.extractTransaction().toHex(); - assert.strictEqual(transaction1, transaction2) - assert.strictEqual(transaction1, f.transaction) + assert.strictEqual(transaction1, transaction2); + assert.strictEqual(transaction1, f.transaction); - const psbt3 = Psbt.fromBase64(f.psbt) - delete psbt3.data.inputs[0].finalScriptSig - delete psbt3.data.inputs[0].finalScriptWitness + const psbt3 = Psbt.fromBase64(f.psbt); + delete psbt3.data.inputs[0].finalScriptSig; + delete psbt3.data.inputs[0].finalScriptWitness; assert.throws(() => { - psbt3.extractTransaction() - }, new RegExp('Not finalized')) + psbt3.extractTransaction(); + }, new RegExp('Not finalized')); - const psbt4 = Psbt.fromBase64(f.psbt) - psbt4.setMaximumFeeRate(1) + const psbt4 = Psbt.fromBase64(f.psbt); + psbt4.setMaximumFeeRate(1); assert.throws(() => { - psbt4.extractTransaction() - }, new RegExp('Warning: You are paying around [\\d.]+ in fees')) + psbt4.extractTransaction(); + }, new RegExp('Warning: You are paying around [\\d.]+ in fees')); - const psbt5 = Psbt.fromBase64(f.psbt) - psbt5.extractTransaction(true) - const fr1 = psbt5.getFeeRate() - const fr2 = psbt5.getFeeRate() - assert.strictEqual(fr1, fr2) + const psbt5 = Psbt.fromBase64(f.psbt); + psbt5.extractTransaction(true); + const fr1 = psbt5.getFeeRate(); + const fr2 = psbt5.getFeeRate(); + assert.strictEqual(fr1, fr2); - const psbt6 = Psbt.fromBase64(f.psbt) - const f1 = psbt6.getFee() - const f2 = psbt6.getFee() - assert.strictEqual(f1, f2) - }) - }) - }) + const psbt6 = Psbt.fromBase64(f.psbt); + const f1 = psbt6.getFee(); + const f2 = psbt6.getFee(); + assert.strictEqual(f1, f2); + }); + }); + }); describe('signInputAsync', () => { fixtures.signInput.checks.forEach(f => { it(f.description, async () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotReject(async () => { await psbtThatShouldsign.signInputAsync( f.shouldSign.inputToCheck, ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, - ) - }) + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.rejects(async () => { await psbtThatShouldThrow.signInputAsync( f.shouldThrow.inputToCheck, ECPair.fromWIF(f.shouldThrow.WIF), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp(f.shouldThrow.errorMessage)) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp(f.shouldThrow.errorMessage)); assert.rejects(async () => { - await psbtThatShouldThrow.signInputAsync( + await (psbtThatShouldThrow.signInputAsync as any)( f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need Signer to sign input')) + ); + }, new RegExp('Need Signer to sign input')); } - }) - }) - }) + }); + }); + }); describe('signInput', () => { fixtures.signInput.checks.forEach(f => { it(f.description, () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotThrow(() => { psbtThatShouldsign.signInput( f.shouldSign.inputToCheck, ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, - ) - }) + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.throws(() => { psbtThatShouldThrow.signInput( f.shouldThrow.inputToCheck, ECPair.fromWIF(f.shouldThrow.WIF), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp(f.shouldThrow.errorMessage)) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp(f.shouldThrow.errorMessage)); assert.throws(() => { - psbtThatShouldThrow.signInput( - f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need Signer to sign input')) + (psbtThatShouldThrow.signInput as any)(f.shouldThrow.inputToCheck); + }, new RegExp('Need Signer to sign input')); } - }) - }) - }) + }); + }); + }); describe('signAllInputsAsync', () => { fixtures.signInput.checks.forEach(f => { - if (f.description === 'checks the input exists') return + if (f.description === 'checks the input exists') return; it(f.description, async () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotReject(async () => { await psbtThatShouldsign.signAllInputsAsync( ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, - ) - }) + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.rejects(async () => { await psbtThatShouldThrow.signAllInputsAsync( ECPair.fromWIF(f.shouldThrow.WIF), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp('No inputs were signed')) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp('No inputs were signed')); assert.rejects(async () => { - await psbtThatShouldThrow.signAllInputsAsync() - }, new RegExp('Need Signer to sign input')) + await (psbtThatShouldThrow.signAllInputsAsync as any)(); + }, new RegExp('Need Signer to sign input')); } - }) - }) - }) + }); + }); + }); describe('signAllInputs', () => { fixtures.signInput.checks.forEach(f => { - if (f.description === 'checks the input exists') return + if (f.description === 'checks the input exists') return; it(f.description, () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotThrow(() => { psbtThatShouldsign.signAllInputs( ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, - ) - }) + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.throws(() => { psbtThatShouldThrow.signAllInputs( ECPair.fromWIF(f.shouldThrow.WIF), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp('No inputs were signed')) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp('No inputs were signed')); assert.throws(() => { - psbtThatShouldThrow.signAllInputs() - }, new RegExp('Need Signer to sign input')) + (psbtThatShouldThrow.signAllInputs as any)(); + }, new RegExp('Need Signer to sign input')); } - }) - }) - }) + }); + }); + }); describe('signInputHDAsync', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, async () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotReject(async () => { await psbtThatShouldsign.signInputHDAsync( f.shouldSign.inputToCheck, bip32.fromBase58(f.shouldSign.xprv), - f.shouldSign.sighashTypes || undefined, - ) - }) + (f.shouldSign as any).sighashTypes || undefined, + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.rejects(async () => { await psbtThatShouldThrow.signInputHDAsync( f.shouldThrow.inputToCheck, bip32.fromBase58(f.shouldThrow.xprv), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp(f.shouldThrow.errorMessage)) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp(f.shouldThrow.errorMessage)); assert.rejects(async () => { - await psbtThatShouldThrow.signInputHDAsync( + await (psbtThatShouldThrow.signInputHDAsync as any)( f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need HDSigner to sign input')) + ); + }, new RegExp('Need HDSigner to sign input')); } - }) - }) - }) + }); + }); + }); describe('signInputHD', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotThrow(() => { psbtThatShouldsign.signInputHD( f.shouldSign.inputToCheck, bip32.fromBase58(f.shouldSign.xprv), - f.shouldSign.sighashTypes || undefined, - ) - }) + (f.shouldSign as any).sighashTypes || undefined, + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.throws(() => { psbtThatShouldThrow.signInputHD( f.shouldThrow.inputToCheck, bip32.fromBase58(f.shouldThrow.xprv), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp(f.shouldThrow.errorMessage)) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp(f.shouldThrow.errorMessage)); assert.throws(() => { - psbtThatShouldThrow.signInputHD( + (psbtThatShouldThrow.signInputHD as any)( f.shouldThrow.inputToCheck, - ) - }, new RegExp('Need HDSigner to sign input')) + ); + }, new RegExp('Need HDSigner to sign input')); } - }) - }) - }) + }); + }); + }); describe('signAllInputsHDAsync', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, async () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotReject(async () => { await psbtThatShouldsign.signAllInputsHDAsync( bip32.fromBase58(f.shouldSign.xprv), - f.shouldSign.sighashTypes || undefined, - ) - }) + (f.shouldSign as any).sighashTypes || undefined, + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.rejects(async () => { await psbtThatShouldThrow.signAllInputsHDAsync( bip32.fromBase58(f.shouldThrow.xprv), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp('No inputs were signed')) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp('No inputs were signed')); assert.rejects(async () => { - await psbtThatShouldThrow.signAllInputsHDAsync() - }, new RegExp('Need HDSigner to sign input')) + await (psbtThatShouldThrow.signAllInputsHDAsync as any)(); + }, new RegExp('Need HDSigner to sign input')); } - }) - }) - }) + }); + }); + }); describe('signAllInputsHD', () => { fixtures.signInputHD.checks.forEach(f => { it(f.description, () => { if (f.shouldSign) { - const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt) + const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); assert.doesNotThrow(() => { psbtThatShouldsign.signAllInputsHD( bip32.fromBase58(f.shouldSign.xprv), - f.shouldSign.sighashTypes || undefined, - ) - }) + (f.shouldSign as any).sighashTypes || undefined, + ); + }); } if (f.shouldThrow) { - const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt) + const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); assert.throws(() => { psbtThatShouldThrow.signAllInputsHD( bip32.fromBase58(f.shouldThrow.xprv), - f.shouldThrow.sighashTypes || undefined, - ) - }, new RegExp('No inputs were signed')) + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp('No inputs were signed')); assert.throws(() => { - psbtThatShouldThrow.signAllInputsHD() - }, new RegExp('Need HDSigner to sign input')) + (psbtThatShouldThrow.signAllInputsHD as any)(); + }, new RegExp('Need HDSigner to sign input')); } - }) - }) - }) + }); + }); + }); describe('finalizeAllInputs', () => { fixtures.finalizeAllInputs.forEach(f => { it(`Finalizes inputs of type "${f.type}"`, () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); - psbt.finalizeAllInputs() + psbt.finalizeAllInputs(); - assert.strictEqual(psbt.toBase64(), f.result) - }) - }) + assert.strictEqual(psbt.toBase64(), f.result); + }); + }); it('fails if no script found', () => { - const psbt = new Psbt() + const psbt = new Psbt(); psbt.addInput({ - hash: '0000000000000000000000000000000000000000000000000000000000000000', - index: 0 - }) + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, + }); assert.throws(() => { - psbt.finalizeAllInputs() - }, new RegExp('No script found for input #0')) + psbt.finalizeAllInputs(); + }, new RegExp('No script found for input #0')); psbt.updateInput(0, { witnessUtxo: { - script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d', 'hex'), - value: 2e5 - } - }) + script: Buffer.from( + '0014d85c2b71d0060b09c9886aeb815e50991dda124d', + 'hex', + ), + value: 2e5, + }, + }); assert.throws(() => { - psbt.finalizeAllInputs() - }, new RegExp('Can not finalize input #0')) - }) - }) + psbt.finalizeAllInputs(); + }, new RegExp('Can not finalize input #0')); + }); + }); describe('addInput', () => { fixtures.addInput.checks.forEach(f => { it(f.description, () => { - const psbt = new Psbt() + const psbt = new Psbt(); if (f.exception) { assert.throws(() => { - psbt.addInput(f.inputData) - }, new RegExp(f.exception)) + psbt.addInput(f.inputData as any); + }, new RegExp(f.exception)); assert.throws(() => { - psbt.addInputs([f.inputData]) - }, new RegExp(f.exception)) + psbt.addInputs([f.inputData as any]); + }, new RegExp(f.exception)); } else { assert.doesNotThrow(() => { - psbt.addInputs([f.inputData]) + psbt.addInputs([f.inputData as any]); if (f.equals) { - assert.strictEqual(psbt.toBase64(), f.equals) + assert.strictEqual(psbt.toBase64(), f.equals); } - }) + }); assert.throws(() => { - psbt.addInput(f.inputData) - }, new RegExp('Duplicate input detected.')) + psbt.addInput(f.inputData as any); + }, new RegExp('Duplicate input detected.')); } - }) - }) - }) + }); + }); + }); describe('addOutput', () => { fixtures.addOutput.checks.forEach(f => { it(f.description, () => { - const psbt = new Psbt() + const psbt = new Psbt(); if (f.exception) { assert.throws(() => { - psbt.addOutput(f.outputData) - }, new RegExp(f.exception)) + psbt.addOutput(f.outputData as any); + }, new RegExp(f.exception)); assert.throws(() => { - psbt.addOutputs([f.outputData]) - }, new RegExp(f.exception)) + psbt.addOutputs([f.outputData as any]); + }, new RegExp(f.exception)); } else { assert.doesNotThrow(() => { - psbt.addOutput(f.outputData) - }) + psbt.addOutput(f.outputData as any); + }); assert.doesNotThrow(() => { - psbt.addOutputs([f.outputData]) - }) + psbt.addOutputs([f.outputData as any]); + }); } - }) - }) - }) + }); + }); + }); describe('setVersion', () => { it('Sets the version value of the unsigned transaction', () => { - const psbt = new Psbt() + const psbt = new Psbt(); - assert.strictEqual(psbt.extractTransaction().version, 2) - psbt.setVersion(1) - assert.strictEqual(psbt.extractTransaction().version, 1) - }) - }) + assert.strictEqual(psbt.extractTransaction().version, 2); + psbt.setVersion(1); + assert.strictEqual(psbt.extractTransaction().version, 1); + }); + }); describe('setLocktime', () => { it('Sets the nLockTime value of the unsigned transaction', () => { - const psbt = new Psbt() + const psbt = new Psbt(); - assert.strictEqual(psbt.extractTransaction().locktime, 0) - psbt.setLocktime(1) - assert.strictEqual(psbt.extractTransaction().locktime, 1) - }) - }) + assert.strictEqual(psbt.extractTransaction().locktime, 0); + psbt.setLocktime(1); + assert.strictEqual(psbt.extractTransaction().locktime, 1); + }); + }); describe('setInputSequence', () => { it('Sets the sequence number for a given input', () => { - const psbt = new Psbt() + const psbt = new Psbt(); psbt.addInput({ - hash: '0000000000000000000000000000000000000000000000000000000000000000', - index: 0 + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, }); - assert.strictEqual(psbt.inputCount, 1) - assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0xffffffff) - psbt.setInputSequence(0, 0) - assert.strictEqual(psbt.__CACHE.__TX.ins[0].sequence, 0) - }) + assert.strictEqual(psbt.inputCount, 1); + assert.strictEqual( + (psbt as any).__CACHE.__TX.ins[0].sequence, + 0xffffffff, + ); + psbt.setInputSequence(0, 0); + assert.strictEqual((psbt as any).__CACHE.__TX.ins[0].sequence, 0); + }); it('throws if input index is too high', () => { - const psbt = new Psbt() + const psbt = new Psbt(); psbt.addInput({ - hash: '0000000000000000000000000000000000000000000000000000000000000000', - index: 0 + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, }); assert.throws(() => { - psbt.setInputSequence(1, 0) - }, new RegExp('Input index too high')) - }) - }) + psbt.setInputSequence(1, 0); + }, new RegExp('Input index too high')); + }); + }); describe('clone', () => { it('Should clone a psbt exactly with no reference', () => { - const f = fixtures.clone - const psbt = Psbt.fromBase64(f.psbt) - const notAClone = Object.assign(new Psbt(), psbt) // references still active - const clone = psbt.clone() + const f = fixtures.clone; + const psbt = Psbt.fromBase64(f.psbt); + const notAClone = Object.assign(new Psbt(), psbt); // references still active + const clone = psbt.clone(); - assert.strictEqual(psbt.validateSignaturesOfAllInputs(), true) + assert.strictEqual(psbt.validateSignaturesOfAllInputs(), true); - assert.strictEqual(clone.toBase64(), psbt.toBase64()) - assert.strictEqual(clone.toBase64(), notAClone.toBase64()) - assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) - psbt.__CACHE.__TX.version |= 0xff0000 - assert.notStrictEqual(clone.toBase64(), psbt.toBase64()) - assert.notStrictEqual(clone.toBase64(), notAClone.toBase64()) - assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) - }) - }) + assert.strictEqual(clone.toBase64(), psbt.toBase64()); + assert.strictEqual(clone.toBase64(), notAClone.toBase64()); + assert.strictEqual(psbt.toBase64(), notAClone.toBase64()); + (psbt as any).__CACHE.__TX.version |= 0xff0000; + assert.notStrictEqual(clone.toBase64(), psbt.toBase64()); + assert.notStrictEqual(clone.toBase64(), notAClone.toBase64()); + assert.strictEqual(psbt.toBase64(), notAClone.toBase64()); + }); + }); describe('setMaximumFeeRate', () => { it('Sets the maximumFeeRate value', () => { - const psbt = new Psbt() + const psbt = new Psbt(); - assert.strictEqual(psbt.opts.maximumFeeRate, 5000) - psbt.setMaximumFeeRate(6000) - assert.strictEqual(psbt.opts.maximumFeeRate, 6000) - }) - }) + assert.strictEqual((psbt as any).opts.maximumFeeRate, 5000); + psbt.setMaximumFeeRate(6000); + assert.strictEqual((psbt as any).opts.maximumFeeRate, 6000); + }); + }); describe('validateSignaturesOfInput', () => { - const f = fixtures.validateSignaturesOfInput + const f = fixtures.validateSignaturesOfInput; it('Correctly validates a signature', () => { - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); - assert.strictEqual(psbt.validateSignaturesOfInput(f.index), true) + assert.strictEqual(psbt.validateSignaturesOfInput(f.index), true); assert.throws(() => { - psbt.validateSignaturesOfInput(f.nonExistantIndex) - }, new RegExp('No signatures to validate')) - }) + psbt.validateSignaturesOfInput(f.nonExistantIndex); + }, new RegExp('No signatures to validate')); + }); it('Correctly validates a signature against a pubkey', () => { - const psbt = Psbt.fromBase64(f.psbt) - assert.strictEqual(psbt.validateSignaturesOfInput(f.index, f.pubkey), true) + const psbt = Psbt.fromBase64(f.psbt); + assert.strictEqual( + psbt.validateSignaturesOfInput(f.index, f.pubkey as any), + true, + ); assert.throws(() => { - psbt.validateSignaturesOfInput(f.index, f.incorrectPubkey) - }, new RegExp('No signatures for this pubkey')) - }) - }) + psbt.validateSignaturesOfInput(f.index, f.incorrectPubkey as any); + }, new RegExp('No signatures for this pubkey')); + }); + }); describe('getFeeRate', () => { it('Throws error if called before inputs are finalized', () => { - const f = fixtures.getFeeRate - const psbt = Psbt.fromBase64(f.psbt) + const f = fixtures.getFeeRate; + const psbt = Psbt.fromBase64(f.psbt); assert.throws(() => { - psbt.getFeeRate() - }, new RegExp('PSBT must be finalized to calculate fee rate')) + psbt.getFeeRate(); + }, new RegExp('PSBT must be finalized to calculate fee rate')); - psbt.finalizeAllInputs() + psbt.finalizeAllInputs(); - assert.strictEqual(psbt.getFeeRate(), f.fee) - psbt.__CACHE.__FEE_RATE = undefined - assert.strictEqual(psbt.getFeeRate(), f.fee) - }) - }) + assert.strictEqual(psbt.getFeeRate(), f.fee); + (psbt as any).__CACHE.__FEE_RATE = undefined; + assert.strictEqual(psbt.getFeeRate(), f.fee); + }); + }); describe('create 1-to-1 transaction', () => { - const alice = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr') - const psbt = new Psbt() + const alice = ECPair.fromWIF( + 'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr', + ); + const psbt = new Psbt(); psbt.addInput({ hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e', index: 0, nonWitnessUtxo: Buffer.from( '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' + - '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + - 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + - '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + - '631e5e1e66009ce3710ceea5b1ad13ffffffff01905f0100000000001976a9148bb' + - 'c95d2709c71607c60ee3f097c1217482f518d88ac00000000', + '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + + 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + + '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + + '631e5e1e66009ce3710ceea5b1ad13ffffffff01905f0100000000001976a9148bb' + + 'c95d2709c71607c60ee3f097c1217482f518d88ac00000000', 'hex', ), sighashType: 1, - }) + }); psbt.addOutput({ address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', - value: 80000 - }) - psbt.signInput(0, alice) + value: 80000, + }); + psbt.signInput(0, alice); assert.throws(() => { - psbt.setVersion(3) - }, new RegExp('Can not modify transaction, signatures exist.')) - psbt.validateSignaturesOfInput(0) - psbt.finalizeAllInputs() + psbt.setVersion(3); + }, new RegExp('Can not modify transaction, signatures exist.')); + psbt.validateSignaturesOfInput(0); + psbt.finalizeAllInputs(); assert.throws(() => { - psbt.setVersion(3) - }, new RegExp('Can not modify transaction, signatures exist.')) + psbt.setVersion(3); + }, new RegExp('Can not modify transaction, signatures exist.')); assert.strictEqual( psbt.extractTransaction().toHex(), '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + - 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + - 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + - '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + - 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + - '08a22724efa6f6a07b0ec4c79aa88ac00000000', - ) - }) + 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + + 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + + '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + + 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + + '08a22724efa6f6a07b0ec4c79aa88ac00000000', + ); + }); describe('Method return types', () => { it('fromBuffer returns Psbt type (not base class)', () => { - const psbt = Psbt.fromBuffer(Buffer.from( - '70736274ff01000a01000000000000000000000000', 'hex' //cHNidP8BAAoBAAAAAAAAAAAAAAAA - )); + const psbt = Psbt.fromBuffer( + Buffer.from( + '70736274ff01000a01000000000000000000000000', + 'hex', //cHNidP8BAAoBAAAAAAAAAAAAAAAA + ), + ); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__CACHE.__TX); - }) + assert.ok((psbt as any).__CACHE.__TX); + }); it('fromBase64 returns Psbt type (not base class)', () => { const psbt = Psbt.fromBase64('cHNidP8BAAoBAAAAAAAAAAAAAAAA'); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__CACHE.__TX); - }) + assert.ok((psbt as any).__CACHE.__TX); + }); it('fromHex returns Psbt type (not base class)', () => { const psbt = Psbt.fromHex('70736274ff01000a01000000000000000000000000'); assert.strictEqual(psbt instanceof Psbt, true); - assert.ok(psbt.__CACHE.__TX); - }) - }) + assert.ok((psbt as any).__CACHE.__TX); + }); + }); describe('Cache', () => { it('non-witness UTXOs are cached', () => { const f = fixtures.cache.nonWitnessUtxo; - const psbt = Psbt.fromBase64(f.psbt) + const psbt = Psbt.fromBase64(f.psbt); const index = f.inputIndex; // Cache is empty - assert.strictEqual(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index], undefined) + assert.strictEqual( + (psbt as any).__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index], + undefined, + ); // Cache is populated - psbt.updateInput(index, { nonWitnessUtxo: f.nonWitnessUtxo }) - const value = psbt.data.inputs[index].nonWitnessUtxo - assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value)) - assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo)) + psbt.updateInput(index, { nonWitnessUtxo: f.nonWitnessUtxo as any }); + const value = psbt.data.inputs[index].nonWitnessUtxo; + assert.ok( + (psbt as any).__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value), + ); + assert.ok( + (psbt as any).__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals( + f.nonWitnessUtxo, + ), + ); // Cache is rebuilt from internal transaction object when cleared - psbt.data.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3]) - psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined - assert.ok(psbt.data.inputs[index].nonWitnessUtxo.equals(value)) - }) - }) -}) + psbt.data.inputs[index].nonWitnessUtxo = Buffer.from([1, 2, 3]); + (psbt as any).__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined; + assert.ok((psbt as any).data.inputs[index].nonWitnessUtxo.equals(value)); + }); + }); +}); diff --git a/test/script.spec.ts b/test/script.spec.ts index d003558..23da986 100644 --- a/test/script.spec.ts +++ b/test/script.spec.ts @@ -1,157 +1,176 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bscript = require('../src/script') -const minimalData = require('minimaldata') - -const fixtures = require('./fixtures/script.json') -const fixtures2 = require('./fixtures/templates.json') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as bscript from '../src/script'; +import * as fixtures from './fixtures/script.json'; +import * as fixtures2 from './fixtures/templates.json'; +const minimalData = require('minimaldata'); describe('script', () => { // TODO describe('isCanonicalPubKey', () => { it('rejects if not provided a Buffer', () => { - assert.strictEqual(false, bscript.isCanonicalPubKey(0)) - }) + assert.strictEqual(false, bscript.isCanonicalPubKey(0 as any)); + }); it('rejects smaller than 33', () => { for (var i = 0; i < 33; i++) { - assert.strictEqual(false, bscript.isCanonicalPubKey(Buffer.from('', i))) + assert.strictEqual( + false, + bscript.isCanonicalPubKey(Buffer.allocUnsafe(i)), + ); } - }) - }) - describe.skip('isCanonicalScriptSignature', () => { - }) + }); + }); + describe.skip('isCanonicalScriptSignature', () => {}); describe('fromASM/toASM', () => { fixtures.valid.forEach(f => { it('encodes/decodes ' + f.asm, () => { - const script = bscript.fromASM(f.asm) - assert.strictEqual(bscript.toASM(script), f.asm) - }) - }) + const script = bscript.fromASM(f.asm); + assert.strictEqual(bscript.toASM(script), f.asm); + }); + }); fixtures.invalid.fromASM.forEach(f => { it('throws ' + f.description, () => { assert.throws(() => { - bscript.fromASM(f.script) - }, new RegExp(f.description)) - }) - }) - }) + bscript.fromASM(f.script); + }, new RegExp(f.description)); + }); + }); + }); describe('fromASM/toASM (templates)', () => { fixtures2.valid.forEach(f => { if (f.inputHex) { - const ih = bscript.toASM(Buffer.from(f.inputHex, 'hex')) + const ih = bscript.toASM(Buffer.from(f.inputHex, 'hex')); it('encodes/decodes ' + ih, () => { - const script = bscript.fromASM(f.input) - assert.strictEqual(script.toString('hex'), f.inputHex) - assert.strictEqual(bscript.toASM(script), f.input) - }) + const script = bscript.fromASM(f.input); + assert.strictEqual(script.toString('hex'), f.inputHex); + assert.strictEqual(bscript.toASM(script), f.input); + }); } if (f.outputHex) { it('encodes/decodes ' + f.output, () => { - const script = bscript.fromASM(f.output) - assert.strictEqual(script.toString('hex'), f.outputHex) - assert.strictEqual(bscript.toASM(script), f.output) - }) + const script = bscript.fromASM(f.output); + assert.strictEqual(script.toString('hex'), f.outputHex); + assert.strictEqual(bscript.toASM(script), f.output); + }); } - }) - }) + }); + }); describe('isPushOnly', () => { fixtures.valid.forEach(f => { it('returns ' + !!f.stack + ' for ' + f.asm, () => { - const script = bscript.fromASM(f.asm) - const chunks = bscript.decompile(script) + const script = bscript.fromASM(f.asm); + const chunks = bscript.decompile(script); - assert.strictEqual(bscript.isPushOnly(chunks), !!f.stack) - }) - }) - }) + assert.strictEqual(bscript.isPushOnly(chunks!), !!f.stack); + }); + }); + }); describe('toStack', () => { fixtures.valid.forEach(f => { it('returns ' + !!f.stack + ' for ' + f.asm, () => { - if (!f.stack || !f.asm) return + if (!f.stack || !f.asm) return; - const script = bscript.fromASM(f.asm) + const script = bscript.fromASM(f.asm); - const stack = bscript.toStack(script) - assert.deepStrictEqual(stack.map(x => { - return x.toString('hex') - }), f.stack) + const stack = bscript.toStack(script); + assert.deepStrictEqual( + stack.map(x => { + return x.toString('hex'); + }), + f.stack, + ); - assert.strictEqual(bscript.toASM(bscript.compile(stack)), f.asm, 'should rebuild same script from stack') - }) - }) - }) + assert.strictEqual( + bscript.toASM(bscript.compile(stack)), + f.asm, + 'should rebuild same script from stack', + ); + }); + }); + }); describe('compile (via fromASM)', () => { fixtures.valid.forEach(f => { - it('(' + f.type + ') compiles ' + f.asm, () => { - const scriptSig = bscript.fromASM(f.asm) + it('compiles ' + f.asm, () => { + const scriptSig = bscript.fromASM(f.asm); - assert.strictEqual(scriptSig.toString('hex'), f.script) + assert.strictEqual(scriptSig.toString('hex'), f.script); if (f.nonstandard) { - const scriptSigNS = bscript.fromASM(f.nonstandard.scriptSig) + const scriptSigNS = bscript.fromASM(f.nonstandard.scriptSig); - assert.strictEqual(scriptSigNS.toString('hex'), f.script) + assert.strictEqual(scriptSigNS.toString('hex'), f.script); } - }) - }) - }) + }); + }); + }); describe('decompile', () => { fixtures.valid.forEach(f => { it('decompiles ' + f.asm, () => { - const chunks = bscript.decompile(Buffer.from(f.script, 'hex')) + const chunks = bscript.decompile(Buffer.from(f.script, 'hex')); - assert.strictEqual(bscript.compile(chunks).toString('hex'), f.script) - assert.strictEqual(bscript.toASM(chunks), f.asm) + assert.strictEqual(bscript.compile(chunks!).toString('hex'), f.script); + assert.strictEqual(bscript.toASM(chunks!), f.asm); if (f.nonstandard) { - const chunksNS = bscript.decompile(Buffer.from(f.nonstandard.scriptSigHex, 'hex')) + const chunksNS = bscript.decompile( + Buffer.from(f.nonstandard.scriptSigHex, 'hex'), + ); - assert.strictEqual(bscript.compile(chunksNS).toString('hex'), f.script) + assert.strictEqual( + bscript.compile(chunksNS!).toString('hex'), + f.script, + ); // toASM converts verbatim, only `compile` transforms the script to a minimalpush compliant script - assert.strictEqual(bscript.toASM(chunksNS), f.nonstandard.scriptSig) + assert.strictEqual(bscript.toASM(chunksNS!), f.nonstandard.scriptSig); } - }) - }) + }); + }); fixtures.invalid.decompile.forEach(f => { - it('fails to decompile ' + f.script + ', because "' + f.description + '"', () => { - const chunks = bscript.decompile(Buffer.from(f.script, 'hex')) + it( + 'fails to decompile ' + f.script + ', because "' + f.description + '"', + () => { + const chunks = bscript.decompile(Buffer.from(f.script, 'hex')); - assert.strictEqual(chunks, null) - }) - }) - }) + assert.strictEqual(chunks, null); + }, + ); + }); + }); describe('SCRIPT_VERIFY_MINIMALDATA policy', () => { fixtures.valid.forEach(f => { - it('compliant for ' + f.type + ' scriptSig ' + f.asm, () => { - const script = Buffer.from(f.script, 'hex') + it('compliant for scriptSig ' + f.asm, () => { + const script = Buffer.from(f.script, 'hex'); - assert(minimalData(script)) - }) - }) + assert(minimalData(script)); + }); + }); - function testEncodingForSize (i) { + function testEncodingForSize(i: number) { it('compliant for data PUSH of length ' + i, () => { - const buffer = Buffer.alloc(i) - const script = bscript.compile([buffer]) + const buffer = Buffer.alloc(i); + const script = bscript.compile([buffer]); - assert(minimalData(script), 'Failed for ' + i + ' length script: ' + script.toString('hex')) - }) + assert( + minimalData(script), + 'Failed for ' + i + ' length script: ' + script.toString('hex'), + ); + }); } for (var i = 0; i < 520; ++i) { - testEncodingForSize(i) + testEncodingForSize(i); } - }) -}) + }); +}); diff --git a/test/script_number.spec.ts b/test/script_number.spec.ts index d5b97e2..3b05082 100644 --- a/test/script_number.spec.ts +++ b/test/script_number.spec.ts @@ -1,26 +1,26 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const scriptNumber = require('../src/script_number') -const fixtures = require('./fixtures/script_number.json') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as scriptNumber from '../src/script_number'; +import * as fixtures from './fixtures/script_number.json'; describe('script-number', () => { describe('decode', () => { fixtures.forEach(f => { it(f.hex + ' returns ' + f.number, () => { - const actual = scriptNumber.decode(Buffer.from(f.hex, 'hex'), f.bytes) + const actual = scriptNumber.decode(Buffer.from(f.hex, 'hex'), f.bytes); - assert.strictEqual(actual, f.number) - }) - }) - }) + assert.strictEqual(actual, f.number); + }); + }); + }); describe('encode', () => { fixtures.forEach(f => { it(f.number + ' returns ' + f.hex, () => { - const actual = scriptNumber.encode(f.number) + const actual = scriptNumber.encode(f.number); - assert.strictEqual(actual.toString('hex'), f.hex) - }) - }) - }) -}) + assert.strictEqual(actual.toString('hex'), f.hex); + }); + }); + }); +}); diff --git a/test/script_signature.spec.ts b/test/script_signature.spec.ts index 6888ca5..3be52ad 100644 --- a/test/script_signature.spec.ts +++ b/test/script_signature.spec.ts @@ -1,64 +1,63 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const bscriptSig = require('../src/script').signature -const Buffer = require('safe-buffer').Buffer -const fixtures = require('./fixtures/signature.json') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import { signature as bscriptSig } from '../src/script'; +import * as fixtures from './fixtures/signature.json'; describe('Script Signatures', () => { - function fromRaw (signature) { - return Buffer.concat([ - Buffer.from(signature.r, 'hex'), - Buffer.from(signature.s, 'hex') - ], 64) + function fromRaw(signature: { r: string; s: string }) { + return Buffer.concat( + [Buffer.from(signature.r, 'hex'), Buffer.from(signature.s, 'hex')], + 64, + ); } - function toRaw (signature) { + function toRaw(signature: Buffer) { return { r: signature.slice(0, 32).toString('hex'), - s: signature.slice(32, 64).toString('hex') - } + s: signature.slice(32, 64).toString('hex'), + }; } describe('encode', () => { fixtures.valid.forEach(f => { it('encodes ' + f.hex, () => { - const buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType) + const buffer = bscriptSig.encode(fromRaw(f.raw), f.hashType); - assert.strictEqual(buffer.toString('hex'), f.hex) - }) - }) + assert.strictEqual(buffer.toString('hex'), f.hex); + }); + }); fixtures.invalid.forEach(f => { - if (!f.raw) return + if (!f.raw) return; it('throws ' + f.exception, () => { - const signature = fromRaw(f.raw) + const signature = fromRaw(f.raw); assert.throws(() => { - bscriptSig.encode(signature, f.hashType) - }, new RegExp(f.exception)) - }) - }) - }) + bscriptSig.encode(signature, f.hashType); + }, new RegExp(f.exception)); + }); + }); + }); describe('decode', () => { fixtures.valid.forEach(f => { it('decodes ' + f.hex, () => { - const decode = bscriptSig.decode(Buffer.from(f.hex, 'hex')) + const decode = bscriptSig.decode(Buffer.from(f.hex, 'hex')); - assert.deepStrictEqual(toRaw(decode.signature), f.raw) - assert.strictEqual(decode.hashType, f.hashType) - }) - }) + assert.deepStrictEqual(toRaw(decode.signature), f.raw); + assert.strictEqual(decode.hashType, f.hashType); + }); + }); fixtures.invalid.forEach(f => { it('throws on ' + f.hex, () => { - const buffer = Buffer.from(f.hex, 'hex') + const buffer = Buffer.from(f.hex, 'hex'); assert.throws(() => { - bscriptSig.decode(buffer) - }, new RegExp(f.exception)) - }) - }) - }) -}) + bscriptSig.decode(buffer); + }, new RegExp(f.exception)); + }); + }); + }); +}); diff --git a/test/transaction.spec.ts b/test/transaction.spec.ts index 3fa9243..c87d9e6 100644 --- a/test/transaction.spec.ts +++ b/test/transaction.spec.ts @@ -1,288 +1,338 @@ -const { describe, it, beforeEach } = require('mocha') -const assert = require('assert') -const bscript = require('../src/script') -const fixtures = require('./fixtures/transaction') -const Transaction = require('..').Transaction +import * as assert from 'assert'; +import { beforeEach, describe, it } from 'mocha'; +import { Transaction } from '..'; +import * as bscript from '../src/script'; +import * as fixtures from './fixtures/transaction.json'; describe('Transaction', () => { - function fromRaw (raw, noWitness) { - const tx = new Transaction() - tx.version = raw.version - tx.locktime = raw.locktime + function fromRaw(raw: any, noWitness?: boolean) { + const tx = new Transaction(); + tx.version = raw.version; + tx.locktime = raw.locktime; - raw.ins.forEach((txIn, i) => { - const txHash = Buffer.from(txIn.hash, 'hex') - let scriptSig + raw.ins.forEach((txIn: any, i: number) => { + const txHash = Buffer.from(txIn.hash, 'hex'); + let scriptSig; if (txIn.data) { - scriptSig = Buffer.from(txIn.data, 'hex') + scriptSig = Buffer.from(txIn.data, 'hex'); } else if (txIn.script) { - scriptSig = bscript.fromASM(txIn.script) + scriptSig = bscript.fromASM(txIn.script); } - tx.addInput(txHash, txIn.index, txIn.sequence, scriptSig) + tx.addInput(txHash, txIn.index, txIn.sequence, scriptSig); if (!noWitness && txIn.witness) { - const witness = txIn.witness.map(x => { - return Buffer.from(x, 'hex') - }) + const witness = txIn.witness.map((x: string) => { + return Buffer.from(x, 'hex'); + }); - tx.setWitness(i, witness) + tx.setWitness(i, witness); } - }) + }); - raw.outs.forEach(txOut => { - let script + raw.outs.forEach((txOut: any) => { + let script: Buffer; if (txOut.data) { - script = Buffer.from(txOut.data, 'hex') + script = Buffer.from(txOut.data, 'hex'); } else if (txOut.script) { - script = bscript.fromASM(txOut.script) + script = bscript.fromASM(txOut.script); } - tx.addOutput(script, txOut.value) - }) + tx.addOutput(script!, txOut.value); + }); - return tx + return tx; } describe('fromBuffer/fromHex', () => { - function importExport (f) { - const id = f.id || f.hash - const txHex = f.hex || f.txHex + function importExport(f: any) { + const id = f.id || f.hash; + const txHex = f.hex || f.txHex; it('imports ' + f.description + ' (' + id + ')', () => { - const actual = Transaction.fromHex(txHex) + const actual = Transaction.fromHex(txHex); - assert.strictEqual(actual.toHex(), txHex) - }) + assert.strictEqual(actual.toHex(), txHex); + }); if (f.whex) { it('imports ' + f.description + ' (' + id + ') as witness', () => { - const actual = Transaction.fromHex(f.whex) + const actual = Transaction.fromHex(f.whex); - assert.strictEqual(actual.toHex(), f.whex) - }) + assert.strictEqual(actual.toHex(), f.whex); + }); } } - fixtures.valid.forEach(importExport) - fixtures.hashForSignature.forEach(importExport) - fixtures.hashForWitnessV0.forEach(importExport) + fixtures.valid.forEach(importExport); + fixtures.hashForSignature.forEach(importExport); + fixtures.hashForWitnessV0.forEach(importExport); fixtures.invalid.fromBuffer.forEach(f => { it('throws on ' + f.exception, () => { assert.throws(() => { - Transaction.fromHex(f.hex) - }, new RegExp(f.exception)) - }) - }) + Transaction.fromHex(f.hex); + }, new RegExp(f.exception)); + }); + }); it('.version should be interpreted as an int32le', () => { - const txHex = 'ffffffff0000ffffffff' - const tx = Transaction.fromHex(txHex) - assert.strictEqual(-1, tx.version) - assert.strictEqual(0xffffffff, tx.locktime) - }) - }) + const txHex = 'ffffffff0000ffffffff'; + const tx = Transaction.fromHex(txHex); + assert.strictEqual(-1, tx.version); + assert.strictEqual(0xffffffff, tx.locktime); + }); + }); describe('toBuffer/toHex', () => { fixtures.valid.forEach(f => { it('exports ' + f.description + ' (' + f.id + ')', () => { - const actual = fromRaw(f.raw, true) - assert.strictEqual(actual.toHex(), f.hex) - }) + const actual = fromRaw(f.raw, true); + assert.strictEqual(actual.toHex(), f.hex); + }); if (f.whex) { it('exports ' + f.description + ' (' + f.id + ') as witness', () => { - const wactual = fromRaw(f.raw) - assert.strictEqual(wactual.toHex(), f.whex) - }) + const wactual = fromRaw(f.raw); + assert.strictEqual(wactual.toHex(), f.whex); + }); } - }) + }); it('accepts target Buffer and offset parameters', () => { - const f = fixtures.valid[0] - const actual = fromRaw(f.raw) - const byteLength = actual.byteLength() + const f = fixtures.valid[0]; + const actual = fromRaw(f.raw); + const byteLength = actual.byteLength(); - const target = Buffer.alloc(byteLength * 2) - const a = actual.toBuffer(target, 0) - const b = actual.toBuffer(target, byteLength) + const target = Buffer.alloc(byteLength * 2); + const a = actual.toBuffer(target, 0); + const b = actual.toBuffer(target, byteLength); - assert.strictEqual(a.length, byteLength) - assert.strictEqual(b.length, byteLength) - assert.strictEqual(a.toString('hex'), f.hex) - assert.strictEqual(b.toString('hex'), f.hex) - assert.deepStrictEqual(a, b) - assert.deepStrictEqual(a, target.slice(0, byteLength)) - assert.deepStrictEqual(b, target.slice(byteLength)) - }) - }) + assert.strictEqual(a.length, byteLength); + assert.strictEqual(b.length, byteLength); + assert.strictEqual(a.toString('hex'), f.hex); + assert.strictEqual(b.toString('hex'), f.hex); + assert.deepStrictEqual(a, b); + assert.deepStrictEqual(a, target.slice(0, byteLength)); + assert.deepStrictEqual(b, target.slice(byteLength)); + }); + }); describe('hasWitnesses', () => { fixtures.valid.forEach(f => { - it('detects if the transaction has witnesses: ' + (f.whex ? 'true' : 'false'), () => { - assert.strictEqual(Transaction.fromHex(f.whex ? f.whex : f.hex).hasWitnesses(), !!f.whex) - }) - }) - }) + it( + 'detects if the transaction has witnesses: ' + + (f.whex ? 'true' : 'false'), + () => { + assert.strictEqual( + Transaction.fromHex(f.whex ? f.whex : f.hex).hasWitnesses(), + !!f.whex, + ); + }, + ); + }); + }); describe('weight/virtualSize', () => { it('computes virtual size', () => { fixtures.valid.forEach(f => { - const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) + const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex); - assert.strictEqual(transaction.virtualSize(), f.virtualSize) - }) - }) + assert.strictEqual(transaction.virtualSize(), f.virtualSize); + }); + }); it('computes weight', () => { fixtures.valid.forEach(f => { - const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) + const transaction = Transaction.fromHex(f.whex ? f.whex : f.hex); - assert.strictEqual(transaction.weight(), f.weight) - }) - }) - }) + assert.strictEqual(transaction.weight(), f.weight); + }); + }); + }); describe('addInput', () => { - let prevTxHash + let prevTxHash: Buffer; beforeEach(() => { - prevTxHash = Buffer.from('ffffffff00ffff000000000000000000000000000000000000000000101010ff', 'hex') - }) + prevTxHash = Buffer.from( + 'ffffffff00ffff000000000000000000000000000000000000000000101010ff', + 'hex', + ); + }); it('returns an index', () => { - const tx = new Transaction() - assert.strictEqual(tx.addInput(prevTxHash, 0), 0) - assert.strictEqual(tx.addInput(prevTxHash, 0), 1) - }) + const tx = new Transaction(); + assert.strictEqual(tx.addInput(prevTxHash, 0), 0); + assert.strictEqual(tx.addInput(prevTxHash, 0), 1); + }); it('defaults to empty script, witness and 0xffffffff SEQUENCE number', () => { - const tx = new Transaction() - tx.addInput(prevTxHash, 0) + const tx = new Transaction(); + tx.addInput(prevTxHash, 0); - assert.strictEqual(tx.ins[0].script.length, 0) - assert.strictEqual(tx.ins[0].witness.length, 0) - assert.strictEqual(tx.ins[0].sequence, 0xffffffff) - }) + assert.strictEqual(tx.ins[0].script.length, 0); + assert.strictEqual(tx.ins[0].witness.length, 0); + assert.strictEqual(tx.ins[0].sequence, 0xffffffff); + }); fixtures.invalid.addInput.forEach(f => { it('throws on ' + f.exception, () => { - const tx = new Transaction() - const hash = Buffer.from(f.hash, 'hex') + const tx = new Transaction(); + const hash = Buffer.from(f.hash, 'hex'); assert.throws(() => { - tx.addInput(hash, f.index) - }, new RegExp(f.exception)) - }) - }) - }) + tx.addInput(hash, f.index); + }, new RegExp(f.exception)); + }); + }); + }); describe('addOutput', () => { it('returns an index', () => { - const tx = new Transaction() - assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 0) - assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 1) - }) - }) + const tx = new Transaction(); + assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 0); + assert.strictEqual(tx.addOutput(Buffer.alloc(0), 0), 1); + }); + }); describe('clone', () => { fixtures.valid.forEach(f => { - let actual - let expected + let actual: Transaction; + let expected: Transaction; beforeEach(() => { - expected = Transaction.fromHex(f.hex) - actual = expected.clone() - }) + expected = Transaction.fromHex(f.hex); + actual = expected.clone(); + }); it('should have value equality', () => { - assert.deepStrictEqual(actual, expected) - }) + assert.deepStrictEqual(actual, expected); + }); it('should not have reference equality', () => { - assert.notStrictEqual(actual, expected) - }) - }) - }) + assert.notStrictEqual(actual, expected); + }); + }); + }); describe('getHash/getId', () => { - function verify (f) { + function verify(f: any) { it('should return the id for ' + f.id + '(' + f.description + ')', () => { - const tx = Transaction.fromHex(f.whex || f.hex) + const tx = Transaction.fromHex(f.whex || f.hex); - assert.strictEqual(tx.getHash().toString('hex'), f.hash) - assert.strictEqual(tx.getId(), f.id) - }) + assert.strictEqual(tx.getHash().toString('hex'), f.hash); + assert.strictEqual(tx.getId(), f.id); + }); } - fixtures.valid.forEach(verify) - }) + fixtures.valid.forEach(verify); + }); describe('isCoinbase', () => { - function verify (f) { - it('should return ' + f.coinbase + ' for ' + f.id + '(' + f.description + ')', () => { - const tx = Transaction.fromHex(f.hex) + function verify(f: any) { + it( + 'should return ' + + f.coinbase + + ' for ' + + f.id + + '(' + + f.description + + ')', + () => { + const tx = Transaction.fromHex(f.hex); - assert.strictEqual(tx.isCoinbase(), f.coinbase) - }) + assert.strictEqual(tx.isCoinbase(), f.coinbase); + }, + ); } - fixtures.valid.forEach(verify) - }) + fixtures.valid.forEach(verify); + }); describe('hashForSignature', () => { it('does not use Witness serialization', () => { - const randScript = Buffer.from('6a', 'hex') + const randScript = Buffer.from('6a', 'hex'); - const tx = new Transaction() - tx.addInput(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'), 0) - tx.addOutput(randScript, 5000000000) + const tx = new Transaction(); + tx.addInput( + Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000000', + 'hex', + ), + 0, + ); + tx.addOutput(randScript, 5000000000); - const original = tx.__toBuffer - tx.__toBuffer = (a, b, c) => { - if (c !== false) throw new Error('hashForSignature MUST pass false') + const original = (tx as any).__toBuffer; + (tx as any).__toBuffer = function( + this: Transaction, + a: any, + b: any, + c: any, + ): any { + if (c !== false) throw new Error('hashForSignature MUST pass false'); - return original.call(this, a, b, c) - } + return original.call(this, a, b, c); + }; assert.throws(() => { - tx.__toBuffer(undefined, undefined, true) - }, /hashForSignature MUST pass false/) + (tx as any).__toBuffer(undefined, undefined, true); + }, /hashForSignature MUST pass false/); // assert hashForSignature does not pass false assert.doesNotThrow(() => { - tx.hashForSignature(0, randScript, 1) - }) - }) + tx.hashForSignature(0, randScript, 1); + }); + }); fixtures.hashForSignature.forEach(f => { - it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : f.script), () => { - const tx = Transaction.fromHex(f.txHex) - const script = bscript.fromASM(f.script) + it( + 'should return ' + + f.hash + + ' for ' + + (f.description ? 'case "' + f.description + '"' : f.script), + () => { + const tx = Transaction.fromHex(f.txHex); + const script = bscript.fromASM(f.script); - assert.strictEqual(tx.hashForSignature(f.inIndex, script, f.type).toString('hex'), f.hash) - }) - }) - }) + assert.strictEqual( + tx.hashForSignature(f.inIndex, script, f.type).toString('hex'), + f.hash, + ); + }, + ); + }); + }); describe('hashForWitnessV0', () => { fixtures.hashForWitnessV0.forEach(f => { - it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : ''), () => { - const tx = Transaction.fromHex(f.txHex) - const script = bscript.fromASM(f.script) + it( + 'should return ' + + f.hash + + ' for ' + + (f.description ? 'case "' + f.description + '"' : ''), + () => { + const tx = Transaction.fromHex(f.txHex); + const script = bscript.fromASM(f.script); - assert.strictEqual(tx.hashForWitnessV0(f.inIndex, script, f.value, f.type).toString('hex'), f.hash) - }) - }) - }) + assert.strictEqual( + tx + .hashForWitnessV0(f.inIndex, script, f.value, f.type) + .toString('hex'), + f.hash, + ); + }, + ); + }); + }); describe('setWitness', () => { it('only accepts a a witness stack (Array of Buffers)', () => { assert.throws(() => { - (new Transaction()).setWitness(0, 'foobar') - }, /Expected property "1" of type \[Buffer], got String "foobar"/) - }) - }) -}) + (new Transaction().setWitness as any)(0, 'foobar'); + }, /Expected property "1" of type \[Buffer], got String "foobar"/); + }); + }); +}); diff --git a/test/transaction_builder.spec.ts b/test/transaction_builder.spec.ts index 6374161..0daa103 100644 --- a/test/transaction_builder.spec.ts +++ b/test/transaction_builder.spec.ts @@ -1,45 +1,57 @@ -const { describe, it, beforeEach } = require('mocha') -const assert = require('assert') -const baddress = require('../src/address') -const bscript = require('../src/script') -const payments = require('../src/payments') +import * as assert from 'assert'; +import { beforeEach, describe, it } from 'mocha'; +import * as baddress from '../src/address'; +import * as bscript from '../src/script'; +import * as payments from '../src/payments'; +import { + ECPair, + networks as NETWORKS, + Transaction, + TransactionBuilder, +} from '..'; -const ECPair = require('../src/ecpair') -const Transaction = require('..').Transaction -const TransactionBuilder = require('..').TransactionBuilder -const NETWORKS = require('../src/networks') +console.warn = () => {}; // Silence the Deprecation Warning -console.warn = () => {} // Silence the Deprecation Warning +import * as fixtures from './fixtures/transaction_builder.json'; -const fixtures = require('./fixtures/transaction_builder') +function constructSign( + f: any, + txb: any, + useOldSignArgs: any, +): TransactionBuilder { + const network = (NETWORKS as any)[f.network]; + const stages = f.stages && f.stages.concat(); -function constructSign (f, txb, useOldSignArgs) { - const network = NETWORKS[f.network] - const stages = f.stages && f.stages.concat() - - f.inputs.forEach((input, index) => { - if (!input.signs) return - input.signs.forEach(sign => { - const keyPair = ECPair.fromWIF(sign.keyPair, network) - let redeemScript - let witnessScript - let witnessValue + f.inputs.forEach((input: any, index: number) => { + if (!input.signs) return; + input.signs.forEach((sign: any) => { + const keyPair = ECPair.fromWIF(sign.keyPair, network); + let redeemScript; + let witnessScript; + let witnessValue; if (sign.redeemScript) { - redeemScript = bscript.fromASM(sign.redeemScript) + redeemScript = bscript.fromASM(sign.redeemScript); } if (sign.value) { - witnessValue = sign.value + witnessValue = sign.value; } if (sign.witnessScript) { - witnessScript = bscript.fromASM(sign.witnessScript) + witnessScript = bscript.fromASM(sign.witnessScript); } if (useOldSignArgs) { // DEPRECATED: v6 will remove this interface - txb.sign(index, keyPair, redeemScript, sign.hashType, witnessValue, witnessScript) + txb.sign( + index, + keyPair, + redeemScript, + sign.hashType, + witnessValue, + witnessScript, + ); } else { // prevOutScriptType is required, see /ts_src/transaction_builder.ts // The PREVOUT_TYPES constant is a Set with all possible values. @@ -51,64 +63,68 @@ function constructSign (f, txb, useOldSignArgs) { hashType: sign.hashType, witnessValue, witnessScript, - }) + }); } if (sign.stage) { - const tx = txb.buildIncomplete() - assert.strictEqual(tx.toHex(), stages.shift()) - txb = TransactionBuilder.fromTransaction(tx, network) + const tx = txb.buildIncomplete(); + assert.strictEqual(tx.toHex(), stages.shift()); + txb = TransactionBuilder.fromTransaction(tx, network); } - }) - }) + }); + }); - return txb + return txb; } -function construct (f, dontSign, useOldSignArgs) { - const network = NETWORKS[f.network] - const txb = new TransactionBuilder(network) +function construct( + f: any, + dontSign?: any, + useOldSignArgs?: any, +): TransactionBuilder { + const network = (NETWORKS as any)[f.network]; + const txb = new TransactionBuilder(network); - if (Number.isFinite(f.version)) txb.setVersion(f.version) - if (f.locktime !== undefined) txb.setLockTime(f.locktime) + if (Number.isFinite(f.version)) txb.setVersion(f.version); + if (f.locktime !== undefined) txb.setLockTime(f.locktime); - f.inputs.forEach(input => { - let prevTx + f.inputs.forEach((input: any) => { + let prevTx; if (input.txRaw) { - const constructed = construct(input.txRaw) - if (input.txRaw.incomplete) prevTx = constructed.buildIncomplete() - else prevTx = constructed.build() + const constructed = construct(input.txRaw); + if (input.txRaw.incomplete) prevTx = constructed.buildIncomplete(); + else prevTx = constructed.build(); } else if (input.txHex) { - prevTx = Transaction.fromHex(input.txHex) + prevTx = Transaction.fromHex(input.txHex); } else { - prevTx = input.txId + prevTx = input.txId; } - let prevTxScript + let prevTxScript; if (input.prevTxScript) { - prevTxScript = bscript.fromASM(input.prevTxScript) + prevTxScript = bscript.fromASM(input.prevTxScript); } - txb.addInput(prevTx, input.vout, input.sequence, prevTxScript) - }) + txb.addInput(prevTx, input.vout, input.sequence, prevTxScript); + }); - f.outputs.forEach(output => { + f.outputs.forEach((output: any) => { if (output.address) { - txb.addOutput(output.address, output.value) + txb.addOutput(output.address, output.value); } else { - txb.addOutput(bscript.fromASM(output.script), output.value) + txb.addOutput(bscript.fromASM(output.script), output.value); } - }) + }); - if (dontSign) return txb - return constructSign(f, txb, useOldSignArgs) + if (dontSign) return txb; + return constructSign(f, txb, useOldSignArgs); } // TODO: Remove loop in v6 -for (const useOldSignArgs of [ false, true ]) { +for (const useOldSignArgs of [false, true]) { // Search for "useOldSignArgs" // to find the second part of this console.warn replace - let consoleWarn; + let consoleWarn: any; if (useOldSignArgs) { consoleWarn = console.warn; // Silence console.warn during these tests @@ -116,597 +132,699 @@ for (const useOldSignArgs of [ false, true ]) { } describe(`TransactionBuilder: useOldSignArgs === ${useOldSignArgs}`, () => { // constants - const keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')) + const keyPair = ECPair.fromPrivateKey( + Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000001', + 'hex', + ), + ); const scripts = [ '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', - '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP' + '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', ].map(x => { - return baddress.toOutputScript(x) - }) - const txHash = Buffer.from('0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', 'hex') + return baddress.toOutputScript(x); + }); + const txHash = Buffer.from( + '0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', + 'hex', + ); describe('fromTransaction', () => { fixtures.valid.build.forEach(f => { it('returns TransactionBuilder, with ' + f.description, () => { - const network = NETWORKS[f.network || 'bitcoin'] + const network = (NETWORKS as any)[f.network || 'bitcoin']; - const tx = Transaction.fromHex(f.txHex) - const txb = TransactionBuilder.fromTransaction(tx, network) - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + const tx = Transaction.fromHex(f.txHex); + const txb = TransactionBuilder.fromTransaction(tx, network); + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build(); - assert.strictEqual(txAfter.toHex(), f.txHex) - assert.strictEqual(txb.network, network) - }) - }) + assert.strictEqual(txAfter.toHex(), f.txHex); + assert.strictEqual(txb.network, network); + }); + }); fixtures.valid.fromTransaction.forEach(f => { it('returns TransactionBuilder, with ' + f.description, () => { - const tx = new Transaction() + const tx = new Transaction(); f.inputs.forEach(input => { - const txHash2 = Buffer.from(input.txId, 'hex').reverse() + const txHash2 = Buffer.from(input.txId, 'hex').reverse() as Buffer; - tx.addInput(txHash2, input.vout, undefined, bscript.fromASM(input.scriptSig)) - }) + tx.addInput( + txHash2, + input.vout, + undefined, + bscript.fromASM(input.scriptSig), + ); + }); f.outputs.forEach(output => { - tx.addOutput(bscript.fromASM(output.script), output.value) - }) + tx.addOutput(bscript.fromASM(output.script), output.value); + }); - const txb = TransactionBuilder.fromTransaction(tx) - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + const txb = TransactionBuilder.fromTransaction(tx); + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build(); txAfter.ins.forEach((input, i) => { - assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) - }) + assert.strictEqual( + bscript.toASM(input.script), + f.inputs[i].scriptSigAfter, + ); + }); txAfter.outs.forEach((output, i) => { - assert.strictEqual(bscript.toASM(output.script), f.outputs[i].script) - }) - }) - }) + assert.strictEqual( + bscript.toASM(output.script), + f.outputs[i].script, + ); + }); + }); + }); fixtures.valid.fromTransactionSequential.forEach(f => { it('with ' + f.description, () => { - const network = NETWORKS[f.network] - const tx = Transaction.fromHex(f.txHex) - const txb = TransactionBuilder.fromTransaction(tx, network) + const network = (NETWORKS as any)[f.network]; + const tx = Transaction.fromHex(f.txHex); + const txb = TransactionBuilder.fromTransaction(tx, network); tx.ins.forEach((input, i) => { - assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSig) - }) + assert.strictEqual( + bscript.toASM(input.script), + f.inputs[i].scriptSig, + ); + }); - constructSign(f, txb, useOldSignArgs) - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() + constructSign(f, txb, useOldSignArgs); + const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build(); txAfter.ins.forEach((input, i) => { - assert.strictEqual(bscript.toASM(input.script), f.inputs[i].scriptSigAfter) - }) + assert.strictEqual( + bscript.toASM(input.script), + f.inputs[i].scriptSigAfter, + ); + }); - assert.strictEqual(txAfter.toHex(), f.txHexAfter) - }) - }) + assert.strictEqual(txAfter.toHex(), f.txHexAfter); + }); + }); it('classifies transaction inputs', () => { - const tx = Transaction.fromHex(fixtures.valid.classification.hex) - const txb = TransactionBuilder.fromTransaction(tx) + const tx = Transaction.fromHex(fixtures.valid.classification.hex); + const txb = TransactionBuilder.fromTransaction(tx); - txb.__INPUTS.forEach(i => { - assert.strictEqual(i.prevOutType, 'scripthash') - assert.strictEqual(i.redeemScriptType, 'multisig') - }) - }) + (txb as any).__INPUTS.forEach((i: any) => { + assert.strictEqual(i.prevOutType, 'scripthash'); + assert.strictEqual(i.redeemScriptType, 'multisig'); + }); + }); fixtures.invalid.fromTransaction.forEach(f => { it('throws ' + f.exception, () => { - const tx = Transaction.fromHex(f.txHex) + const tx = Transaction.fromHex(f.txHex); assert.throws(() => { - TransactionBuilder.fromTransaction(tx) - }, new RegExp(f.exception)) - }) - }) - }) + TransactionBuilder.fromTransaction(tx); + }, new RegExp(f.exception)); + }); + }); + }); describe('addInput', () => { - let txb + let txb: TransactionBuilder; beforeEach(() => { - txb = new TransactionBuilder() - }) + txb = new TransactionBuilder(); + }); it('accepts a txHash, index [and sequence number]', () => { - const vin = txb.addInput(txHash, 1, 54) - assert.strictEqual(vin, 0) + const vin = txb.addInput(txHash, 1, 54); + assert.strictEqual(vin, 0); - const txIn = txb.__TX.ins[0] - assert.strictEqual(txIn.hash, txHash) - assert.strictEqual(txIn.index, 1) - assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__INPUTS[0].prevOutScript, undefined) - }) + const txIn = (txb as any).__TX.ins[0]; + assert.strictEqual(txIn.hash, txHash); + assert.strictEqual(txIn.index, 1); + assert.strictEqual(txIn.sequence, 54); + assert.strictEqual((txb as any).__INPUTS[0].prevOutScript, undefined); + }); it('accepts a txHash, index [, sequence number and scriptPubKey]', () => { - const vin = txb.addInput(txHash, 1, 54, scripts[1]) - assert.strictEqual(vin, 0) + const vin = txb.addInput(txHash, 1, 54, scripts[1]); + assert.strictEqual(vin, 0); - const txIn = txb.__TX.ins[0] - assert.strictEqual(txIn.hash, txHash) - assert.strictEqual(txIn.index, 1) - assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) - }) + const txIn = (txb as any).__TX.ins[0]; + assert.strictEqual(txIn.hash, txHash); + assert.strictEqual(txIn.index, 1); + assert.strictEqual(txIn.sequence, 54); + assert.strictEqual((txb as any).__INPUTS[0].prevOutScript, scripts[1]); + }); it('accepts a prevTx, index [and sequence number]', () => { - const prevTx = new Transaction() - prevTx.addOutput(scripts[0], 0) - prevTx.addOutput(scripts[1], 1) + const prevTx = new Transaction(); + prevTx.addOutput(scripts[0], 0); + prevTx.addOutput(scripts[1], 1); - const vin = txb.addInput(prevTx, 1, 54) - assert.strictEqual(vin, 0) + const vin = txb.addInput(prevTx, 1, 54); + assert.strictEqual(vin, 0); - const txIn = txb.__TX.ins[0] - assert.deepStrictEqual(txIn.hash, prevTx.getHash()) - assert.strictEqual(txIn.index, 1) - assert.strictEqual(txIn.sequence, 54) - assert.strictEqual(txb.__INPUTS[0].prevOutScript, scripts[1]) - }) + const txIn = (txb as any).__TX.ins[0]; + assert.deepStrictEqual(txIn.hash, prevTx.getHash()); + assert.strictEqual(txIn.index, 1); + assert.strictEqual(txIn.sequence, 54); + assert.strictEqual((txb as any).__INPUTS[0].prevOutScript, scripts[1]); + }); it('returns the input index', () => { - assert.strictEqual(txb.addInput(txHash, 0), 0) - assert.strictEqual(txb.addInput(txHash, 1), 1) - }) + assert.strictEqual(txb.addInput(txHash, 0), 0); + assert.strictEqual(txb.addInput(txHash, 1), 1); + }); it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 1000) + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 1000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); assert.throws(() => { - txb.addInput(txHash, 0) - }, /No, this would invalidate signatures/) - }) - }) + txb.addInput(txHash, 0); + }, /No, this would invalidate signatures/); + }); + }); describe('addOutput', () => { - let txb + let txb: TransactionBuilder; beforeEach(() => { - txb = new TransactionBuilder() - }) + txb = new TransactionBuilder(); + }); it('accepts an address string and value', () => { - const { address } = payments.p2pkh({ pubkey: keyPair.publicKey }) - const vout = txb.addOutput(address, 1000) - assert.strictEqual(vout, 0) + const { address } = payments.p2pkh({ pubkey: keyPair.publicKey }); + const vout = txb.addOutput(address!, 1000); + assert.strictEqual(vout, 0); - const txout = txb.__TX.outs[0] - assert.deepStrictEqual(txout.script, scripts[0]) - assert.strictEqual(txout.value, 1000) - }) + const txout = (txb as any).__TX.outs[0]; + assert.deepStrictEqual(txout.script, scripts[0]); + assert.strictEqual(txout.value, 1000); + }); it('accepts a ScriptPubKey and value', () => { - const vout = txb.addOutput(scripts[0], 1000) - assert.strictEqual(vout, 0) + const vout = txb.addOutput(scripts[0], 1000); + assert.strictEqual(vout, 0); - const txout = txb.__TX.outs[0] - assert.deepStrictEqual(txout.script, scripts[0]) - assert.strictEqual(txout.value, 1000) - }) + const txout = (txb as any).__TX.outs[0]; + assert.deepStrictEqual(txout.script, scripts[0]); + assert.strictEqual(txout.value, 1000); + }); it('throws if address is of the wrong network', () => { assert.throws(() => { - txb.addOutput('2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9', 1000) - }, /2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9 has no matching Script/) - }) + txb.addOutput('2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9', 1000); + }, /2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9 has no matching Script/); + }); it('add second output after signed first input with SIGHASH_NONE', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 2000) + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 2000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: Transaction.SIGHASH_NONE, - }) - assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) - }) + }); + assert.strictEqual(txb.addOutput(scripts[1], 9000), 1); + }); it('add first output after signed first input with SIGHASH_NONE', () => { - txb.addInput(txHash, 0) + txb.addInput(txHash, 0); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: Transaction.SIGHASH_NONE, - }) - assert.strictEqual(txb.addOutput(scripts[0], 2000), 0) - }) + }); + assert.strictEqual(txb.addOutput(scripts[0], 2000), 0); + }); it('add second output after signed first input with SIGHASH_SINGLE', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 2000) + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 2000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: Transaction.SIGHASH_SINGLE, - }) - assert.strictEqual(txb.addOutput(scripts[1], 9000), 1) - }) + }); + assert.strictEqual(txb.addOutput(scripts[1], 9000), 1); + }); it('add first output after signed first input with SIGHASH_SINGLE', () => { - txb.addInput(txHash, 0) + txb.addInput(txHash, 0); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: Transaction.SIGHASH_SINGLE, - }) + }); assert.throws(() => { - txb.addOutput(scripts[0], 2000) - }, /No, this would invalidate signatures/) - }) + txb.addOutput(scripts[0], 2000); + }, /No, this would invalidate signatures/); + }); it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 2000) + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 2000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); assert.throws(() => { - txb.addOutput(scripts[1], 9000) - }, /No, this would invalidate signatures/) - }) - }) + txb.addOutput(scripts[1], 9000); + }, /No, this would invalidate signatures/); + }); + }); describe('setLockTime', () => { it('throws if if there exist any scriptSigs', () => { - const txb = new TransactionBuilder() - txb.addInput(txHash, 0) - txb.addOutput(scripts[0], 100) + const txb = new TransactionBuilder(); + txb.addInput(txHash, 0); + txb.addOutput(scripts[0], 100); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); assert.throws(() => { - txb.setLockTime(65535) - }, /No, this would invalidate signatures/) - }) - }) + txb.setLockTime(65535); + }, /No, this would invalidate signatures/); + }); + }); describe('sign', () => { it('supports the alternative abstract interface { publicKey, sign }', () => { const keyPair = { - publicKey: ECPair.makeRandom({ rng: () => { return Buffer.alloc(32, 1) } }).publicKey, - sign: hash => { return Buffer.alloc(64, 0x5f) } - } + publicKey: ECPair.makeRandom({ + rng: () => { + return Buffer.alloc(32, 1); + }, + }).publicKey, + sign: () => { + return Buffer.alloc(64, 0x5f); + }, + }; - const txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) + const txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 1, + ); + txb.addOutput('1111111111111111111114oLvT2', 100000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) - assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') - }) + }); + assert.strictEqual( + txb.build().toHex(), + '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', + ); + }); it('supports low R signature signing', () => { - let txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) + let txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 1, + ); + txb.addOutput('1111111111111111111114oLvT2', 100000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); // high R - assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + assert.strictEqual( + txb.build().toHex(), + '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', + ); - txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) - txb.setLowR() + txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 1, + ); + txb.addOutput('1111111111111111111114oLvT2', 100000); + txb.setLowR(); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) + }); // low R - assert.strictEqual(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') - }) + assert.strictEqual( + txb.build().toHex(), + '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', + ); + }); it('fails when missing required arguments', () => { - let txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) - txb.addOutput('1111111111111111111114oLvT2', 100000) + let txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 1, + ); + txb.addOutput('1111111111111111111114oLvT2', 100000); assert.throws(() => { - txb.sign() - }, /TransactionBuilder sign first arg must be TxbSignArg or number/) + (txb as any).sign(); + }, /TransactionBuilder sign first arg must be TxbSignArg or number/); assert.throws(() => { txb.sign({ prevOutScriptType: 'p2pkh', vin: 1, keyPair, - }) - }, /No input at index: 1/) + }); + }, /No input at index: 1/); assert.throws(() => { - txb.sign({ + (txb as any).sign({ prevOutScriptType: 'p2pkh', keyPair, - }) - }, /sign must include vin parameter as Number \(input index\)/) + }); + }, /sign must include vin parameter as Number \(input index\)/); assert.throws(() => { - txb.sign({ + (txb as any).sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair: {}, - }) - }, /sign must include keyPair parameter as Signer interface/) + }); + }, /sign must include keyPair parameter as Signer interface/); assert.throws(() => { - txb.sign({ + (txb as any).sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, hashType: 'string', - }) - }, /sign hashType parameter must be a number/) + }); + }, /sign hashType parameter must be a number/); if (useOldSignArgs) { assert.throws(() => { - txb.sign(0) - }, /sign requires keypair/) + txb.sign(0); + }, /sign requires keypair/); } - }) + }); fixtures.invalid.sign.forEach(f => { - it('throws ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), () => { - const txb = construct(f, true) + it( + 'throws ' + + f.exception + + (f.description ? ' (' + f.description + ')' : ''), + () => { + const txb = construct(f, true); - let threw = false - f.inputs.forEach((input, index) => { - input.signs.forEach(sign => { - const keyPairNetwork = NETWORKS[sign.network || f.network] - const keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork) - let redeemScript - let witnessScript + let threw = false; + (f.inputs as any).forEach( + (input: any, index: number): void => { + input.signs.forEach((sign: any) => { + const keyPairNetwork = (NETWORKS as any)[ + sign.network || f.network + ]; + const keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork); + let redeemScript: Buffer | undefined; + let witnessScript: Buffer | undefined; - if (sign.redeemScript) { - redeemScript = bscript.fromASM(sign.redeemScript) - } + if (sign.redeemScript) { + redeemScript = bscript.fromASM(sign.redeemScript); + } - if (sign.witnessScript) { - witnessScript = bscript.fromASM(sign.witnessScript) - } + if (sign.witnessScript) { + witnessScript = bscript.fromASM(sign.witnessScript); + } - if (sign.throws) { - assert.throws(() => { - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: index, - keyPair: keyPair2, - redeemScript, - hashType: sign.hashType, - witnessValue: sign.value, - witnessScript, - }) - }, new RegExp(f.exception)) - threw = true - } else { - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: index, - keyPair: keyPair2, - redeemScript, - hashType: sign.hashType, - witnessValue: sign.value, - witnessScript, - }) - } - }) - }) + if (sign.throws) { + assert.throws(() => { + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: index, + keyPair: keyPair2, + redeemScript, + hashType: sign.hashType, + witnessValue: sign.value, + witnessScript, + }); + }, new RegExp(f.exception)); + threw = true; + } else { + txb.sign({ + prevOutScriptType: sign.prevOutScriptType, + vin: index, + keyPair: keyPair2, + redeemScript, + hashType: sign.hashType, + witnessValue: sign.value, + witnessScript, + }); + } + }); + }, + ); - assert.strictEqual(threw, true) - }) - }) - }) + assert.strictEqual(threw, true); + }, + ); + }); + }); describe('build', () => { fixtures.valid.build.forEach(f => { it('builds "' + f.description + '"', () => { - const txb = construct(f, undefined, useOldSignArgs) - const tx = f.incomplete ? txb.buildIncomplete() : txb.build() + const txb = construct(f, undefined, useOldSignArgs); + const tx = f.incomplete ? txb.buildIncomplete() : txb.build(); - assert.strictEqual(tx.toHex(), f.txHex) - }) - }) + assert.strictEqual(tx.toHex(), f.txHex); + }); + }); // TODO: remove duplicate test code fixtures.invalid.build.forEach(f => { describe('for ' + (f.description || f.exception), () => { it('throws ' + f.exception, () => { assert.throws(() => { - let txb + let txb; if (f.txHex) { - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) + txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(f.txHex), + ); } else { - txb = construct(f, undefined, useOldSignArgs) + txb = construct(f, undefined, useOldSignArgs); } - txb.build() - }, new RegExp(f.exception)) - }) + txb.build(); + }, new RegExp(f.exception)); + }); // if throws on incomplete too, enforce that if (f.incomplete) { it('throws ' + f.exception, () => { assert.throws(() => { - let txb + let txb; if (f.txHex) { - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) + txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(f.txHex), + ); } else { - txb = construct(f, undefined, useOldSignArgs) + txb = construct(f, undefined, useOldSignArgs); } - txb.buildIncomplete() - }, new RegExp(f.exception)) - }) + txb.buildIncomplete(); + }, new RegExp(f.exception)); + }); } else { it('does not throw if buildIncomplete', () => { - let txb + let txb; if (f.txHex) { - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(f.txHex)) + txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(f.txHex), + ); } else { - txb = construct(f, undefined, useOldSignArgs) + txb = construct(f, undefined, useOldSignArgs); } - txb.buildIncomplete() - }) + txb.buildIncomplete(); + }); } - }) - }) + }); + }); it('for incomplete with 0 signatures', () => { - const randomTxData = '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000' - const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH' + const randomTxData = + '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000'; + const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH'; - const randomTx = Transaction.fromHex(randomTxData) - let tx = new TransactionBuilder() - tx.addInput(randomTx, 0) - tx.addOutput(randomAddress, 1000) - tx = tx.buildIncomplete() - assert(tx) - }) + const randomTx = Transaction.fromHex(randomTxData); + const txb = new TransactionBuilder(); + txb.addInput(randomTx, 0); + txb.addOutput(randomAddress, 1000); + const tx = txb.buildIncomplete(); + assert(tx); + }); it('for incomplete P2SH with 0 signatures', () => { - const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', 'hex') // arbitrary P2SH input - const inpTx = Transaction.fromBuffer(inp) + const inp = Buffer.from( + '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', + 'hex', + ); // arbitrary P2SH input + const inpTx = Transaction.fromBuffer(inp); - const txb = new TransactionBuilder(NETWORKS.testnet) - txb.addInput(inpTx, 0) - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output + const txb = new TransactionBuilder(NETWORKS.testnet); + txb.addInput(inpTx, 0); + txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8); // arbitrary output - txb.buildIncomplete() - }) + txb.buildIncomplete(); + }); it('for incomplete P2WPKH with 0 signatures', () => { - const inp = Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', 'hex') - const inpTx = Transaction.fromBuffer(inp) + const inp = Buffer.from( + '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', + 'hex', + ); + const inpTx = Transaction.fromBuffer(inp); - const txb = new TransactionBuilder(NETWORKS.testnet) - txb.addInput(inpTx, 0) - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output + const txb = new TransactionBuilder(NETWORKS.testnet); + txb.addInput(inpTx, 0); + txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8); // arbitrary output - txb.buildIncomplete() - }) + txb.buildIncomplete(); + }); it('for incomplete P2WSH with 0 signatures', () => { - const inpTx = Transaction.fromBuffer(Buffer.from('010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', 'hex')) + const inpTx = Transaction.fromBuffer( + Buffer.from( + '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', + 'hex', + ), + ); - const txb = new TransactionBuilder(NETWORKS.testnet) - txb.addInput(inpTx, 0) - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8) // arbitrary output + const txb = new TransactionBuilder(NETWORKS.testnet); + txb.addInput(inpTx, 0); + txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8); // arbitrary output - txb.buildIncomplete() - }) - }) + txb.buildIncomplete(); + }); + }); describe('multisig', () => { fixtures.valid.multisig.forEach(f => { it(f.description, () => { - const network = NETWORKS[f.network] - let txb = construct(f, true) - let tx + const network = (NETWORKS as any)[f.network]; + let txb = construct(f, true); + let tx: Transaction; f.inputs.forEach((input, i) => { - const redeemScript = bscript.fromASM(input.redeemScript) + const redeemScript = bscript.fromASM(input.redeemScript); input.signs.forEach(sign => { // rebuild the transaction each-time after the first if (tx) { // manually override the scriptSig? if (sign.scriptSigBefore) { - tx.ins[i].script = bscript.fromASM(sign.scriptSigBefore) + tx.ins[i].script = bscript.fromASM(sign.scriptSigBefore); } // rebuild - txb = TransactionBuilder.fromTransaction(tx, network) + txb = TransactionBuilder.fromTransaction(tx, network); } - const keyPair2 = ECPair.fromWIF(sign.keyPair, network) + const keyPair2 = ECPair.fromWIF(sign.keyPair, network); txb.sign({ prevOutScriptType: sign.prevOutScriptType, vin: i, keyPair: keyPair2, redeemScript, - hashType: sign.hashType, - }) + hashType: (sign as any).hashType, + }); // update the tx - tx = txb.buildIncomplete() + tx = txb.buildIncomplete(); // now verify the serialized scriptSig is as expected - assert.strictEqual(bscript.toASM(tx.ins[i].script), sign.scriptSig) - }) - }) + assert.strictEqual( + bscript.toASM(tx.ins[i].script), + sign.scriptSig, + ); + }); + }); - tx = txb.build() - assert.strictEqual(tx.toHex(), f.txHex) - }) - }) - }) + tx = txb.build(); + assert.strictEqual(tx.toHex(), f.txHex); + }); + }); + }); describe('various edge case', () => { - const network = NETWORKS.testnet + const network = NETWORKS.testnet; it('should warn of high fee for segwit transaction based on VSize, not Size', () => { - const rawtx = '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + - '1df90000000000fffffffffdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a1df9' + - '0100000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca40000' + - '000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca401000000' + - '00ffffffff0100040000000000001976a914cf307285359ab7ef6a2daa0522c7908ddf5fe7a988ac024730' + - '440220113324438816338406841775e079b04c50d04f241da652a4035b1017ea1ecf5502205802191eb49c' + - '54bf2a5667aea72e51c3ca92085efc60f12d1ebda3a64aff343201210283409659355b6d1cc3c32decd5d5' + - '61abaac86c37a353b52895a5e6c196d6f44802483045022100dc2892874e6d8708e3f5a058c5c9263cdf03' + - '969492270f89ee4933caf6daf8bb0220391dfe61a002709b63b9d64422d3db09b727839d1287e10a128a5d' + - 'b52a82309301210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f448024830' + - '450221009e3ed3a6ae93a018f443257b43e47b55cf7f7f3547d8807178072234686b22160220576121cfe6' + - '77c7eddf5575ea0a7c926247df6eca723c4f85df306e8bc08ea2df01210283409659355b6d1cc3c32decd5' + - 'd561abaac86c37a353b52895a5e6c196d6f44802473044022007be81ffd4297441ab10e740fc9bab9545a2' + - '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + - '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000' - const txb = TransactionBuilder.fromTransaction(Transaction.fromHex(rawtx)) - txb.__INPUTS[0].value = 241530 - txb.__INPUTS[1].value = 241530 - txb.__INPUTS[2].value = 248920 - txb.__INPUTS[3].value = 248920 + const rawtx = + '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + + '1df90000000000fffffffffdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a1df9' + + '0100000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca40000' + + '000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca401000000' + + '00ffffffff0100040000000000001976a914cf307285359ab7ef6a2daa0522c7908ddf5fe7a988ac024730' + + '440220113324438816338406841775e079b04c50d04f241da652a4035b1017ea1ecf5502205802191eb49c' + + '54bf2a5667aea72e51c3ca92085efc60f12d1ebda3a64aff343201210283409659355b6d1cc3c32decd5d5' + + '61abaac86c37a353b52895a5e6c196d6f44802483045022100dc2892874e6d8708e3f5a058c5c9263cdf03' + + '969492270f89ee4933caf6daf8bb0220391dfe61a002709b63b9d64422d3db09b727839d1287e10a128a5d' + + 'b52a82309301210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f448024830' + + '450221009e3ed3a6ae93a018f443257b43e47b55cf7f7f3547d8807178072234686b22160220576121cfe6' + + '77c7eddf5575ea0a7c926247df6eca723c4f85df306e8bc08ea2df01210283409659355b6d1cc3c32decd5' + + 'd561abaac86c37a353b52895a5e6c196d6f44802473044022007be81ffd4297441ab10e740fc9bab9545a2' + + '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + + '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000'; + const txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(rawtx), + ); + (txb as any).__INPUTS[0].value = 241530; + (txb as any).__INPUTS[1].value = 241530; + (txb as any).__INPUTS[2].value = 248920; + (txb as any).__INPUTS[3].value = 248920; assert.throws(() => { - txb.build() - }, new RegExp('Transaction has absurd fees')) - }) + txb.build(); + }, new RegExp('Transaction has absurd fees')); + }); it('should classify witness inputs with witness = true during multisigning', () => { - const keyPair = ECPair.fromWIF('cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', network) - const witnessScript = Buffer.from('522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', 'hex') - const redeemScript = Buffer.from('002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', 'hex') - const scriptPubKey = Buffer.from('a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', 'hex') - const txb = new TransactionBuilder(network) - txb.setVersion(1) - txb.addInput('a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', 1, 0xffffffff, scriptPubKey) - txb.addOutput(scriptPubKey, 99000) + const keyPair = ECPair.fromWIF( + 'cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', + network, + ); + const witnessScript = Buffer.from( + '522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', + 'hex', + ); + const redeemScript = Buffer.from( + '002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', + 'hex', + ); + const scriptPubKey = Buffer.from( + 'a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', + 'hex', + ); + const txb = new TransactionBuilder(network); + txb.setVersion(1); + txb.addInput( + 'a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', + 1, + 0xffffffff, + scriptPubKey, + ); + txb.addOutput(scriptPubKey, 99000); txb.sign({ prevOutScriptType: 'p2sh-p2wsh-p2ms', vin: 0, @@ -714,76 +832,116 @@ for (const useOldSignArgs of [ false, true ]) { redeemScript, witnessValue: 100000, witnessScript, - }) + }); // 2-of-2 signed only once - const tx = txb.buildIncomplete() + const tx = txb.buildIncomplete(); // Only input is segwit, so txid should be accurate with the final tx - assert.strictEqual(tx.getId(), 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') + assert.strictEqual( + tx.getId(), + 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821', + ); - const txHex = tx.toHex() - TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) - }) + const txHex = tx.toHex(); + TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)); + }); it('should handle badly pre-filled OP_0s', () => { // OP_0 is used where a signature is missing - const redeemScripSig = bscript.fromASM('OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') - const redeemScript = bscript.fromASM('OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG') + const redeemScripSig = bscript.fromASM( + 'OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae', + ); + const redeemScript = bscript.fromASM( + 'OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG', + ); - const tx = new Transaction() - tx.addInput(Buffer.from('cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f07149', 'hex'), 0, undefined, redeemScripSig) - tx.addOutput(Buffer.from('76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac', 'hex'), 1000) + const tx = new Transaction(); + tx.addInput( + Buffer.from( + 'cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f07149', + 'hex', + ), + 0, + undefined, + redeemScripSig, + ); + tx.addOutput( + Buffer.from( + '76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac', + 'hex', + ), + 1000, + ); // now import the Transaction - const txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet) + const txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet); - const keyPair2 = ECPair.fromWIF('91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', network) + const keyPair2 = ECPair.fromWIF( + '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', + network, + ); txb.sign({ prevOutScriptType: 'p2sh-p2ms', vin: 0, keyPair: keyPair2, redeemScript, - }) + }); - const tx2 = txb.build() - assert.strictEqual(tx2.getId(), 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') - assert.strictEqual(bscript.toASM(tx2.ins[0].script), 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') - }) + const tx2 = txb.build(); + assert.strictEqual( + tx2.getId(), + 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9', + ); + assert.strictEqual( + bscript.toASM(tx2.ins[0].script), + 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae', + ); + }); it('should not classify blank scripts as nonstandard', () => { - let txb = new TransactionBuilder() - txb.setVersion(1) - txb.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) + let txb = new TransactionBuilder(); + txb.setVersion(1); + txb.addInput( + 'aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', + 0, + ); - const incomplete = txb.buildIncomplete().toHex() - const keyPair = ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') + const incomplete = txb.buildIncomplete().toHex(); + const keyPair = ECPair.fromWIF( + 'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy', + ); // sign, as expected - txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) + txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) - const txId = txb.build().getId() - assert.strictEqual(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') + }); + const txId = txb.build().getId(); + assert.strictEqual( + txId, + '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3', + ); // and, repeat - txb = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) - txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) + txb = TransactionBuilder.fromTransaction( + Transaction.fromHex(incomplete), + ); + txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000); txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, keyPair, - }) - const txId2 = txb.build().getId() - assert.strictEqual(txId, txId2) + }); + const txId2 = txb.build().getId(); + assert.strictEqual(txId, txId2); // TODO: Remove me in v6 if (useOldSignArgs) { console.warn = consoleWarn; } - }) - }) - }) + }); + }); + }); } diff --git a/test/ts-node-register.js b/test/ts-node-register.js new file mode 100644 index 0000000..fef284d --- /dev/null +++ b/test/ts-node-register.js @@ -0,0 +1,5 @@ +// This file is required to run mocha tests on the TS files directly + +require("ts-node").register({ + project: "test/tsconfig.json", +}); diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000..e29620d --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "target": "ES2017", + "module": "commonjs", + "outDir": "../", + "declaration": false, + "rootDir": "../", + "rootDirs": [ + "../src", + "../types" + ], + "types": [ + "node", + "mocha" + ], + "allowJs": false, + "resolveJsonModule": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "esModuleInterop": false, + "noUnusedLocals": true, + "noUnusedParameters": true, + "baseUrl": ".", + "paths": { + "../src/*": ["../ts_src/*"] + } + }, + "include": [ + "./**/*.ts" + ], + "exclude": [ + "../ts_src/**/*.ts" + ] +} diff --git a/test/types.spec.ts b/test/types.spec.ts index 8911ca1..363b83c 100644 --- a/test/types.spec.ts +++ b/test/types.spec.ts @@ -1,38 +1,44 @@ -const { describe, it } = require('mocha') -const assert = require('assert') -const types = require('../src/types') -const typeforce = require('typeforce') +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import * as types from '../src/types'; +const typeforce = require('typeforce'); describe('types', () => { describe('Buffer Hash160/Hash256', () => { - const buffer20byte = Buffer.alloc(20) - const buffer32byte = Buffer.alloc(32) + const buffer20byte = Buffer.alloc(20); + const buffer32byte = Buffer.alloc(32); it('return true for valid size', () => { - assert(types.Hash160bit(buffer20byte)) - assert(types.Hash256bit(buffer32byte)) - }) + assert(types.Hash160bit(buffer20byte)); + assert(types.Hash256bit(buffer32byte)); + }); it('return true for oneOf', () => { assert.doesNotThrow(() => { - typeforce(types.oneOf(types.Hash160bit, types.Hash256bit), buffer32byte) - }) + typeforce( + types.oneOf(types.Hash160bit, types.Hash256bit), + buffer32byte, + ); + }); assert.doesNotThrow(() => { - typeforce(types.oneOf(types.Hash256bit, types.Hash160bit), buffer32byte) - }) - }) + typeforce( + types.oneOf(types.Hash256bit, types.Hash160bit), + buffer32byte, + ); + }); + }); it('throws for invalid size', () => { assert.throws(() => { - types.Hash160bit(buffer32byte) - }, /Expected Buffer\(Length: 20\), got Buffer\(Length: 32\)/) + types.Hash160bit(buffer32byte); + }, /Expected Buffer\(Length: 20\), got Buffer\(Length: 32\)/); assert.throws(() => { - types.Hash256bit(buffer20byte) - }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 20\)/) - }) - }) + types.Hash256bit(buffer20byte); + }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 20\)/); + }); + }); describe('Satoshi', () => { [ @@ -41,11 +47,11 @@ describe('types', () => { { value: 1, result: true }, { value: 20999999 * 1e8, result: true }, { value: 21000000 * 1e8, result: true }, - { value: 21000001 * 1e8, result: false } + { value: 21000001 * 1e8, result: false }, ].forEach(f => { it('returns ' + f.result + ' for valid for ' + f.value, () => { - assert.strictEqual(types.Satoshi(f.value), f.result) - }) - }) - }) -}) + assert.strictEqual(types.Satoshi(f.value), f.result); + }); + }); + }); +}); diff --git a/ts_src/block.ts b/ts_src/block.ts index 820c075..cf4ed51 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -148,7 +148,7 @@ export class Block { return anyTxHasWitness(this.transactions!); } - byteLength(headersOnly: boolean): number { + byteLength(headersOnly?: boolean): number { if (headersOnly || !this.transactions) return 80; return ( @@ -174,7 +174,7 @@ export class Block { } // TODO: buffer, offset compatibility - toBuffer(headersOnly: boolean): Buffer { + toBuffer(headersOnly?: boolean): Buffer { const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); let offset: number = 0; @@ -213,7 +213,7 @@ export class Block { return buffer; } - toHex(headersOnly: boolean): string { + toHex(headersOnly?: boolean): string { return this.toBuffer(headersOnly).toString('hex'); } diff --git a/ts_src/classify.ts b/ts_src/classify.ts index a7ec68c..319e934 100644 --- a/ts_src/classify.ts +++ b/ts_src/classify.ts @@ -38,7 +38,7 @@ function classifyOutput(script: Buffer): string { return types.NONSTANDARD; } -function classifyInput(script: Buffer, allowIncomplete: boolean): string { +function classifyInput(script: Buffer, allowIncomplete?: boolean): string { // XXX: optimization, below functions .decompile before use const chunks = decompile(script); if (!chunks) throw new TypeError('Invalid script'); @@ -51,7 +51,7 @@ function classifyInput(script: Buffer, allowIncomplete: boolean): string { return types.NONSTANDARD; } -function classifyWitness(script: Buffer[], allowIncomplete: boolean): string { +function classifyWitness(script: Buffer[], allowIncomplete?: boolean): string { // XXX: optimization, below functions .decompile before use const chunks = decompile(script); if (!chunks) throw new TypeError('Invalid script'); diff --git a/ts_src/index.ts b/ts_src/index.ts index 58c39c7..505407f 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -17,6 +17,12 @@ export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; export { ECPairInterface, Signer, SignerAsync } from './ecpair'; export { Network } from './networks'; -export { Payment, PaymentOpts, Stack, StackElement } from './payments'; +export { + Payment, + PaymentCreator, + PaymentOpts, + Stack, + StackElement, +} from './payments'; export { OpCode } from './script'; export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/ts_src/payments/index.ts b/ts_src/payments/index.ts index c48a6f1..4b7f111 100644 --- a/ts_src/payments/index.ts +++ b/ts_src/payments/index.ts @@ -25,6 +25,8 @@ export interface Payment { witness?: Buffer[]; } +export type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment; + export type PaymentFunction = () => Payment; export interface PaymentOpts { diff --git a/tslint.json b/tslint.json index 1ba998d..8a9cde6 100644 --- a/tslint.json +++ b/tslint.json @@ -16,7 +16,7 @@ "no-bitwise": false, "no-console": false, "no-empty": [true, "allow-empty-catch"], - "no-implicit-dependencies": true, + "no-implicit-dependencies": [true, "dev"], "no-return-await": true, "no-var-requires": false, "no-unused-expression": false, diff --git a/types/block.d.ts b/types/block.d.ts index dd4fc55..0cda4c8 100644 --- a/types/block.d.ts +++ b/types/block.d.ts @@ -16,12 +16,12 @@ export declare class Block { getWitnessCommit(): Buffer | null; hasWitnessCommit(): boolean; hasWitness(): boolean; - byteLength(headersOnly: boolean): number; + byteLength(headersOnly?: boolean): number; getHash(): Buffer; getId(): string; getUTCDate(): Date; - toBuffer(headersOnly: boolean): Buffer; - toHex(headersOnly: boolean): string; + toBuffer(headersOnly?: boolean): Buffer; + toHex(headersOnly?: boolean): string; checkTxRoots(): boolean; checkProofOfWork(): boolean; private __checkMerkleRoot; diff --git a/types/classify.d.ts b/types/classify.d.ts index 67f913b..d0f07b4 100644 --- a/types/classify.d.ts +++ b/types/classify.d.ts @@ -11,6 +11,6 @@ declare const types: { WITNESS_COMMITMENT: string; }; declare function classifyOutput(script: Buffer): string; -declare function classifyInput(script: Buffer, allowIncomplete: boolean): string; -declare function classifyWitness(script: Buffer[], allowIncomplete: boolean): string; +declare function classifyInput(script: Buffer, allowIncomplete?: boolean): string; +declare function classifyWitness(script: Buffer[], allowIncomplete?: boolean): string; export { classifyInput as input, classifyOutput as output, classifyWitness as witness, types, }; diff --git a/types/index.d.ts b/types/index.d.ts index fc5a932..68da119 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -14,6 +14,6 @@ export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; export { ECPairInterface, Signer, SignerAsync } from './ecpair'; export { Network } from './networks'; -export { Payment, PaymentOpts, Stack, StackElement } from './payments'; +export { Payment, PaymentCreator, PaymentOpts, Stack, StackElement, } from './payments'; export { OpCode } from './script'; export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/types/payments/index.d.ts b/types/payments/index.d.ts index 7f17b79..1edf071 100644 --- a/types/payments/index.d.ts +++ b/types/payments/index.d.ts @@ -24,6 +24,7 @@ export interface Payment { redeem?: Payment; witness?: Buffer[]; } +export declare type PaymentCreator = (a: Payment, opts?: PaymentOpts) => Payment; export declare type PaymentFunction = () => Payment; export interface PaymentOpts { validate?: boolean; From e3f7e76afca88a269b2977b32023da048f95de4b Mon Sep 17 00:00:00 2001 From: yuki akiyama <you2197901@gmail.com> Date: Wed, 11 Sep 2019 16:59:07 +0900 Subject: [PATCH 457/568] fix integration link. --- README.md | 74 +++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 6f46004..a94d0ae 100644 --- a/README.md +++ b/README.md @@ -89,46 +89,46 @@ Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). We will move towards replacing all instances of TransactionBuilder in the tests with the new Psbt. Currently we have a few examples on how to use the newer Psbt class at the following link: -- [Psbt examples](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions-psbt.js) +- [Psbt examples](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions-psbt.spec.ts) The rest of the examples are below (using TransactionBuilder for Transaction creation) -- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Generate a SegWit P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Generate a SegWit 3-of-4 multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Generate a SegWit 2-of-2 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Support the retrieval of transactions for an address (3rd party blockchain)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.js) -- [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2WPKH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2PK input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Verify a Transaction signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js) -- [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) -- [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) -- [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) -- [Create a BIP32, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) -- [Create a BIP44, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) -- [Create a BIP49, bitcoin testnet, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) -- [Use BIP39 to generate BIP32 addresses](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.js) -- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js) -- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js) -- [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js) -- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.js) -- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js) -- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js) -- [Create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js) -- [Create (and broadcast via 3PBP) a Transaction where Alice (mediator) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js) -- [Create (and broadcast via 3PBP) a Transaction where Alice (mediator) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.js) +- [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Generate a SegWit address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Generate a SegWit P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Generate a SegWit 3-of-4 multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Generate a SegWit 2-of-2 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Support the retrieval of transactions for an address (3rd party blockchain)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) +- [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2SH(P2WPKH) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2WPKH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit P2PK input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Verify a Transaction signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) +- [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) +- [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) +- [Create a BIP32, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) +- [Create a BIP44, bitcoin, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) +- [Create a BIP49, bitcoin testnet, account 0, external address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) +- [Use BIP39 to generate BIP32 addresses](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.spec.ts) +- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/cltv.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.spec.ts) +- [Create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction where Alice (mediator) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction where Alice (mediator) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/csv.spec.ts) If you have a use case that you feel could be listed here, please [ask for it](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new)! From f376913a4c102dcbca1cea5075a0a99482f86f1d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 12 Sep 2019 13:15:52 +0900 Subject: [PATCH 458/568] Remove TransactionBuilder from tests (besides transaction_builder.spec.ts) --- README.md | 11 +- test/integration/cltv.spec.ts | 45 +- test/integration/csv.spec.ts | 49 +- test/integration/payments.spec.ts | 45 +- test/integration/transactions-psbt.spec.ts | 669 ---------------- test/integration/transactions.spec.ts | 850 +++++++++++++-------- 6 files changed, 623 insertions(+), 1046 deletions(-) delete mode 100644 test/integration/transactions-psbt.spec.ts diff --git a/README.md b/README.md index a94d0ae..b034f20 100644 --- a/README.md +++ b/README.md @@ -85,14 +85,6 @@ The below examples are implemented as integration tests, they should be very eas Otherwise, pull requests are appreciated. Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). -### Warning: Currently the tests use TransactionBuilder, which will be removed in the future (v6.x.x or higher) -We will move towards replacing all instances of TransactionBuilder in the tests with the new Psbt. - -Currently we have a few examples on how to use the newer Psbt class at the following link: -- [Psbt examples](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions-psbt.spec.ts) - -The rest of the examples are below (using TransactionBuilder for Transaction creation) - - [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) @@ -104,7 +96,6 @@ The rest of the examples are below (using TransactionBuilder for Transaction cre - [Generate a Testnet address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Generate a Litecoin address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Create a 1-to-1 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) -- [Create a 2-to-2 Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a typical Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction with an OP\_RETURN output](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction with a 2-of-4 P2SH(multisig) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) @@ -112,7 +103,7 @@ The rest of the examples are below (using TransactionBuilder for Transaction cre - [Create (and broadcast via 3PBP) a Transaction with a SegWit P2WPKH input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction with a SegWit P2PK input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Create (and broadcast via 3PBP) a Transaction with a SegWit 3-of-4 P2SH(P2WSH(multisig)) input](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) -- [Verify a Transaction signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) +- [Create (and broadcast via 3PBP) a Transaction and sign with an HDSigner interface (bip32)](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts) - [Import a BIP32 testnet xpriv and export to WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) - [Export a BIP32 xpriv, then import it](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) - [Export a BIP32 xpub](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts) diff --git a/test/integration/cltv.spec.ts b/test/integration/cltv.spec.ts index afdcaa5..3bc0b14 100644 --- a/test/integration/cltv.spec.ts +++ b/test/integration/cltv.spec.ts @@ -5,6 +5,14 @@ import { regtestUtils } from './_regtest'; const regtest = regtestUtils.network; const bip65 = require('bip65'); +function toOutputScript(address: string): Buffer { + return bitcoin.address.toOutputScript(address, regtest); +} + +function idToHash(txid: string): Buffer { + return Buffer.from(txid, 'hex').reverse() as Buffer; +} + const alice = bitcoin.ECPair.fromWIF( 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest, @@ -13,7 +21,6 @@ const bob = bitcoin.ECPair.fromWIF( 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest, ); -console.warn = () => {}; // Silence the Deprecation Warning describe('bitcoinjs-lib (transactions w/ CLTV)', () => { // force update MTP @@ -59,14 +66,13 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => { // fund the P2SH(CLTV) address const unspent = await regtestUtils.faucet(address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.setLockTime(lockTime); + const tx = new bitcoin.Transaction(); + tx.locktime = lockTime; // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); + tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete(); const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { @@ -102,14 +108,13 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => { // fund the P2SH(CLTV) address const unspent = await regtestUtils.faucet(address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.setLockTime(lockTime); + const tx = new bitcoin.Transaction(); + tx.locktime = lockTime; // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); + tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete(); const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { @@ -147,14 +152,13 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => { // fund the P2SH(CLTV) address const unspent = await regtestUtils.faucet(address!, 2e5); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.setLockTime(lockTime); + const tx = new bitcoin.Transaction(); + tx.locktime = lockTime; // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 8e4); + tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 8e4); // {Alice's signature} {Bob's signature} OP_FALSE - const tx = txb.buildIncomplete(); const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { @@ -189,14 +193,13 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => { // fund the P2SH(CLTV) address const unspent = await regtestUtils.faucet(address!, 2e4); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.setLockTime(lockTime); + const tx = new bitcoin.Transaction(); + tx.locktime = lockTime; // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - txb.addInput(unspent.txId, unspent.vout, 0xfffffffe); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); + tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete(); const signatureHash = tx.hashForSignature(0, redeemScript, hashType); const redeemScriptSig = bitcoin.payments.p2sh({ redeem: { diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts index 0bfb970..7d4f4a4 100644 --- a/test/integration/csv.spec.ts +++ b/test/integration/csv.spec.ts @@ -5,6 +5,14 @@ import { regtestUtils } from './_regtest'; const regtest = regtestUtils.network; const bip68 = require('bip68'); +function toOutputScript(address: string): Buffer { + return bitcoin.address.toOutputScript(address, regtest); +} + +function idToHash(txid: string): Buffer { + return Buffer.from(txid, 'hex').reverse() as Buffer; +} + const alice = bitcoin.ECPair.fromWIF( 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest, @@ -21,7 +29,6 @@ const dave = bitcoin.ECPair.fromWIF( 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', regtest, ); -console.warn = () => {}; // Silence the Deprecation Warning describe('bitcoinjs-lib (transactions w/ CSV)', () => { // force update MTP @@ -111,12 +118,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // fund the P2SH(CSV) address const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout, sequence); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout, sequence); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete(); const signatureHash = tx.hashForSignature( 0, p2sh.redeem!.output!, @@ -164,12 +171,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // fund the P2SH(CSV) address const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout, sequence); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout, sequence); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4); // {Alice's signature} OP_TRUE - const tx = txb.buildIncomplete(); const signatureHash = tx.hashForSignature( 0, p2sh.redeem!.output!, @@ -219,12 +226,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // fund the P2SH(CCSV) address const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); // OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE - const tx = txb.buildIncomplete(); const signatureHash = tx.hashForSignature( 0, p2sh.redeem!.output!, @@ -282,12 +289,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // fund the P2SH(CCSV) address const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout, sequence1); // Set sequence1 for input - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout, sequence1); // Set sequence1 for input + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); // OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE - const tx = txb.buildIncomplete(); const signatureHash = tx.hashForSignature( 0, p2sh.redeem!.output!, @@ -345,12 +352,12 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // fund the P2SH(CCSV) address const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout, sequence2); // Set sequence2 for input - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 7e4); + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout, sequence2); // Set sequence2 for input + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); // {Alice mediator sig} OP_FALSE - const tx = txb.buildIncomplete(); const signatureHash = tx.hashForSignature( 0, p2sh.redeem!.output!, diff --git a/test/integration/payments.spec.ts b/test/integration/payments.spec.ts index 6592c2c..ef40387 100644 --- a/test/integration/payments.spec.ts +++ b/test/integration/payments.spec.ts @@ -6,7 +6,6 @@ const keyPairs = [ bitcoin.ECPair.makeRandom({ network: NETWORK }), bitcoin.ECPair.makeRandom({ network: NETWORK }), ]; -console.warn = () => {}; // Silence the Deprecation Warning async function buildAndSign( depends: any, @@ -15,37 +14,35 @@ async function buildAndSign( witnessScript: any, ) { const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4); + const utx = await regtestUtils.fetch(unspent.txId); - const txb = new bitcoin.TransactionBuilder(NETWORK); - txb.addInput(unspent.txId, unspent.vout, undefined, prevOutput); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); - - const posType = depends.prevOutScriptType; - const needsValue = !!witnessScript || posType.slice(-6) === 'p2wpkh'; + const psbt = new bitcoin.Psbt({ network: NETWORK }) + .addInput({ + hash: unspent.txId, + index: unspent.vout, + nonWitnessUtxo: Buffer.from(utx.txHex, 'hex'), + ...(redeemScript ? { redeemScript } : {}), + ...(witnessScript ? { witnessScript } : {}), + }) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }); if (depends.signatures) { keyPairs.forEach(keyPair => { - txb.sign({ - prevOutScriptType: posType, - vin: 0, - keyPair, - redeemScript, - witnessValue: needsValue ? unspent.value : undefined, - witnessScript, - }); + psbt.signInput(0, keyPair); }); } else if (depends.signature) { - txb.sign({ - prevOutScriptType: posType, - vin: 0, - keyPair: keyPairs[0], - redeemScript, - witnessValue: needsValue ? unspent.value : undefined, - witnessScript, - }); + psbt.signInput(0, keyPairs[0]); } - return regtestUtils.broadcast(txb.build().toHex()); + return regtestUtils.broadcast( + psbt + .finalizeAllInputs() + .extractTransaction() + .toHex(), + ); } ['p2ms', 'p2pk', 'p2pkh', 'p2wpkh'].forEach(k => { diff --git a/test/integration/transactions-psbt.spec.ts b/test/integration/transactions-psbt.spec.ts deleted file mode 100644 index a98407d..0000000 --- a/test/integration/transactions-psbt.spec.ts +++ /dev/null @@ -1,669 +0,0 @@ -import * as assert from 'assert'; -import { describe, it } from 'mocha'; -import * as bitcoin from '../..'; -import { regtestUtils } from './_regtest'; -import * as bip32 from 'bip32'; -const rng = require('randombytes'); -const regtest = regtestUtils.network; - -// See bottom of file for some helper functions used to make the payment objects needed. - -describe('bitcoinjs-lib (transactions with psbt)', () => { - it('can create a 1-to-1 Transaction', () => { - const alice = bitcoin.ECPair.fromWIF( - 'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr', - ); - const psbt = new bitcoin.Psbt(); - psbt.setVersion(2); // These are defaults. This line is not needed. - psbt.setLocktime(0); // These are defaults. This line is not needed. - psbt.addInput({ - // if hash is string, txid, if hash is Buffer, is reversed compared to txid - hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e', - index: 0, - sequence: 0xffffffff, // These are defaults. This line is not needed. - - // non-segwit inputs now require passing the whole previous tx as Buffer - nonWitnessUtxo: Buffer.from( - '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' + - '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + - 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + - '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + - '631e5e1e66009ce3710ceea5b1ad13ffffffff01' + - // value in satoshis (Int64LE) = 0x015f90 = 90000 - '905f010000000000' + - // scriptPubkey length - '19' + - // scriptPubkey - '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' + - // locktime - '00000000', - 'hex', - ), - - // // If this input was segwit, instead of nonWitnessUtxo, you would add - // // a witnessUtxo as follows. The scriptPubkey and the value only are needed. - // witnessUtxo: { - // script: Buffer.from( - // '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac', - // 'hex', - // ), - // value: 90000, - // }, - - // Not featured here: - // redeemScript. A Buffer of the redeemScript for P2SH - // witnessScript. A Buffer of the witnessScript for P2WSH - }); - psbt.addOutput({ - address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', - value: 80000, - }); - psbt.signInput(0, alice); - psbt.validateSignaturesOfInput(0); - psbt.finalizeAllInputs(); - assert.strictEqual( - psbt.extractTransaction().toHex(), - '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + - 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + - 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + - '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + - 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + - '08a22724efa6f6a07b0ec4c79aa88ac00000000', - ); - }); - - it('can create (and broadcast via 3PBP) a typical Transaction', async () => { - // these are { payment: Payment; keys: ECPair[] } - const alice1 = createPayment('p2pkh'); - const alice2 = createPayment('p2pkh'); - - // give Alice 2 unspent outputs - const inputData1 = await getInputData( - 5e4, - alice1.payment, - false, - 'noredeem', - ); - const inputData2 = await getInputData( - 7e4, - alice2.payment, - false, - 'noredeem', - ); - { - const { - hash, // string of txid or Buffer of tx hash. (txid and hash are reverse order) - index, // the output index of the txo you are spending - nonWitnessUtxo, // the full previous transaction as a Buffer - } = inputData1; - assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1); - } - - // network is only needed if you pass an address to addOutput - // using script (Buffer of scriptPubkey) instead will avoid needed network. - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData1) // alice1 unspent - .addInput(inputData2) // alice2 unspent - .addOutput({ - address: 'mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', - value: 8e4, - }) // the actual "spend" - .addOutput({ - address: alice2.payment.address, // OR script, which is a Buffer. - value: 1e4, - }); // Alice's change - // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee - - // Let's show a new feature with PSBT. - // We can have multiple signers sign in parrallel and combine them. - // (this is not necessary, but a nice feature) - - // encode to send out to the signers - const psbtBaseText = psbt.toBase64(); - - // each signer imports - const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText); - const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText); - - // Alice signs each input with the respective private keys - // signInput and signInputAsync are better - // (They take the input index explicitly as the first arg) - signer1.signAllInputs(alice1.keys[0]); - signer2.signAllInputs(alice2.keys[0]); - - // If your signer object's sign method returns a promise, use the following - // await signer2.signAllInputsAsync(alice2.keys[0]) - - // encode to send back to combiner (signer 1 and 2 are not near each other) - const s1text = signer1.toBase64(); - const s2text = signer2.toBase64(); - - const final1 = bitcoin.Psbt.fromBase64(s1text); - const final2 = bitcoin.Psbt.fromBase64(s2text); - - // final1.combine(final2) would give the exact same result - psbt.combine(final1, final2); - - // Finalizer wants to check all signatures are valid before finalizing. - // If the finalizer wants to check for specific pubkeys, the second arg - // can be passed. See the first multisig example below. - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); - assert.strictEqual(psbt.validateSignaturesOfInput(1), true); - - // This step it new. Since we separate the signing operation and - // the creation of the scriptSig and witness stack, we are able to - psbt.finalizeAllInputs(); - - // build and broadcast our RegTest network - await regtestUtils.broadcast(psbt.extractTransaction().toHex()); - // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 - }); - - it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => { - const alice1 = createPayment('p2pkh'); - const inputData1 = await getInputData( - 2e5, - alice1.payment, - false, - 'noredeem', - ); - - const data = Buffer.from('bitcoinjs-lib', 'utf8'); - const embed = bitcoin.payments.embed({ data: [data] }); - - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData1) - .addOutput({ - script: embed.output!, - value: 1000, - }) - .addOutput({ - address: regtestUtils.RANDOM_ADDRESS, - value: 1e5, - }) - .signInput(0, alice1.keys[0]); - - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); - psbt.finalizeAllInputs(); - - // build and broadcast to the RegTest network - await regtestUtils.broadcast(psbt.extractTransaction().toHex()); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => { - const multisig = createPayment('p2sh-p2ms(2 of 4)'); - const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh'); - { - const { - hash, - index, - nonWitnessUtxo, - redeemScript, // NEW: P2SH needs to give redeemScript when adding an input. - } = inputData1; - assert.deepStrictEqual( - { hash, index, nonWitnessUtxo, redeemScript }, - inputData1, - ); - } - - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData1) - .addOutput({ - address: regtestUtils.RANDOM_ADDRESS, - value: 1e4, - }) - .signInput(0, multisig.keys[0]) - .signInput(0, multisig.keys[2]); - - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); - assert.strictEqual( - psbt.validateSignaturesOfInput(0, multisig.keys[0].publicKey), - true, - ); - assert.throws(() => { - psbt.validateSignaturesOfInput(0, multisig.keys[3].publicKey); - }, new RegExp('No signatures for this pubkey')); - 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: 1e4, - }); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => { - const p2sh = createPayment('p2sh-p2wpkh'); - const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh'); - const inputData2 = await getInputData(5e4, p2sh.payment, true, 'p2sh'); - { - const { - hash, - index, - witnessUtxo, // NEW: this is an object of the output being spent { script: Buffer; value: Satoshis; } - redeemScript, - } = inputData; - assert.deepStrictEqual( - { hash, index, witnessUtxo, redeemScript }, - inputData, - ); - } - const keyPair = p2sh.keys[0]; - const outputData = { - script: p2sh.payment.output, // sending to myself for fun - value: 2e4, - }; - const outputData2 = { - script: p2sh.payment.output, // sending to myself for fun - value: 7e4, - }; - - const tx = new bitcoin.Psbt() - .addInputs([inputData, inputData2]) - .addOutputs([outputData, outputData2]) - .signAllInputs(keyPair) - .finalizeAllInputs() - .extractTransaction(); - - // build and broadcast to the Bitcoin RegTest network - await regtestUtils.broadcast(tx.toHex()); - - await regtestUtils.verify({ - txId: tx.getId(), - address: p2sh.payment.address, - vout: 0, - value: 2e4, - }); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input with nonWitnessUtxo', async () => { - // For learning purposes, ignore this test. - // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData - const p2sh = createPayment('p2sh-p2wpkh'); - const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh'); - const inputData2 = await getInputData(5e4, p2sh.payment, false, 'p2sh'); - const keyPair = p2sh.keys[0]; - const outputData = { - script: p2sh.payment.output, - value: 2e4, - }; - const outputData2 = { - script: p2sh.payment.output, - value: 7e4, - }; - const tx = new bitcoin.Psbt() - .addInputs([inputData, inputData2]) - .addOutputs([outputData, outputData2]) - .signAllInputs(keyPair) - .finalizeAllInputs() - .extractTransaction(); - await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ - txId: tx.getId(), - address: p2sh.payment.address, - vout: 0, - value: 2e4, - }); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { - // the only thing that changes is you don't give a redeemscript for input data - - const p2wpkh = createPayment('p2wpkh'); - const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem'); - { - const { hash, index, witnessUtxo } = inputData; - assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData); - } - - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData) - .addOutput({ - address: regtestUtils.RANDOM_ADDRESS, - value: 2e4, - }) - .signInput(0, p2wpkh.keys[0]); - - assert.strictEqual(psbt.validateSignaturesOfInput(0), 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, - }); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input with nonWitnessUtxo', async () => { - // For learning purposes, ignore this test. - // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData - const p2wpkh = createPayment('p2wpkh'); - const inputData = await getInputData( - 5e4, - p2wpkh.payment, - false, - 'noredeem', - ); - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData) - .addOutput({ - address: regtestUtils.RANDOM_ADDRESS, - value: 2e4, - }) - .signInput(0, p2wpkh.keys[0]); - psbt.finalizeAllInputs(); - const tx = psbt.extractTransaction(); - await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 2e4, - }); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => { - const p2wsh = createPayment('p2wsh-p2pk'); - const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh'); - { - const { - hash, - index, - witnessUtxo, - witnessScript, // NEW: A Buffer of the witnessScript - } = inputData; - assert.deepStrictEqual( - { hash, index, witnessUtxo, witnessScript }, - inputData, - ); - } - - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData) - .addOutput({ - address: regtestUtils.RANDOM_ADDRESS, - value: 2e4, - }) - .signInput(0, p2wsh.keys[0]); - - assert.strictEqual(psbt.validateSignaturesOfInput(0), 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, - }); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input with nonWitnessUtxo', async () => { - // For learning purposes, ignore this test. - // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData - const p2wsh = createPayment('p2wsh-p2pk'); - const inputData = await getInputData(5e4, p2wsh.payment, false, 'p2wsh'); - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData) - .addOutput({ - address: regtestUtils.RANDOM_ADDRESS, - value: 2e4, - }) - .signInput(0, p2wsh.keys[0]); - psbt.finalizeAllInputs(); - const tx = psbt.extractTransaction(); - await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 2e4, - }); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { - const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); - const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh'); - { - const { - hash, - index, - witnessUtxo, - redeemScript, - witnessScript, - } = inputData; - assert.deepStrictEqual( - { hash, index, witnessUtxo, redeemScript, witnessScript }, - inputData, - ); - } - - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData) - .addOutput({ - address: regtestUtils.RANDOM_ADDRESS, - value: 2e4, - }) - .signInput(0, p2sh.keys[0]) - .signInput(0, p2sh.keys[2]) - .signInput(0, p2sh.keys[3]); - - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); - assert.strictEqual( - psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey), - true, - ); - assert.throws(() => { - psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey); - }, new RegExp('No signatures for this pubkey')); - 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, - }); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo', async () => { - // For learning purposes, ignore this test. - // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData - const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); - const inputData = await getInputData( - 5e4, - p2sh.payment, - false, - 'p2sh-p2wsh', - ); - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData) - .addOutput({ - address: regtestUtils.RANDOM_ADDRESS, - value: 2e4, - }) - .signInput(0, p2sh.keys[0]) - .signInput(0, p2sh.keys[2]) - .signInput(0, p2sh.keys[3]); - psbt.finalizeAllInputs(); - const tx = psbt.extractTransaction(); - await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - 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.validateSignaturesOfInput(0), true); - assert.strictEqual( - psbt.validateSignaturesOfInput(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: string, myKeys?: any[], network?: any) { - network = network || regtest; - const splitType = _type.split('-').reverse(); - const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; - const keys = myKeys || []; - let m: number | undefined; - if (isMultisig) { - const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/); - m = parseInt(match![1]); - let n = parseInt(match![2]); - 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--; - } - } - if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network })); - - let payment: any; - splitType.forEach(type => { - if (type.slice(0, 4) === 'p2ms') { - payment = bitcoin.payments.p2ms({ - m, - pubkeys: keys.map(key => key.publicKey).sort(), - network, - }); - } else if (['p2sh', 'p2wsh'].indexOf(type) > -1) { - payment = (bitcoin.payments as any)[type]({ - redeem: payment, - network, - }); - } else { - payment = (bitcoin.payments as any)[type]({ - pubkey: keys[0].publicKey, - network, - }); - } - }); - - return { - payment, - keys, - }; -} - -function getWitnessUtxo(out: any) { - delete out.address; - out.script = Buffer.from(out.script, 'hex'); - return out; -} - -async function getInputData( - amount: number, - payment: any, - isSegwit: boolean, - redeemType: string, -) { - const unspent = await regtestUtils.faucetComplex(payment.output, amount); - const utx = await regtestUtils.fetch(unspent.txId); - // for non segwit inputs, you must pass the full transaction buffer - const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex'); - // for segwit inputs, you only need the output script and value as an object. - const witnessUtxo = getWitnessUtxo(utx.outs[unspent.vout]); - const mixin = isSegwit ? { witnessUtxo } : { nonWitnessUtxo }; - const mixin2: any = {}; - switch (redeemType) { - case 'p2sh': - mixin2.redeemScript = payment.redeem.output; - break; - case 'p2wsh': - mixin2.witnessScript = payment.redeem.output; - break; - case 'p2sh-p2wsh': - mixin2.witnessScript = payment.redeem.redeem.output; - mixin2.redeemScript = payment.redeem.output; - break; - } - return { - hash: unspent.txId, - index: unspent.vout, - ...mixin, - ...mixin2, - }; -} diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts index 8a313f2..a98407d 100644 --- a/test/integration/transactions.spec.ts +++ b/test/integration/transactions.spec.ts @@ -2,190 +2,230 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; +import * as bip32 from 'bip32'; +const rng = require('randombytes'); const regtest = regtestUtils.network; -console.warn = () => {}; // Silence the Deprecation Warning -function rng() { - return Buffer.from('YT8dAtK4d16A3P1z+TpwB2jJ4aFH3g9M1EioIBkLEV4=', 'base64'); -} +// See bottom of file for some helper functions used to make the payment objects needed. -describe('bitcoinjs-lib (transactions)', () => { +describe('bitcoinjs-lib (transactions with psbt)', () => { it('can create a 1-to-1 Transaction', () => { const alice = bitcoin.ECPair.fromWIF( - 'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy', + 'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr', ); - const txb = new bitcoin.TransactionBuilder(); + const psbt = new bitcoin.Psbt(); + psbt.setVersion(2); // These are defaults. This line is not needed. + psbt.setLocktime(0); // These are defaults. This line is not needed. + psbt.addInput({ + // if hash is string, txid, if hash is Buffer, is reversed compared to txid + hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e', + index: 0, + sequence: 0xffffffff, // These are defaults. This line is not needed. - txb.setVersion(1); - txb.addInput( - '61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', - 0, - ); // Alice's previous transaction output, has 15000 satoshis - txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000); - // (in)15000 - (out)12000 = (fee)3000, this is the miner fee + // non-segwit inputs now require passing the whole previous tx as Buffer + nonWitnessUtxo: Buffer.from( + '0200000001f9f34e95b9d5c8abcd20fc5bd4a825d1517be62f0f775e5f36da944d9' + + '452e550000000006b483045022100c86e9a111afc90f64b4904bd609e9eaed80d48' + + 'ca17c162b1aca0a788ac3526f002207bb79b60d4fc6526329bf18a77135dc566020' + + '9e761da46e1c2f1152ec013215801210211755115eabf846720f5cb18f248666fec' + + '631e5e1e66009ce3710ceea5b1ad13ffffffff01' + + // value in satoshis (Int64LE) = 0x015f90 = 90000 + '905f010000000000' + + // scriptPubkey length + '19' + + // scriptPubkey + '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac' + + // locktime + '00000000', + 'hex', + ), - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair: alice, + // // If this input was segwit, instead of nonWitnessUtxo, you would add + // // a witnessUtxo as follows. The scriptPubkey and the value only are needed. + // witnessUtxo: { + // script: Buffer.from( + // '76a9148bbc95d2709c71607c60ee3f097c1217482f518d88ac', + // 'hex', + // ), + // value: 90000, + // }, + + // Not featured here: + // redeemScript. A Buffer of the redeemScript for P2SH + // witnessScript. A Buffer of the witnessScript for P2WSH }); - - // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below + psbt.addOutput({ + address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', + value: 80000, + }); + psbt.signInput(0, alice); + psbt.validateSignaturesOfInput(0); + psbt.finalizeAllInputs(); assert.strictEqual( - txb.build().toHex(), - '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000', - ); - }); - - it('can create a 2-to-2 Transaction', () => { - const alice = bitcoin.ECPair.fromWIF( - 'L1Knwj9W3qK3qMKdTvmg3VfzUs3ij2LETTFhxza9LfD5dngnoLG1', - ); - const bob = bitcoin.ECPair.fromWIF( - 'KwcN2pT3wnRAurhy7qMczzbkpY5nXMW2ubh696UBc1bcwctTx26z', - ); - - const txb = new bitcoin.TransactionBuilder(); - txb.setVersion(1); - txb.addInput( - 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c', - 6, - ); // Alice's previous transaction output, has 200000 satoshis - txb.addInput( - '7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730', - 0, - ); // Bob's previous transaction output, has 300000 satoshis - txb.addOutput('1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb', 180000); - txb.addOutput('1JtK9CQw1syfWj1WtFMWomrYdV3W2tWBF9', 170000); - // (in)(200000 + 300000) - (out)(180000 + 170000) = (fee)150000, this is the miner fee - - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 1, - keyPair: bob, - }); // Bob signs his input, which was the second input (1th) - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair: alice, - }); // Alice signs her input, which was the first input (0th) - - // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below - assert.strictEqual( - txb.build().toHex(), - '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000', + psbt.extractTransaction().toHex(), + '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + + 'b067d000000006b483045022100931b6db94aed25d5486884d83fc37160f37f3368c0' + + 'd7f48c757112abefec983802205fda64cff98c849577026eb2ce916a50ea70626a766' + + '9f8596dd89b720a26b4d501210365db9da3f8a260078a7e8f8b708a1161468fb2323f' + + 'fda5ec16b261ec1056f455ffffffff0180380100000000001976a914ca0d36044e0dc' + + '08a22724efa6f6a07b0ec4c79aa88ac00000000', ); }); it('can create (and broadcast via 3PBP) a typical Transaction', async () => { - const alice1 = bitcoin.ECPair.makeRandom({ network: regtest }); - const alice2 = bitcoin.ECPair.makeRandom({ network: regtest }); - const aliceChange = bitcoin.ECPair.makeRandom({ - network: regtest, - rng: rng, - }); - - const alice1pkh = bitcoin.payments.p2pkh({ - pubkey: alice1.publicKey, - network: regtest, - }); - const alice2pkh = bitcoin.payments.p2pkh({ - pubkey: alice2.publicKey, - network: regtest, - }); - const aliceCpkh = bitcoin.payments.p2pkh({ - pubkey: aliceChange.publicKey, - network: regtest, - }); + // these are { payment: Payment; keys: ECPair[] } + const alice1 = createPayment('p2pkh'); + const alice2 = createPayment('p2pkh'); // give Alice 2 unspent outputs - const unspent0 = await regtestUtils.faucet(alice1pkh.address!, 5e4); + const inputData1 = await getInputData( + 5e4, + alice1.payment, + false, + 'noredeem', + ); + const inputData2 = await getInputData( + 7e4, + alice2.payment, + false, + 'noredeem', + ); + { + const { + hash, // string of txid or Buffer of tx hash. (txid and hash are reverse order) + index, // the output index of the txo you are spending + nonWitnessUtxo, // the full previous transaction as a Buffer + } = inputData1; + assert.deepStrictEqual({ hash, index, nonWitnessUtxo }, inputData1); + } - const unspent1 = await regtestUtils.faucet(alice2pkh.address!, 7e4); - - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent0.txId, unspent0.vout); // alice1 unspent - txb.addInput(unspent1.txId, unspent1.vout); // alice2 unspent - txb.addOutput('mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', 8e4); // the actual "spend" - txb.addOutput(aliceCpkh.address!, 1e4); // Alice's change + // network is only needed if you pass an address to addOutput + // using script (Buffer of scriptPubkey) instead will avoid needed network. + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData1) // alice1 unspent + .addInput(inputData2) // alice2 unspent + .addOutput({ + address: 'mwCwTceJvYV27KXBc3NJZys6CjsgsoeHmf', + value: 8e4, + }) // the actual "spend" + .addOutput({ + address: alice2.payment.address, // OR script, which is a Buffer. + value: 1e4, + }); // Alice's change // (in)(5e4 + 7e4) - (out)(8e4 + 1e4) = (fee)3e4 = 30000, this is the miner fee + // Let's show a new feature with PSBT. + // We can have multiple signers sign in parrallel and combine them. + // (this is not necessary, but a nice feature) + + // encode to send out to the signers + const psbtBaseText = psbt.toBase64(); + + // each signer imports + const signer1 = bitcoin.Psbt.fromBase64(psbtBaseText); + const signer2 = bitcoin.Psbt.fromBase64(psbtBaseText); + // Alice signs each input with the respective private keys - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair: alice1, - }); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 1, - keyPair: alice2, - }); + // signInput and signInputAsync are better + // (They take the input index explicitly as the first arg) + signer1.signAllInputs(alice1.keys[0]); + signer2.signAllInputs(alice2.keys[0]); + + // If your signer object's sign method returns a promise, use the following + // await signer2.signAllInputsAsync(alice2.keys[0]) + + // encode to send back to combiner (signer 1 and 2 are not near each other) + const s1text = signer1.toBase64(); + const s2text = signer2.toBase64(); + + const final1 = bitcoin.Psbt.fromBase64(s1text); + const final2 = bitcoin.Psbt.fromBase64(s2text); + + // final1.combine(final2) would give the exact same result + psbt.combine(final1, final2); + + // Finalizer wants to check all signatures are valid before finalizing. + // If the finalizer wants to check for specific pubkeys, the second arg + // can be passed. See the first multisig example below. + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(1), true); + + // This step it new. Since we separate the signing operation and + // the creation of the scriptSig and witness stack, we are able to + psbt.finalizeAllInputs(); // build and broadcast our RegTest network - await regtestUtils.broadcast(txb.build().toHex()); + await regtestUtils.broadcast(psbt.extractTransaction().toHex()); // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 }); it('can create (and broadcast via 3PBP) a Transaction with an OP_RETURN output', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); - const p2pkh = bitcoin.payments.p2pkh({ - pubkey: keyPair.publicKey, - network: regtest, - }); + const alice1 = createPayment('p2pkh'); + const inputData1 = await getInputData( + 2e5, + alice1.payment, + false, + 'noredeem', + ); - const unspent = await regtestUtils.faucet(p2pkh.address!, 2e5); - - const txb = new bitcoin.TransactionBuilder(regtest); const data = Buffer.from('bitcoinjs-lib', 'utf8'); const embed = bitcoin.payments.embed({ data: [data] }); - txb.addInput(unspent.txId, unspent.vout); - txb.addOutput(embed.output!, 1000); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e5); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - }); + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData1) + .addOutput({ + script: embed.output!, + value: 1000, + }) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 1e5, + }) + .signInput(0, alice1.keys[0]); + + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + psbt.finalizeAllInputs(); // build and broadcast to the RegTest network - await regtestUtils.broadcast(txb.build().toHex()); + await regtestUtils.broadcast(psbt.extractTransaction().toHex()); }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2MS(2 of 4)) (multisig) input', async () => { - const keyPairs = [ - bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }), - ]; - const pubkeys = keyPairs.map(x => x.publicKey); - const p2ms = bitcoin.payments.p2ms({ - m: 2, - pubkeys: pubkeys, - network: regtest, - }); - const p2sh = bitcoin.payments.p2sh({ redeem: p2ms, network: regtest }); + const multisig = createPayment('p2sh-p2ms(2 of 4)'); + const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh'); + { + const { + hash, + index, + nonWitnessUtxo, + redeemScript, // NEW: P2SH needs to give redeemScript when adding an input. + } = inputData1; + assert.deepStrictEqual( + { hash, index, nonWitnessUtxo, redeemScript }, + inputData1, + ); + } - const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData1) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 1e4, + }) + .signInput(0, multisig.keys[0]) + .signInput(0, multisig.keys[2]); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 1e4); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual( + psbt.validateSignaturesOfInput(0, multisig.keys[0].publicKey), + true, + ); + assert.throws(() => { + psbt.validateSignaturesOfInput(0, multisig.keys[3].publicKey); + }, new RegExp('No signatures for this pubkey')); + psbt.finalizeAllInputs(); - txb.sign({ - prevOutScriptType: 'p2sh-p2ms', - vin: 0, - keyPair: keyPairs[0], - redeemScript: p2sh.redeem!.output, - }); - txb.sign({ - prevOutScriptType: 'p2sh-p2ms', - vin: 0, - keyPair: keyPairs[2], - redeemScript: p2sh.redeem!.output, - }); - const tx = txb.build(); + const tx = psbt.extractTransaction(); // build and broadcast to the Bitcoin RegTest network await regtestUtils.broadcast(tx.toHex()); @@ -199,27 +239,101 @@ describe('bitcoinjs-lib (transactions)', () => { }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); - const p2wpkh = bitcoin.payments.p2wpkh({ - pubkey: keyPair.publicKey, - network: regtest, + const p2sh = createPayment('p2sh-p2wpkh'); + const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh'); + const inputData2 = await getInputData(5e4, p2sh.payment, true, 'p2sh'); + { + const { + hash, + index, + witnessUtxo, // NEW: this is an object of the output being spent { script: Buffer; value: Satoshis; } + redeemScript, + } = inputData; + assert.deepStrictEqual( + { hash, index, witnessUtxo, redeemScript }, + inputData, + ); + } + const keyPair = p2sh.keys[0]; + const outputData = { + script: p2sh.payment.output, // sending to myself for fun + value: 2e4, + }; + const outputData2 = { + script: p2sh.payment.output, // sending to myself for fun + value: 7e4, + }; + + const tx = new bitcoin.Psbt() + .addInputs([inputData, inputData2]) + .addOutputs([outputData, outputData2]) + .signAllInputs(keyPair) + .finalizeAllInputs() + .extractTransaction(); + + // build and broadcast to the Bitcoin RegTest network + await regtestUtils.broadcast(tx.toHex()); + + await regtestUtils.verify({ + txId: tx.getId(), + address: p2sh.payment.address, + vout: 0, + value: 2e4, }); - const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: regtest }); + }); - const unspent = await regtestUtils.faucet(p2sh.address!, 5e4); - - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); - txb.sign({ - prevOutScriptType: 'p2sh-p2wpkh', - vin: 0, - keyPair: keyPair, - redeemScript: p2sh.redeem!.output, - witnessValue: unspent.value, + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WPKH) input with nonWitnessUtxo', async () => { + // For learning purposes, ignore this test. + // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData + const p2sh = createPayment('p2sh-p2wpkh'); + const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh'); + const inputData2 = await getInputData(5e4, p2sh.payment, false, 'p2sh'); + const keyPair = p2sh.keys[0]; + const outputData = { + script: p2sh.payment.output, + value: 2e4, + }; + const outputData2 = { + script: p2sh.payment.output, + value: 7e4, + }; + const tx = new bitcoin.Psbt() + .addInputs([inputData, inputData2]) + .addOutputs([outputData, outputData2]) + .signAllInputs(keyPair) + .finalizeAllInputs() + .extractTransaction(); + await regtestUtils.broadcast(tx.toHex()); + await regtestUtils.verify({ + txId: tx.getId(), + address: p2sh.payment.address, + vout: 0, + value: 2e4, }); + }); - const tx = txb.build(); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { + // the only thing that changes is you don't give a redeemscript for input data + + const p2wpkh = createPayment('p2wpkh'); + const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem'); + { + const { hash, index, witnessUtxo } = inputData; + assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData); + } + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2wpkh.keys[0]); + + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + psbt.finalizeAllInputs(); + + const tx = psbt.extractTransaction(); // build and broadcast to the Bitcoin RegTest network await regtestUtils.broadcast(tx.toHex()); @@ -232,30 +346,26 @@ describe('bitcoinjs-lib (transactions)', () => { }); }); - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); - const p2wpkh = bitcoin.payments.p2wpkh({ - pubkey: keyPair.publicKey, - network: regtest, - }); - - const unspent = await regtestUtils.faucetComplex(p2wpkh.output!, 5e4); - - // XXX: build the Transaction w/ a P2WPKH input - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout, undefined, p2wpkh.output); // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); - txb.sign({ - prevOutScriptType: 'p2wpkh', - vin: 0, - keyPair: keyPair, - witnessValue: unspent.value, - }); // NOTE: no redeem script - const tx = txb.build(); - - // build and broadcast (the P2WPKH transaction) to the Bitcoin RegTest network + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input with nonWitnessUtxo', async () => { + // For learning purposes, ignore this test. + // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData + const p2wpkh = createPayment('p2wpkh'); + const inputData = await getInputData( + 5e4, + p2wpkh.payment, + false, + 'noredeem', + ); + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2wpkh.keys[0]); + psbt.finalizeAllInputs(); + const tx = psbt.extractTransaction(); await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, @@ -265,29 +375,35 @@ describe('bitcoinjs-lib (transactions)', () => { }); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input', async () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: regtest }); - const p2pk = bitcoin.payments.p2pk({ - pubkey: keyPair.publicKey, - network: regtest, - }); - const p2wsh = bitcoin.payments.p2wsh({ redeem: p2pk, network: regtest }); + const p2wsh = createPayment('p2wsh-p2pk'); + const inputData = await getInputData(5e4, p2wsh.payment, true, 'p2wsh'); + { + const { + hash, + index, + witnessUtxo, + witnessScript, // NEW: A Buffer of the witnessScript + } = inputData; + assert.deepStrictEqual( + { hash, index, witnessUtxo, witnessScript }, + inputData, + ); + } - const unspent = await regtestUtils.faucetComplex(p2wsh.output!, 5e4); + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2wsh.keys[0]); - // XXX: build the Transaction w/ a P2WSH input - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout, undefined, p2wsh.output); // NOTE: provide the prevOutScript! - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 2e4); - txb.sign({ - prevOutScriptType: 'p2wsh-p2pk', - vin: 0, - keyPair: keyPair, - witnessValue: 5e4, - witnessScript: p2wsh.redeem!.output, - }); // NOTE: provide a witnessScript! - const tx = txb.build(); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + psbt.finalizeAllInputs(); - // build and broadcast (the P2WSH transaction) to the Bitcoin RegTest network + const tx = psbt.extractTransaction(); + + // build and broadcast to the Bitcoin RegTest network await regtestUtils.broadcast(tx.toHex()); await regtestUtils.verify({ @@ -298,50 +414,67 @@ describe('bitcoinjs-lib (transactions)', () => { }); }); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WSH(P2PK) input with nonWitnessUtxo', async () => { + // For learning purposes, ignore this test. + // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData + const p2wsh = createPayment('p2wsh-p2pk'); + const inputData = await getInputData(5e4, p2wsh.payment, false, 'p2wsh'); + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2wsh.keys[0]); + psbt.finalizeAllInputs(); + const tx = psbt.extractTransaction(); + await regtestUtils.broadcast(tx.toHex()); + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4, + }); + }); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { - const keyPairs = [ - bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }), - bitcoin.ECPair.makeRandom({ network: regtest }), - ]; - const pubkeys = keyPairs.map(x => x.publicKey); + const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); + const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh'); + { + const { + hash, + index, + witnessUtxo, + redeemScript, + witnessScript, + } = inputData; + assert.deepStrictEqual( + { hash, index, witnessUtxo, redeemScript, witnessScript }, + inputData, + ); + } - const p2ms = bitcoin.payments.p2ms({ m: 3, pubkeys, network: regtest }); - const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network: regtest }); - const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network: regtest }); + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2sh.keys[0]) + .signInput(0, p2sh.keys[2]) + .signInput(0, p2sh.keys[3]); - const unspent = await regtestUtils.faucet(p2sh.address!, 6e4); + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual( + psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey), + true, + ); + assert.throws(() => { + psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey); + }, new RegExp('No signatures for this pubkey')); + psbt.finalizeAllInputs(); - const txb = new bitcoin.TransactionBuilder(regtest); - txb.addInput(unspent.txId, unspent.vout, undefined, p2sh.output); - txb.addOutput(regtestUtils.RANDOM_ADDRESS, 3e4); - txb.sign({ - prevOutScriptType: 'p2sh-p2wsh-p2ms', - vin: 0, - keyPair: keyPairs[0], - redeemScript: p2sh.redeem!.output, - witnessValue: unspent.value, - witnessScript: p2wsh.redeem!.output, - }); - txb.sign({ - prevOutScriptType: 'p2sh-p2wsh-p2ms', - vin: 0, - keyPair: keyPairs[2], - redeemScript: p2sh.redeem!.output, - witnessValue: unspent.value, - witnessScript: p2wsh.redeem!.output, - }); - txb.sign({ - prevOutScriptType: 'p2sh-p2wsh-p2ms', - vin: 0, - keyPair: keyPairs[3], - redeemScript: p2sh.redeem!.output, - witnessValue: unspent.value, - witnessScript: p2wsh.redeem!.output, - }); - - const tx = txb.build(); + const tx = psbt.extractTransaction(); // build and broadcast to the Bitcoin RegTest network await regtestUtils.broadcast(tx.toHex()); @@ -350,72 +483,187 @@ describe('bitcoinjs-lib (transactions)', () => { txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, vout: 0, - value: 3e4, + value: 2e4, }); }); - it('can verify Transaction (P2PKH) signatures', () => { - const txHex = - '010000000321c5f7e7bc98b3feda84aad36a5c99a02bcb8823a2f3eccbcd5da209698b5c20000000006b48304502210099e021772830207cf7c55b69948d3b16b4dcbf1f55a9cd80ebf8221a169735f9022064d33f11d62cd28240b3862afc0b901adc9f231c7124dd19bdb30367b61964c50121032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63dffffffff8a75ce85441ddb3f342708ee33cc8ed418b07d9ba9e0e7c4e1cccfe9f52d8a88000000006946304302207916c23dae212c95a920423902fa44e939fb3d542f4478a7b46e9cde53705800021f0d74e9504146e404c1b8f9cba4dff2d4782e3075491c9ed07ce4a7d1c4461a01210216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2affffffffdfef93f69fe32e944fad79fa8f882b3a155d80383252348caba1a77a5abbf7ef000000006b483045022100faa6e9ca289b46c64764a624c59ac30d9abcf1d4a04c4de9089e67cbe0d300a502206930afa683f6807502de5c2431bf9a1fd333c8a2910a76304df0f3d23d83443f0121039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18fffffffff01ff4b0000000000001976a9146c86476d1d85cd60116cd122a274e6a570a5a35c88acc96d0700'; - const keyPairs = [ - '032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d', - '0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a', - '039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f', - ].map(q => { - return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')); - }); - - const tx = bitcoin.Transaction.fromHex(txHex); - - tx.ins.forEach((input, i) => { - const keyPair = keyPairs[i]; - const p2pkh = bitcoin.payments.p2pkh({ - pubkey: keyPair.publicKey, - input: input.script, - }); - - const ss = bitcoin.script.signature.decode(p2pkh.signature!); - const hash = tx.hashForSignature(i, p2pkh.output!, ss.hashType); - - assert.strictEqual(keyPair.verify(hash, ss.signature), true); + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo', async () => { + // For learning purposes, ignore this test. + // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData + const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); + const inputData = await getInputData( + 5e4, + p2sh.payment, + false, + 'p2sh-p2wsh', + ); + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2sh.keys[0]) + .signInput(0, p2sh.keys[2]) + .signInput(0, p2sh.keys[3]); + psbt.finalizeAllInputs(); + const tx = psbt.extractTransaction(); + await regtestUtils.broadcast(tx.toHex()); + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4, }); }); - it('can verify Transaction (P2SH(P2WPKH)) signatures', () => { - const utxos = { - 'f72d1d83ac40fcedd01415751556a905844ab5f44bbb7728565ebb91b1590109:0': { - value: 50000, - }, + 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); + } - const txHex = - '02000000000101090159b191bb5e562877bb4bf4b54a8405a95615751514d0edfc40ac831d2df7000000001716001435a179e5516947a39ae9c8a25e9fe62c0fc598edffffffff01204e0000000000001976a91431d43308d3c886d53e9ae8a45728370571ff456988ac0247304402206ec41f685b997a51f325b07ee852e82a535f6b52ef54485cc133e05168aa052a022070bafa86108acb51c77b2b259ae8fb7fd1efa10fef804fcfe9b13c2db719acf5012103fb03e9d0a9af86cbed94225dbb8bb70f6b82109bce0a61ddcf41dab6cbb4871100000000'; - const tx = bitcoin.Transaction.fromHex(txHex); + // You can add extra attributes for updateData into the addInput(s) object(s) + Object.assign(inputData, updateData); - tx.ins.forEach((input, i) => { - const txId = (Buffer.from(input.hash).reverse() as Buffer).toString( - 'hex', - ); - const utxo = (utxos as any)[`${txId}:${i}`]; - if (!utxo) throw new Error('Missing utxo'); + 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!!! - const p2sh = bitcoin.payments.p2sh({ - input: input.script, - witness: input.witness, - }); - const p2wpkh = bitcoin.payments.p2wpkh(p2sh.redeem!); - const p2pkh = bitcoin.payments.p2pkh({ pubkey: p2wpkh.pubkey }); // because P2WPKH is annoying + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual( + psbt.validateSignaturesOfInput(0, childNode.publicKey), + true, + ); + psbt.finalizeAllInputs(); - const ss = bitcoin.script.signature.decode(p2wpkh.signature!); - const hash = tx.hashForWitnessV0( - i, - p2pkh.output!, - utxo.value, - ss.hashType, - ); - const keyPair = bitcoin.ECPair.fromPublicKey(p2wpkh.pubkey!); // aka, cQ3EtF4mApRcogNGSeyPTKbmfxxn3Yfb1wecfKSws9a8bnYuxoAk + const tx = psbt.extractTransaction(); - assert.strictEqual(keyPair.verify(hash, ss.signature), true); + // 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: string, myKeys?: any[], network?: any) { + network = network || regtest; + const splitType = _type.split('-').reverse(); + const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; + const keys = myKeys || []; + let m: number | undefined; + if (isMultisig) { + const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/); + m = parseInt(match![1]); + let n = parseInt(match![2]); + 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--; + } + } + if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network })); + + let payment: any; + splitType.forEach(type => { + if (type.slice(0, 4) === 'p2ms') { + payment = bitcoin.payments.p2ms({ + m, + pubkeys: keys.map(key => key.publicKey).sort(), + network, + }); + } else if (['p2sh', 'p2wsh'].indexOf(type) > -1) { + payment = (bitcoin.payments as any)[type]({ + redeem: payment, + network, + }); + } else { + payment = (bitcoin.payments as any)[type]({ + pubkey: keys[0].publicKey, + network, + }); + } + }); + + return { + payment, + keys, + }; +} + +function getWitnessUtxo(out: any) { + delete out.address; + out.script = Buffer.from(out.script, 'hex'); + return out; +} + +async function getInputData( + amount: number, + payment: any, + isSegwit: boolean, + redeemType: string, +) { + const unspent = await regtestUtils.faucetComplex(payment.output, amount); + const utx = await regtestUtils.fetch(unspent.txId); + // for non segwit inputs, you must pass the full transaction buffer + const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex'); + // for segwit inputs, you only need the output script and value as an object. + const witnessUtxo = getWitnessUtxo(utx.outs[unspent.vout]); + const mixin = isSegwit ? { witnessUtxo } : { nonWitnessUtxo }; + const mixin2: any = {}; + switch (redeemType) { + case 'p2sh': + mixin2.redeemScript = payment.redeem.output; + break; + case 'p2wsh': + mixin2.witnessScript = payment.redeem.output; + break; + case 'p2sh-p2wsh': + mixin2.witnessScript = payment.redeem.redeem.output; + mixin2.redeemScript = payment.redeem.output; + break; + } + return { + hash: unspent.txId, + index: unspent.vout, + ...mixin, + ...mixin2, + }; +} From 34b0b525fc78736c009465f1a8e602f565d5c3d3 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 12 Sep 2019 15:20:33 +0900 Subject: [PATCH 459/568] Update @types/node and fix problems with types --- package-lock.json | 13 ++++++++++--- package.json | 2 +- test/integration/cltv.spec.ts | 2 +- test/integration/csv.spec.ts | 2 +- test/psbt.spec.ts | 2 +- types/address.d.ts | 1 - types/block.d.ts | 1 - types/bufferutils.d.ts | 1 - types/classify.d.ts | 1 - types/crypto.d.ts | 1 - types/ecpair.d.ts | 1 - types/payments/index.d.ts | 1 - types/psbt.d.ts | 1 - types/script.d.ts | 1 - types/script_number.d.ts | 1 - types/script_signature.d.ts | 1 - types/templates/multisig/input.d.ts | 1 - types/templates/multisig/output.d.ts | 1 - types/templates/nulldata.d.ts | 1 - types/templates/pubkey/input.d.ts | 1 - types/templates/pubkey/output.d.ts | 1 - types/templates/pubkeyhash/input.d.ts | 1 - types/templates/pubkeyhash/output.d.ts | 1 - types/templates/scripthash/input.d.ts | 1 - types/templates/scripthash/output.d.ts | 1 - types/templates/witnesscommitment/output.d.ts | 1 - types/templates/witnesspubkeyhash/input.d.ts | 1 - types/templates/witnesspubkeyhash/output.d.ts | 1 - types/templates/witnessscripthash/input.d.ts | 1 - types/templates/witnessscripthash/output.d.ts | 1 - types/transaction.d.ts | 1 - types/transaction_builder.d.ts | 1 - 32 files changed, 14 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5812365..4c3dd29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -156,9 +156,9 @@ "dev": true }, "@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + "version": "12.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz", + "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==" }, "@types/proxyquire": { "version": "1.3.28", @@ -261,6 +261,13 @@ "tiny-secp256k1": "^1.1.0", "typeforce": "^1.11.5", "wif": "^2.0.6" + }, + "dependencies": { + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + } } }, "bip39": { diff --git a/package.json b/package.json index 700b7fd..f624f76 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "types" ], "dependencies": { - "@types/node": "10.12.18", + "@types/node": "12.7.5", "bech32": "^1.1.2", "bip174": "^1.0.1", "bip32": "^2.0.4", diff --git a/test/integration/cltv.spec.ts b/test/integration/cltv.spec.ts index 3bc0b14..f83f0e2 100644 --- a/test/integration/cltv.spec.ts +++ b/test/integration/cltv.spec.ts @@ -10,7 +10,7 @@ function toOutputScript(address: string): Buffer { } function idToHash(txid: string): Buffer { - return Buffer.from(txid, 'hex').reverse() as Buffer; + return Buffer.from(txid, 'hex').reverse(); } const alice = bitcoin.ECPair.fromWIF( diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts index 7d4f4a4..316d227 100644 --- a/test/integration/csv.spec.ts +++ b/test/integration/csv.spec.ts @@ -10,7 +10,7 @@ function toOutputScript(address: string): Buffer { } function idToHash(txid: string): Buffer { - return Buffer.from(txid, 'hex').reverse() as Buffer; + return Buffer.from(txid, 'hex').reverse(); } const alice = bitcoin.ECPair.fromWIF( diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 02a420d..39647b1 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -14,7 +14,7 @@ const initBuffers = (object: any): typeof preFixtures => const data = result[1]; const encoding = result[2]; - return Buffer.from(data, encoding); + return Buffer.from(data, encoding as any); }); const fixtures = initBuffers(preFixtures); diff --git a/types/address.d.ts b/types/address.d.ts index be0e00a..5c7ed5a 100644 --- a/types/address.d.ts +++ b/types/address.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Network } from './networks'; export interface Base58CheckResult { hash: Buffer; diff --git a/types/block.d.ts b/types/block.d.ts index 0cda4c8..d77bb1b 100644 --- a/types/block.d.ts +++ b/types/block.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Transaction } from './transaction'; export declare class Block { static fromBuffer(buffer: Buffer): Block; diff --git a/types/bufferutils.d.ts b/types/bufferutils.d.ts index 2686e4e..1eff78d 100644 --- a/types/bufferutils.d.ts +++ b/types/bufferutils.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function readUInt64LE(buffer: Buffer, offset: number): number; export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number; export declare function reverseBuffer(buffer: Buffer): Buffer; diff --git a/types/classify.d.ts b/types/classify.d.ts index d0f07b4..6ea4754 100644 --- a/types/classify.d.ts +++ b/types/classify.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> declare const types: { P2MS: string; NONSTANDARD: string; diff --git a/types/crypto.d.ts b/types/crypto.d.ts index 1743681..5d93acd 100644 --- a/types/crypto.d.ts +++ b/types/crypto.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function ripemd160(buffer: Buffer): Buffer; export declare function sha1(buffer: Buffer): Buffer; export declare function sha256(buffer: Buffer): Buffer; diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts index 0b69dfe..447c608 100644 --- a/types/ecpair.d.ts +++ b/types/ecpair.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Network } from './networks'; interface ECPairOptions { compressed?: boolean; diff --git a/types/payments/index.d.ts b/types/payments/index.d.ts index 1edf071..922e0bf 100644 --- a/types/payments/index.d.ts +++ b/types/payments/index.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Network } from '../networks'; import { p2data as embed } from './embed'; import { p2ms } from './p2ms'; diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 4db075c..b1bacea 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; diff --git a/types/script.d.ts b/types/script.d.ts index 52ad4dd..4b04615 100644 --- a/types/script.d.ts +++ b/types/script.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Stack } from './payments'; import * as scriptNumber from './script_number'; import * as scriptSignature from './script_signature'; diff --git a/types/script_number.d.ts b/types/script_number.d.ts index 015bb89..cf535fc 100644 --- a/types/script_number.d.ts +++ b/types/script_number.d.ts @@ -1,3 +1,2 @@ -/// <reference types="node" /> export declare function decode(buffer: Buffer, maxLength?: number, minimal?: boolean): number; export declare function encode(_number: number): Buffer; diff --git a/types/script_signature.d.ts b/types/script_signature.d.ts index 2057dd9..fbf18d5 100644 --- a/types/script_signature.d.ts +++ b/types/script_signature.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> interface ScriptSignature { signature: Buffer; hashType: number; diff --git a/types/templates/multisig/input.d.ts b/types/templates/multisig/input.d.ts index a207dd6..6d46515 100644 --- a/types/templates/multisig/input.d.ts +++ b/types/templates/multisig/input.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Stack } from '../../payments'; export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean; export declare namespace check { diff --git a/types/templates/multisig/output.d.ts b/types/templates/multisig/output.d.ts index a207dd6..6d46515 100644 --- a/types/templates/multisig/output.d.ts +++ b/types/templates/multisig/output.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Stack } from '../../payments'; export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean; export declare namespace check { diff --git a/types/templates/nulldata.d.ts b/types/templates/nulldata.d.ts index aff3cd0..bf6497c 100644 --- a/types/templates/nulldata.d.ts +++ b/types/templates/nulldata.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare namespace check { var toJSON: () => string; diff --git a/types/templates/pubkey/input.d.ts b/types/templates/pubkey/input.d.ts index c4ffeab..4b70d2d 100644 --- a/types/templates/pubkey/input.d.ts +++ b/types/templates/pubkey/input.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Stack } from '../../payments'; export declare function check(script: Buffer | Stack): boolean; export declare namespace check { diff --git a/types/templates/pubkey/output.d.ts b/types/templates/pubkey/output.d.ts index c4ffeab..4b70d2d 100644 --- a/types/templates/pubkey/output.d.ts +++ b/types/templates/pubkey/output.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Stack } from '../../payments'; export declare function check(script: Buffer | Stack): boolean; export declare namespace check { diff --git a/types/templates/pubkeyhash/input.d.ts b/types/templates/pubkeyhash/input.d.ts index c4ffeab..4b70d2d 100644 --- a/types/templates/pubkeyhash/input.d.ts +++ b/types/templates/pubkeyhash/input.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Stack } from '../../payments'; export declare function check(script: Buffer | Stack): boolean; export declare namespace check { diff --git a/types/templates/pubkeyhash/output.d.ts b/types/templates/pubkeyhash/output.d.ts index 091758f..d66d8ac 100644 --- a/types/templates/pubkeyhash/output.d.ts +++ b/types/templates/pubkeyhash/output.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare namespace check { var toJSON: () => string; diff --git a/types/templates/scripthash/input.d.ts b/types/templates/scripthash/input.d.ts index a04d03c..14bbd5b 100644 --- a/types/templates/scripthash/input.d.ts +++ b/types/templates/scripthash/input.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function check(script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean; export declare namespace check { var toJSON: () => string; diff --git a/types/templates/scripthash/output.d.ts b/types/templates/scripthash/output.d.ts index 091758f..d66d8ac 100644 --- a/types/templates/scripthash/output.d.ts +++ b/types/templates/scripthash/output.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare namespace check { var toJSON: () => string; diff --git a/types/templates/witnesscommitment/output.d.ts b/types/templates/witnesscommitment/output.d.ts index 778c9a1..ec155a2 100644 --- a/types/templates/witnesscommitment/output.d.ts +++ b/types/templates/witnesscommitment/output.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare namespace check { var toJSON: () => string; diff --git a/types/templates/witnesspubkeyhash/input.d.ts b/types/templates/witnesspubkeyhash/input.d.ts index c4ffeab..4b70d2d 100644 --- a/types/templates/witnesspubkeyhash/input.d.ts +++ b/types/templates/witnesspubkeyhash/input.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Stack } from '../../payments'; export declare function check(script: Buffer | Stack): boolean; export declare namespace check { diff --git a/types/templates/witnesspubkeyhash/output.d.ts b/types/templates/witnesspubkeyhash/output.d.ts index 091758f..d66d8ac 100644 --- a/types/templates/witnesspubkeyhash/output.d.ts +++ b/types/templates/witnesspubkeyhash/output.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare namespace check { var toJSON: () => string; diff --git a/types/templates/witnessscripthash/input.d.ts b/types/templates/witnessscripthash/input.d.ts index b2a6e8a..767df3a 100644 --- a/types/templates/witnessscripthash/input.d.ts +++ b/types/templates/witnessscripthash/input.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function check(chunks: Buffer[], allowIncomplete?: boolean): boolean; export declare namespace check { var toJSON: () => string; diff --git a/types/templates/witnessscripthash/output.d.ts b/types/templates/witnessscripthash/output.d.ts index 091758f..d66d8ac 100644 --- a/types/templates/witnessscripthash/output.d.ts +++ b/types/templates/witnessscripthash/output.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export declare function check(script: Buffer | Array<number | Buffer>): boolean; export declare namespace check { var toJSON: () => string; diff --git a/types/transaction.d.ts b/types/transaction.d.ts index 9bdba19..d7462b4 100644 --- a/types/transaction.d.ts +++ b/types/transaction.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> export interface BlankOutput { script: Buffer; valueBuffer: Buffer; diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts index 2799464..a80fc0f 100644 --- a/types/transaction_builder.d.ts +++ b/types/transaction_builder.d.ts @@ -1,4 +1,3 @@ -/// <reference types="node" /> import { Signer } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; From 9810049f4cec0421dbe66a8d1c761547ac4e1fba Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 12 Sep 2019 17:35:08 +0900 Subject: [PATCH 460/568] Add tslint to tests --- .travis.yml | 2 + package.json | 1 + test/bitcoin.core.spec.ts | 21 +- test/block.spec.ts | 4 +- test/bufferutils.spec.ts | 4 +- test/classify.spec.ts | 4 +- test/integration/addresses.spec.ts | 32 +- test/integration/bip32.spec.ts | 10 +- test/integration/blocks.spec.ts | 9 +- test/integration/cltv.spec.ts | 330 ++++++++------- test/integration/csv.spec.ts | 571 ++++++++++++++------------ test/integration/payments.spec.ts | 2 +- test/integration/transactions.spec.ts | 169 ++++---- test/payments.spec.ts | 6 +- test/payments.utils.ts | 17 +- test/psbt.spec.ts | 4 +- test/script.spec.ts | 16 +- test/script_signature.spec.ts | 9 +- test/transaction.spec.ts | 8 +- test/transaction_builder.spec.ts | 106 +++-- tslint.json | 2 +- 21 files changed, 749 insertions(+), 578 deletions(-) diff --git a/.travis.yml b/.travis.yml index bb87738..f7aad0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,8 @@ matrix: env: TEST_SUITE=gitdiff:ci - node_js: "lts/*" env: TEST_SUITE=lint + - node_js: "lts/*" + env: TEST_SUITE=lint:tests - node_js: "lts/*" env: TEST_SUITE=coverage env: diff --git a/package.json b/package.json index f624f76..093a097 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "gitdiff:ci": "npm run build && git diff --exit-code", "integration": "npm run build && npm run nobuild:integration", "lint": "tslint -p tsconfig.json -c tslint.json", + "lint:tests": "tslint -p test/tsconfig.json -c tslint.json", "mocha:ts": "mocha --recursive --require test/ts-node-register", "nobuild:coverage-report": "nyc report --reporter=lcov", "nobuild:coverage-html": "nyc report --reporter=html", diff --git a/test/bitcoin.core.spec.ts b/test/bitcoin.core.spec.ts index 55b16ac..94b74e3 100644 --- a/test/bitcoin.core.spec.ts +++ b/test/bitcoin.core.spec.ts @@ -7,8 +7,8 @@ import * as base58KeysInvalid from './fixtures/core/base58_keys_invalid.json'; import * as base58KeysValid from './fixtures/core/base58_keys_valid.json'; import * as blocksValid from './fixtures/core/blocks.json'; import * as sigCanonical from './fixtures/core/sig_canonical.json'; -import * as sigHash from './fixtures/core/sighash.json'; import * as sigNoncanonical from './fixtures/core/sig_noncanonical.json'; +import * as sigHash from './fixtures/core/sighash.json'; import * as txValid from './fixtures/core/tx_valid.json'; describe('Bitcoin-core', () => { @@ -72,11 +72,11 @@ describe('Bitcoin-core', () => { ]; base58KeysInvalid.forEach(f => { - const string = f[0]; + const strng = f[0]; - it('throws on ' + string, () => { + it('throws on ' + strng, () => { assert.throws(() => { - const address = bitcoin.address.fromBase58Check(string); + const address = bitcoin.address.fromBase58Check(strng); assert.notStrictEqual( allowedNetworks.indexOf(address.version), @@ -121,11 +121,11 @@ describe('Bitcoin-core', () => { ]; base58KeysInvalid.forEach(f => { - const string = f[0]; + const strng = f[0]; - it('throws on ' + string, () => { + it('throws on ' + strng, () => { assert.throws(() => { - bitcoin.ECPair.fromWIF(string, allowedNetworks); + bitcoin.ECPair.fromWIF(strng, allowedNetworks); }, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/); }); }); @@ -242,9 +242,14 @@ describe('Bitcoin-core', () => { const buffer = Buffer.from(hex, 'hex'); it('throws on ' + description, () => { + const reg = new RegExp( + 'Expected DER (integer|sequence)|(R|S) value (excessively ' + + 'padded|is negative)|(R|S|DER sequence) length is (zero|too ' + + 'short|too long|invalid)|Invalid hashType', + ); assert.throws(() => { bitcoin.script.signature.decode(buffer); - }, /Expected DER (integer|sequence)|(R|S) value (excessively padded|is negative)|(R|S|DER sequence) length is (zero|too short|too long|invalid)|Invalid hashType/); + }, reg); }); }); }); diff --git a/test/block.spec.ts b/test/block.spec.ts index 6f8ed03..0f74392 100644 --- a/test/block.spec.ts +++ b/test/block.spec.ts @@ -8,7 +8,9 @@ describe('Block', () => { describe('version', () => { it('should be interpreted as an int32le', () => { const blockHex = - 'ffffffff0000000000000000000000000000000000000000000000000000000000000000414141414141414141414141414141414141414141414141414141414141414101000000020000000300000000'; + 'ffffffff000000000000000000000000000000000000000000000000000000000000' + + '00004141414141414141414141414141414141414141414141414141414141414141' + + '01000000020000000300000000'; const block = Block.fromHex(blockHex); assert.strictEqual(-1, block.version); assert.strictEqual(1, block.timestamp); diff --git a/test/bufferutils.spec.ts b/test/bufferutils.spec.ts index 4308af9..33ad8f5 100644 --- a/test/bufferutils.spec.ts +++ b/test/bufferutils.spec.ts @@ -9,9 +9,9 @@ describe('bufferutils', () => { fixtures.valid.forEach(f => { it('decodes ' + f.hex, () => { const buffer = Buffer.from(f.hex, 'hex'); - const number = bufferutils.readUInt64LE(buffer, 0); + const num = bufferutils.readUInt64LE(buffer, 0); - assert.strictEqual(number, f.dec); + assert.strictEqual(num, f.dec); }); }); diff --git a/test/classify.spec.ts b/test/classify.spec.ts index b2464c0..931250e 100644 --- a/test/classify.spec.ts +++ b/test/classify.spec.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; -import * as bscript from '../src/script'; import * as classify from '../src/classify'; +import * as bscript from '../src/script'; import * as fixtures from './fixtures/templates.json'; @@ -10,9 +10,9 @@ import * as nullData from '../src/templates/nulldata'; import * as pubKey from '../src/templates/pubkey'; import * as pubKeyHash from '../src/templates/pubkeyhash'; import * as scriptHash from '../src/templates/scripthash'; +import * as witnessCommitment from '../src/templates/witnesscommitment'; import * as witnessPubKeyHash from '../src/templates/witnesspubkeyhash'; import * as witnessScriptHash from '../src/templates/witnessscripthash'; -import * as witnessCommitment from '../src/templates/witnesscommitment'; const tmap = { pubKey, diff --git a/test/integration/addresses.spec.ts b/test/integration/addresses.spec.ts index 49dc578..d8feb7c 100644 --- a/test/integration/addresses.spec.ts +++ b/test/integration/addresses.spec.ts @@ -6,23 +6,27 @@ const dhttp = regtestUtils.dhttp; const TESTNET = bitcoin.networks.testnet; describe('bitcoinjs-lib (addresses)', () => { - it('can generate a random address [and support the retrieval of transactions for that address (via 3PBP)', async () => { - const keyPair = bitcoin.ECPair.makeRandom(); - const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); + it( + 'can generate a random address [and support the retrieval of ' + + 'transactions for that address (via 3PBP)]', + async () => { + const keyPair = bitcoin.ECPair.makeRandom(); + const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); - // bitcoin P2PKH addresses start with a '1' - assert.strictEqual(address!.startsWith('1'), true); + // bitcoin P2PKH addresses start with a '1' + assert.strictEqual(address!.startsWith('1'), true); - const result = await dhttp({ - method: 'GET', - url: 'https://blockchain.info/rawaddr/' + address, - }); + const result = await dhttp({ + method: 'GET', + url: 'https://blockchain.info/rawaddr/' + address, + }); - // random private keys [probably!] have no transactions - assert.strictEqual((result as any).n_tx, 0); - assert.strictEqual((result as any).total_received, 0); - assert.strictEqual((result as any).total_sent, 0); - }); + // random private keys [probably!] have no transactions + assert.strictEqual((result as any).n_tx, 0); + assert.strictEqual((result as any).total_received, 0); + assert.strictEqual((result as any).total_sent, 0); + }, + ); it('can import an address via WIF', () => { const keyPair = bitcoin.ECPair.fromWIF( diff --git a/test/integration/bip32.spec.ts b/test/integration/bip32.spec.ts index 1279d78..7cd9e2f 100644 --- a/test/integration/bip32.spec.ts +++ b/test/integration/bip32.spec.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; -import { describe, it } from 'mocha'; import * as bip32 from 'bip32'; import * as bip39 from 'bip39'; +import { describe, it } from 'mocha'; import * as bitcoin from '../..'; function getAddress(node: any, network?: any): string { @@ -25,8 +25,8 @@ describe('bitcoinjs-lib (BIP32)', () => { 'praise you muffin lion enable neck grocery crumble super myself license ghost'; const seed = bip39.mnemonicToSeedSync(mnemonic); const node = bip32.fromSeed(seed); - const string = node.toBase58(); - const restored = bip32.fromBase58(string); + const strng = node.toBase58(); + const restored = bip32.fromBase58(strng); assert.strictEqual(getAddress(node), getAddress(restored)); // same public key assert.strictEqual(node.toWIF(), restored.toWIF()); // same private key @@ -37,10 +37,10 @@ describe('bitcoinjs-lib (BIP32)', () => { 'praise you muffin lion enable neck grocery crumble super myself license ghost'; const seed = bip39.mnemonicToSeedSync(mnemonic); const node = bip32.fromSeed(seed); - const string = node.neutered().toBase58(); + const strng = node.neutered().toBase58(); assert.strictEqual( - string, + strng, 'xpub661MyMwAqRbcGhVeaVfEBA25e3cP9DsJQZoE8iep5fZSxy3TnPBNBgWnMZx56oreNc48ZoTkQfatNJ9VWnQ7ZcLZcVStpaXLTeG8bGrzX3n', ); }); diff --git a/test/integration/blocks.spec.ts b/test/integration/blocks.spec.ts index 5eed0fc..a98c5eb 100644 --- a/test/integration/blocks.spec.ts +++ b/test/integration/blocks.spec.ts @@ -6,7 +6,14 @@ describe('bitcoinjs-lib (blocks)', () => { it('can extract a height from a CoinBase transaction', () => { // from 00000000000000000097669cdca131f24d40c4cc7d80eaa65967a2d09acf6ce6 const txHex = - '010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98fd16761d220400000000000000aa340000d49f0000ffffffff02b07fc366000000001976a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12bf1e400120000000000000000000000000000000000000000000000000000000000000000000000000'; + '010000000001010000000000000000000000000000000000000000000000000000000' + + '000000000ffffffff50037f9a07174d696e656420627920416e74506f6f6c685b205a' + + '2b1f7bfabe6d6d36afe1910eca9405b66f97750940a656e38e2c0312958190ff8e98f' + + 'd16761d220400000000000000aa340000d49f0000ffffffff02b07fc3660000000019' + + '76a9148349212dc27ce3ab4c5b29b85c4dec643d764b1788ac0000000000000000266' + + 'a24aa21a9ed72d9432948505e3d3062f1307a3f027a5dea846ff85e47159680919c12' + + 'bf1e40012000000000000000000000000000000000000000000000000000000000000' + + '0000000000000'; const tx = bitcoin.Transaction.fromHex(txHex); assert.strictEqual(tx.ins.length, 1); diff --git a/test/integration/cltv.spec.ts b/test/integration/cltv.spec.ts index f83f0e2..d5141c7 100644 --- a/test/integration/cltv.spec.ts +++ b/test/integration/cltv.spec.ts @@ -30,8 +30,14 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => { const hashType = bitcoin.Transaction.SIGHASH_ALL; - type keyPair = { publicKey: Buffer }; - function cltvCheckSigOutput(aQ: keyPair, bQ: keyPair, lockTime: number) { + interface KeyPair { + publicKey: Buffer; + } + function cltvCheckSigOutput( + aQ: KeyPair, + bQ: KeyPair, + lockTime: number, + ): Buffer { return bitcoin.script.fromASM( ` OP_IF @@ -50,173 +56,201 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => { ); } - function utcNow() { + function utcNow(): number { return Math.floor(Date.now() / 1000); } // expiry past, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the past)', async () => { - // 3 hours ago - const lockTime = bip65.encode({ utc: utcNow() - 3600 * 3 }); - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); - const { address } = bitcoin.payments.p2sh({ - redeem: { output: redeemScript, network: regtest }, - network: regtest, - }); + it( + 'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' + + 'the output after the expiry (in the past)', + async () => { + // 3 hours ago + const lockTime = bip65.encode({ utc: utcNow() - 3600 * 3 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); - // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address!, 1e5); - const tx = new bitcoin.Transaction(); - tx.locktime = lockTime; - // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); + // fund the P2SH(CLTV) address + const unspent = await regtestUtils.faucet(address!, 1e5); + const tx = new bitcoin.Transaction(); + tx.locktime = lockTime; + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. + tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - // {Alice's signature} OP_TRUE - const signatureHash = tx.hashForSignature(0, redeemScript, hashType); - const redeemScriptSig = bitcoin.payments.p2sh({ - redeem: { - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE, - ]), - output: redeemScript, - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + // {Alice's signature} OP_TRUE + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode( + alice.sign(signatureHash), + hashType, + ), + bitcoin.opcodes.OP_TRUE, + ]), + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - await regtestUtils.broadcast(tx.toHex()); + await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4, - }); - }); + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4, + }); + }, + ); // expiry will pass, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future)', async () => { - const height = await regtestUtils.height(); - // 5 blocks from now - const lockTime = bip65.encode({ blocks: height + 5 }); - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); - const { address } = bitcoin.payments.p2sh({ - redeem: { output: redeemScript, network: regtest }, - network: regtest, - }); + it( + 'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' + + 'the output after the expiry (in the future)', + async () => { + const height = await regtestUtils.height(); + // 5 blocks from now + const lockTime = bip65.encode({ blocks: height + 5 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); - // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address!, 1e5); - const tx = new bitcoin.Transaction(); - tx.locktime = lockTime; - // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); + // fund the P2SH(CLTV) address + const unspent = await regtestUtils.faucet(address!, 1e5); + const tx = new bitcoin.Transaction(); + tx.locktime = lockTime; + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. + tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - // {Alice's signature} OP_TRUE - const signatureHash = tx.hashForSignature(0, redeemScript, hashType); - const redeemScriptSig = bitcoin.payments.p2sh({ - redeem: { - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE, - ]), - output: redeemScript, - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + // {Alice's signature} OP_TRUE + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode( + alice.sign(signatureHash), + hashType, + ), + bitcoin.opcodes.OP_TRUE, + ]), + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently - // ... - // into the future! - await regtestUtils.mine(5); - await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4, - }); - }); + // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently + // ... + // into the future! + await regtestUtils.mine(5); + await regtestUtils.broadcast(tx.toHex()); + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4, + }); + }, + ); // expiry ignored, {Bob's signature} {Alice's signature} OP_FALSE - it('can create (and broadcast via 3PBP) a Transaction where Alice and Bob can redeem the output at any time', async () => { - // two hours ago - const lockTime = bip65.encode({ utc: utcNow() - 3600 * 2 }); - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); - const { address } = bitcoin.payments.p2sh({ - redeem: { output: redeemScript, network: regtest }, - network: regtest, - }); + it( + 'can create (and broadcast via 3PBP) a Transaction where Alice and Bob can ' + + 'redeem the output at any time', + async () => { + // two hours ago + const lockTime = bip65.encode({ utc: utcNow() - 3600 * 2 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); - // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address!, 2e5); - const tx = new bitcoin.Transaction(); - tx.locktime = lockTime; - // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 8e4); + // fund the P2SH(CLTV) address + const unspent = await regtestUtils.faucet(address!, 2e5); + const tx = new bitcoin.Transaction(); + tx.locktime = lockTime; + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. + tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 8e4); - // {Alice's signature} {Bob's signature} OP_FALSE - const signatureHash = tx.hashForSignature(0, redeemScript, hashType); - const redeemScriptSig = bitcoin.payments.p2sh({ - redeem: { - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_FALSE, - ]), - output: redeemScript, - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + // {Alice's signature} {Bob's signature} OP_FALSE + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode( + alice.sign(signatureHash), + hashType, + ), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_FALSE, + ]), + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 8e4, - }); - }); + await regtestUtils.broadcast(tx.toHex()); + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 8e4, + }); + }, + ); // expiry in the future, {Alice's signature} OP_TRUE - it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry', async () => { - // two hours from now - const lockTime = bip65.encode({ utc: utcNow() + 3600 * 2 }); - const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); - const { address } = bitcoin.payments.p2sh({ - redeem: { output: redeemScript, network: regtest }, - network: regtest, - }); + it( + 'can create (but fail to broadcast via 3PBP) a Transaction where Alice ' + + 'attempts to redeem before the expiry', + async () => { + // two hours from now + const lockTime = bip65.encode({ utc: utcNow() + 3600 * 2 }); + const redeemScript = cltvCheckSigOutput(alice, bob, lockTime); + const { address } = bitcoin.payments.p2sh({ + redeem: { output: redeemScript, network: regtest }, + network: regtest, + }); - // fund the P2SH(CLTV) address - const unspent = await regtestUtils.faucet(address!, 2e4); - const tx = new bitcoin.Transaction(); - tx.locktime = lockTime; - // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. - tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4); + // fund the P2SH(CLTV) address + const unspent = await regtestUtils.faucet(address!, 2e4); + const tx = new bitcoin.Transaction(); + tx.locktime = lockTime; + // Note: nSequence MUST be <= 0xfffffffe otherwise LockTime is ignored, and is immediately spendable. + tx.addInput(idToHash(unspent.txId), unspent.vout, 0xfffffffe); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4); - // {Alice's signature} OP_TRUE - const signatureHash = tx.hashForSignature(0, redeemScript, hashType); - const redeemScriptSig = bitcoin.payments.p2sh({ - redeem: { - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE, - ]), - output: redeemScript, - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + // {Alice's signature} OP_TRUE + const signatureHash = tx.hashForSignature(0, redeemScript, hashType); + const redeemScriptSig = bitcoin.payments.p2sh({ + redeem: { + input: bitcoin.script.compile([ + bitcoin.script.signature.encode( + alice.sign(signatureHash), + hashType, + ), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE, + ]), + output: redeemScript, + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); - await regtestUtils.broadcast(tx.toHex()).catch(err => { - assert.throws(() => { - if (err) throw err; - }, /Error: non-final \(code 64\)/); - }); - }); + await regtestUtils.broadcast(tx.toHex()).catch(err => { + assert.throws(() => { + if (err) throw err; + }, /Error: non-final \(code 64\)/); + }); + }, + ); }); diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts index 316d227..d6f99c7 100644 --- a/test/integration/csv.spec.ts +++ b/test/integration/csv.spec.ts @@ -38,9 +38,15 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { const hashType = bitcoin.Transaction.SIGHASH_ALL; - type keyPair = { publicKey: Buffer }; + interface KeyPair { + publicKey: Buffer; + } // IF MTP (from when confirmed) > seconds, _alice can redeem - function csvCheckSigOutput(_alice: keyPair, _bob: keyPair, sequence: number) { + function csvCheckSigOutput( + _alice: KeyPair, + _bob: KeyPair, + sequence: number, + ): Buffer { return bitcoin.script.fromASM( ` OP_IF @@ -62,17 +68,20 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // 2 of 3 multisig of _bob, _charles, _dave, // but after sequence1 time, _alice can allow the multisig to become 1 of 3. // but after sequence2 time, _alice can sign for the output all by themself. + + /* tslint:disable-next-line */ // Ref: https://github.com/bitcoinbook/bitcoinbook/blob/f8b883dcd4e3d1b9adf40fed59b7e898fbd9241f/ch07.asciidoc#complex-script-example + // Note: bitcoinjs-lib will not offer specific support for problems with // advanced script usages such as below. Use at your own risk. function complexCsvOutput( - _alice: keyPair, - _bob: keyPair, - _charles: keyPair, - _dave: keyPair, + _alice: KeyPair, + _bob: KeyPair, + _charles: KeyPair, + _dave: KeyPair, sequence1: number, sequence2: number, - ) { + ): Buffer { return bitcoin.script.fromASM( ` OP_IF @@ -105,287 +114,319 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { } // expiry will pass, {Alice's signature} OP_TRUE - it('can create (and broadcast via 3PBP) a Transaction where Alice can redeem the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)', async () => { - // 5 blocks from now - const sequence = bip68.encode({ blocks: 5 }); - const p2sh = bitcoin.payments.p2sh({ - redeem: { - output: csvCheckSigOutput(alice, bob, sequence), - }, - network: regtest, - }); - - // fund the P2SH(CSV) address - const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - - const tx = new bitcoin.Transaction(); - tx.version = 2; - tx.addInput(idToHash(unspent.txId), unspent.vout, sequence); - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - - // {Alice's signature} OP_TRUE - const signatureHash = tx.hashForSignature( - 0, - p2sh.redeem!.output!, - hashType, - ); - const redeemScriptSig = bitcoin.payments.p2sh({ - network: regtest, - redeem: { + it( + 'can create (and broadcast via 3PBP) a Transaction where Alice can redeem ' + + 'the output after the expiry (in the future) (simple CHECKSEQUENCEVERIFY)', + async () => { + // 5 blocks from now + const sequence = bip68.encode({ blocks: 5 }); + const p2sh = bitcoin.payments.p2sh({ + redeem: { + output: csvCheckSigOutput(alice, bob, sequence), + }, network: regtest, - output: p2sh.redeem!.output, - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE, - ]), - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + }); - // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently - // ... - // into the future! - await regtestUtils.mine(10); + // fund the P2SH(CSV) address + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - await regtestUtils.broadcast(tx.toHex()); + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout, sequence); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4, - }); - }); + // {Alice's signature} OP_TRUE + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem!.output, + input: bitcoin.script.compile([ + bitcoin.script.signature.encode( + alice.sign(signatureHash), + hashType, + ), + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); + + // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently + // ... + // into the future! + await regtestUtils.mine(10); + + await regtestUtils.broadcast(tx.toHex()); + + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4, + }); + }, + ); // expiry in the future, {Alice's signature} OP_TRUE - it('can create (but fail to broadcast via 3PBP) a Transaction where Alice attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)', async () => { - // two hours after confirmation - const sequence = bip68.encode({ seconds: 7168 }); - const p2sh = bitcoin.payments.p2sh({ - network: regtest, - redeem: { - output: csvCheckSigOutput(alice, bob, sequence), - }, - }); - - // fund the P2SH(CSV) address - const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); - - const tx = new bitcoin.Transaction(); - tx.version = 2; - tx.addInput(idToHash(unspent.txId), unspent.vout, sequence); - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4); - - // {Alice's signature} OP_TRUE - const signatureHash = tx.hashForSignature( - 0, - p2sh.redeem!.output!, - hashType, - ); - const redeemScriptSig = bitcoin.payments.p2sh({ - network: regtest, - redeem: { + it( + 'can create (but fail to broadcast via 3PBP) a Transaction where Alice ' + + 'attempts to redeem before the expiry (simple CHECKSEQUENCEVERIFY)', + async () => { + // two hours after confirmation + const sequence = bip68.encode({ seconds: 7168 }); + const p2sh = bitcoin.payments.p2sh({ network: regtest, - output: p2sh.redeem!.output, - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.opcodes.OP_TRUE, - ]), - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + redeem: { + output: csvCheckSigOutput(alice, bob, sequence), + }, + }); - await regtestUtils.broadcast(tx.toHex()).catch(err => { - assert.throws(() => { - if (err) throw err; - }, /Error: non-BIP68-final \(code 64\)/); - }); - }); + // fund the P2SH(CSV) address + const unspent = await regtestUtils.faucet(p2sh.address!, 2e4); + + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout, sequence); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 1e4); + + // {Alice's signature} OP_TRUE + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem!.output, + input: bitcoin.script.compile([ + bitcoin.script.signature.encode( + alice.sign(signatureHash), + hashType, + ), + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); + + await regtestUtils.broadcast(tx.toHex()).catch(err => { + assert.throws(() => { + if (err) throw err; + }, /Error: non-BIP68-final \(code 64\)/); + }); + }, + ); // Check first combination of complex CSV, 2 of 3 - it('can create (and broadcast via 3PBP) a Transaction where Bob and Charles can send (complex CHECKSEQUENCEVERIFY)', async () => { - // 2 blocks from now - const sequence1 = bip68.encode({ blocks: 2 }); - // 5 blocks from now - const sequence2 = bip68.encode({ blocks: 5 }); - const p2sh = bitcoin.payments.p2sh({ - redeem: { - output: complexCsvOutput( - alice, - bob, - charles, - dave, - sequence1, - sequence2, - ), - }, - network: regtest, - }); - - // fund the P2SH(CCSV) address - const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - - const tx = new bitcoin.Transaction(); - tx.version = 2; - tx.addInput(idToHash(unspent.txId), unspent.vout); - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - - // OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE - const signatureHash = tx.hashForSignature( - 0, - p2sh.redeem!.output!, - hashType, - ); - const redeemScriptSig = bitcoin.payments.p2sh({ - network: regtest, - redeem: { - network: regtest, - output: p2sh.redeem!.output, - input: bitcoin.script.compile([ - bitcoin.opcodes.OP_0, - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.script.signature.encode( - charles.sign(signatureHash), - hashType, + it( + 'can create (and broadcast via 3PBP) a Transaction where Bob and Charles ' + + 'can send (complex CHECKSEQUENCEVERIFY)', + async () => { + // 2 blocks from now + const sequence1 = bip68.encode({ blocks: 2 }); + // 5 blocks from now + const sequence2 = bip68.encode({ blocks: 5 }); + const p2sh = bitcoin.payments.p2sh({ + redeem: { + output: complexCsvOutput( + alice, + bob, + charles, + dave, + sequence1, + sequence2, ), - bitcoin.opcodes.OP_TRUE, - bitcoin.opcodes.OP_TRUE, - ]), - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + }, + network: regtest, + }); - await regtestUtils.broadcast(tx.toHex()); + // fund the P2SH(CCSV) address + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4, - }); - }); + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout); + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); + + // OP_0 {Bob sig} {Charles sig} OP_TRUE OP_TRUE + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem!.output, + input: bitcoin.script.compile([ + bitcoin.opcodes.OP_0, + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.script.signature.encode( + charles.sign(signatureHash), + hashType, + ), + bitcoin.opcodes.OP_TRUE, + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); + + await regtestUtils.broadcast(tx.toHex()); + + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4, + }); + }, + ); // Check first combination of complex CSV, mediator + 1 of 3 after 2 blocks - it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)', async () => { - // 2 blocks from now - const sequence1 = bip68.encode({ blocks: 2 }); - // 5 blocks from now - const sequence2 = bip68.encode({ blocks: 5 }); - const p2sh = bitcoin.payments.p2sh({ - redeem: { - output: complexCsvOutput( - alice, - bob, - charles, - dave, - sequence1, - sequence2, - ), - }, - network: regtest, - }); - - // fund the P2SH(CCSV) address - const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - - const tx = new bitcoin.Transaction(); - tx.version = 2; - tx.addInput(idToHash(unspent.txId), unspent.vout, sequence1); // Set sequence1 for input - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - - // OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE - const signatureHash = tx.hashForSignature( - 0, - p2sh.redeem!.output!, - hashType, - ); - const redeemScriptSig = bitcoin.payments.p2sh({ - network: regtest, - redeem: { + it( + 'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) ' + + 'and Bob can send after 2 blocks (complex CHECKSEQUENCEVERIFY)', + async () => { + // 2 blocks from now + const sequence1 = bip68.encode({ blocks: 2 }); + // 5 blocks from now + const sequence2 = bip68.encode({ blocks: 5 }); + const p2sh = bitcoin.payments.p2sh({ + redeem: { + output: complexCsvOutput( + alice, + bob, + charles, + dave, + sequence1, + sequence2, + ), + }, network: regtest, - output: p2sh.redeem!.output, - input: bitcoin.script.compile([ - bitcoin.opcodes.OP_0, - bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_0, - bitcoin.opcodes.OP_TRUE, - ]), - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + }); - // Wait 2 blocks - await regtestUtils.mine(2); + // fund the P2SH(CCSV) address + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - await regtestUtils.broadcast(tx.toHex()); + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout, sequence1); // Set sequence1 for input + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4, - }); - }); + // OP_0 {Bob sig} {Alice mediator sig} OP_FALSE OP_TRUE + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem!.output, + input: bitcoin.script.compile([ + bitcoin.opcodes.OP_0, + bitcoin.script.signature.encode(bob.sign(signatureHash), hashType), + bitcoin.script.signature.encode( + alice.sign(signatureHash), + hashType, + ), + bitcoin.opcodes.OP_0, + bitcoin.opcodes.OP_TRUE, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); + + // Wait 2 blocks + await regtestUtils.mine(2); + + await regtestUtils.broadcast(tx.toHex()); + + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4, + }); + }, + ); // Check first combination of complex CSV, mediator after 5 blocks - it('can create (and broadcast via 3PBP) a Transaction where Alice (mediator) can send after 5 blocks (complex CHECKSEQUENCEVERIFY)', async () => { - // 2 blocks from now - const sequence1 = bip68.encode({ blocks: 2 }); - // 5 blocks from now - const sequence2 = bip68.encode({ blocks: 5 }); - const p2sh = bitcoin.payments.p2sh({ - redeem: { - output: complexCsvOutput( - alice, - bob, - charles, - dave, - sequence1, - sequence2, - ), - }, - network: regtest, - }); - - // fund the P2SH(CCSV) address - const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - - const tx = new bitcoin.Transaction(); - tx.version = 2; - tx.addInput(idToHash(unspent.txId), unspent.vout, sequence2); // Set sequence2 for input - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - - // {Alice mediator sig} OP_FALSE - const signatureHash = tx.hashForSignature( - 0, - p2sh.redeem!.output!, - hashType, - ); - const redeemScriptSig = bitcoin.payments.p2sh({ - network: regtest, - redeem: { + it( + 'can create (and broadcast via 3PBP) a Transaction where Alice (mediator) ' + + 'can send after 5 blocks (complex CHECKSEQUENCEVERIFY)', + async () => { + // 2 blocks from now + const sequence1 = bip68.encode({ blocks: 2 }); + // 5 blocks from now + const sequence2 = bip68.encode({ blocks: 5 }); + const p2sh = bitcoin.payments.p2sh({ + redeem: { + output: complexCsvOutput( + alice, + bob, + charles, + dave, + sequence1, + sequence2, + ), + }, network: regtest, - output: p2sh.redeem!.output, - input: bitcoin.script.compile([ - bitcoin.script.signature.encode(alice.sign(signatureHash), hashType), - bitcoin.opcodes.OP_0, - ]), - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + }); - // Wait 5 blocks - await regtestUtils.mine(5); + // fund the P2SH(CCSV) address + const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); - await regtestUtils.broadcast(tx.toHex()); + const tx = new bitcoin.Transaction(); + tx.version = 2; + tx.addInput(idToHash(unspent.txId), unspent.vout, sequence2); // Set sequence2 for input + tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 7e4, - }); - }); + // {Alice mediator sig} OP_FALSE + const signatureHash = tx.hashForSignature( + 0, + p2sh.redeem!.output!, + hashType, + ); + const redeemScriptSig = bitcoin.payments.p2sh({ + network: regtest, + redeem: { + network: regtest, + output: p2sh.redeem!.output, + input: bitcoin.script.compile([ + bitcoin.script.signature.encode( + alice.sign(signatureHash), + hashType, + ), + bitcoin.opcodes.OP_0, + ]), + }, + }).input; + tx.setInputScript(0, redeemScriptSig!); + + // Wait 5 blocks + await regtestUtils.mine(5); + + await regtestUtils.broadcast(tx.toHex()); + + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 7e4, + }); + }, + ); }); diff --git a/test/integration/payments.spec.ts b/test/integration/payments.spec.ts index ef40387..58f48f5 100644 --- a/test/integration/payments.spec.ts +++ b/test/integration/payments.spec.ts @@ -12,7 +12,7 @@ async function buildAndSign( prevOutput: any, redeemScript: any, witnessScript: any, -) { +): Promise<null> { const unspent = await regtestUtils.faucetComplex(prevOutput, 5e4); const utx = await regtestUtils.fetch(unspent.txId); diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts index a98407d..2bcee3b 100644 --- a/test/integration/transactions.spec.ts +++ b/test/integration/transactions.spec.ts @@ -1,8 +1,8 @@ import * as assert from 'assert'; +import * as bip32 from 'bip32'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; -import * as bip32 from 'bip32'; const rng = require('randombytes'); const regtest = regtestUtils.network; @@ -437,85 +437,98 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); }); - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', async () => { - const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); - const inputData = await getInputData(5e4, p2sh.payment, true, 'p2sh-p2wsh'); - { - const { - hash, - index, - witnessUtxo, - redeemScript, - witnessScript, - } = inputData; - assert.deepStrictEqual( - { hash, index, witnessUtxo, redeemScript, witnessScript }, - inputData, + it( + 'can create (and broadcast via 3PBP) a Transaction, w/ a ' + + 'P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input', + async () => { + const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); + const inputData = await getInputData( + 5e4, + p2sh.payment, + true, + 'p2sh-p2wsh', ); - } + { + const { + hash, + index, + witnessUtxo, + redeemScript, + witnessScript, + } = inputData; + assert.deepStrictEqual( + { hash, index, witnessUtxo, redeemScript, witnessScript }, + inputData, + ); + } - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData) - .addOutput({ + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2sh.keys[0]) + .signInput(0, p2sh.keys[2]) + .signInput(0, p2sh.keys[3]); + + assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual( + psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey), + true, + ); + assert.throws(() => { + psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey); + }, new RegExp('No signatures for this pubkey')); + 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, - }) - .signInput(0, p2sh.keys[0]) - .signInput(0, p2sh.keys[2]) - .signInput(0, p2sh.keys[3]); + }); + }, + ); - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); - assert.strictEqual( - psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey), - true, - ); - assert.throws(() => { - psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey); - }, new RegExp('No signatures for this pubkey')); - 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, - }); - }); - - it('can create (and broadcast via 3PBP) a Transaction, w/ a P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo', async () => { - // For learning purposes, ignore this test. - // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData - const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); - const inputData = await getInputData( - 5e4, - p2sh.payment, - false, - 'p2sh-p2wsh', - ); - const psbt = new bitcoin.Psbt({ network: regtest }) - .addInput(inputData) - .addOutput({ + it( + 'can create (and broadcast via 3PBP) a Transaction, w/ a ' + + 'P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input with nonWitnessUtxo', + async () => { + // For learning purposes, ignore this test. + // REPEATING ABOVE BUT WITH nonWitnessUtxo by passing false to getInputData + const p2sh = createPayment('p2sh-p2wsh-p2ms(3 of 4)'); + const inputData = await getInputData( + 5e4, + p2sh.payment, + false, + 'p2sh-p2wsh', + ); + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2sh.keys[0]) + .signInput(0, p2sh.keys[2]) + .signInput(0, p2sh.keys[3]); + psbt.finalizeAllInputs(); + const tx = psbt.extractTransaction(); + await regtestUtils.broadcast(tx.toHex()); + await regtestUtils.verify({ + txId: tx.getId(), address: regtestUtils.RANDOM_ADDRESS, + vout: 0, value: 2e4, - }) - .signInput(0, p2sh.keys[0]) - .signInput(0, p2sh.keys[2]) - .signInput(0, p2sh.keys[3]); - psbt.finalizeAllInputs(); - const tx = psbt.extractTransaction(); - await regtestUtils.broadcast(tx.toHex()); - await regtestUtils.verify({ - txId: tx.getId(), - address: regtestUtils.RANDOM_ADDRESS, - vout: 0, - value: 2e4, - }); - }); + }); + }, + ); it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input using HD', async () => { const hdRoot = bip32.fromSeed(rng(64)); @@ -581,7 +594,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }); }); -function createPayment(_type: string, myKeys?: any[], network?: any) { +function createPayment(_type: string, myKeys?: any[], network?: any): any { network = network || regtest; const splitType = _type.split('-').reverse(); const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; @@ -589,8 +602,8 @@ function createPayment(_type: string, myKeys?: any[], network?: any) { let m: number | undefined; if (isMultisig) { const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/); - m = parseInt(match![1]); - let n = parseInt(match![2]); + m = parseInt(match![1], 10); + let n = parseInt(match![2], 10); if (keys.length > 0 && keys.length !== n) { throw new Error('Need n keys for multisig'); } @@ -628,7 +641,7 @@ function createPayment(_type: string, myKeys?: any[], network?: any) { }; } -function getWitnessUtxo(out: any) { +function getWitnessUtxo(out: any): any { delete out.address; out.script = Buffer.from(out.script, 'hex'); return out; @@ -639,7 +652,7 @@ async function getInputData( payment: any, isSegwit: boolean, redeemType: string, -) { +): Promise<any> { const unspent = await regtestUtils.faucetComplex(payment.output, amount); const utx = await regtestUtils.fetch(unspent.txId); // for non segwit inputs, you must pass the full transaction buffer diff --git a/test/payments.spec.ts b/test/payments.spec.ts index 051cc04..bc123cb 100644 --- a/test/payments.spec.ts +++ b/test/payments.spec.ts @@ -1,11 +1,11 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; -import * as u from './payments.utils'; import { PaymentCreator } from '../src/payments'; +import * as u from './payments.utils'; ['embed', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2wpkh', 'p2wsh'].forEach(p => { describe(p, () => { let fn: PaymentCreator; - let payment = require('../src/payments/' + p); + const payment = require('../src/payments/' + p); if (p === 'embed') { fn = payment.p2data; } else { @@ -83,7 +83,7 @@ import { PaymentCreator } from '../src/payments'; disabled[k] = true; }); - for (let key in depends) { + for (const key in depends) { if (key in disabled) continue; const dependencies = depends[key]; diff --git a/test/payments.utils.ts b/test/payments.utils.ts index bcf79c7..3f0ce23 100644 --- a/test/payments.utils.ts +++ b/test/payments.utils.ts @@ -1,6 +1,6 @@ import * as t from 'assert'; -import * as bscript from '../src/script'; import * as BNETWORKS from '../src/networks'; +import * as bscript from '../src/script'; function tryHex(x: Buffer | Buffer[]): string | string[] { if (Buffer.isBuffer(x)) return x.toString('hex'); @@ -17,12 +17,13 @@ function tryASM(x: Buffer): string { if (Buffer.isBuffer(x)) return bscript.toASM(x); return x; } -function asmToBuffer(x: string) { +function asmToBuffer(x: string): Buffer { if (x === '') return Buffer.alloc(0); return bscript.fromASM(x); } -function carryOver(a: any, b: any) { - for (let k in b) { +function carryOver(a: any, b: any): void { + for (const k in b) { + if (!k) continue; if (k in a && k === 'redeem') { carryOver(a[k], b[k]); continue; @@ -36,7 +37,7 @@ function carryOver(a: any, b: any) { } } -function equateBase(a: any, b: any, context: string) { +function equateBase(a: any, b: any, context: string): void { if ('output' in b) t.strictEqual( tryASM(a.output), @@ -53,7 +54,7 @@ function equateBase(a: any, b: any, context: string) { ); } -export function equate(a: any, b: any, args?: any) { +export function equate(a: any, b: any, args?: any): void { b = Object.assign({}, b); carryOver(b, args); @@ -108,7 +109,7 @@ export function equate(a: any, b: any, args?: any) { t.deepStrictEqual(tryHex(a.data), tryHex(b.data), 'Inequal *.data'); } -export function preform(x: any) { +export function preform(x: any): any { x = Object.assign({}, x); if (x.network) x.network = (BNETWORKS as any)[x.network]; @@ -148,7 +149,7 @@ export function preform(x: any) { return x; } -export function from(path: string, object: any, result?: any) { +export function from(path: string, object: any, result?: any): any { const paths = path.split('.'); result = result || {}; diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 39647b1..5f52cab 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -19,7 +19,7 @@ const initBuffers = (object: any): typeof preFixtures => const fixtures = initBuffers(preFixtures); -const upperCaseFirstLetter = (str: string) => +const upperCaseFirstLetter = (str: string): string => str.replace(/^./, s => s.toUpperCase()); // const b = (hex: string) => Buffer.from(hex, 'hex'); @@ -662,7 +662,7 @@ describe(`Psbt`, () => { const psbt = Psbt.fromBuffer( Buffer.from( '70736274ff01000a01000000000000000000000000', - 'hex', //cHNidP8BAAoBAAAAAAAAAAAAAAAA + 'hex', // cHNidP8BAAoBAAAAAAAAAAAAAAAA ), ); assert.strictEqual(psbt instanceof Psbt, true); diff --git a/test/script.spec.ts b/test/script.spec.ts index 23da986..95a339d 100644 --- a/test/script.spec.ts +++ b/test/script.spec.ts @@ -12,7 +12,7 @@ describe('script', () => { assert.strictEqual(false, bscript.isCanonicalPubKey(0 as any)); }); it('rejects smaller than 33', () => { - for (var i = 0; i < 33; i++) { + for (let i = 0; i < 33; i++) { assert.strictEqual( false, bscript.isCanonicalPubKey(Buffer.allocUnsafe(i)), @@ -20,7 +20,9 @@ describe('script', () => { } }); }); - describe.skip('isCanonicalScriptSignature', () => {}); + describe.skip('isCanonicalScriptSignature', () => { + assert.ok(true); + }); describe('fromASM/toASM', () => { fixtures.valid.forEach(f => { @@ -157,19 +159,19 @@ describe('script', () => { }); }); - function testEncodingForSize(i: number) { - it('compliant for data PUSH of length ' + i, () => { - const buffer = Buffer.alloc(i); + function testEncodingForSize(num: number): void { + it('compliant for data PUSH of length ' + num, () => { + const buffer = Buffer.alloc(num); const script = bscript.compile([buffer]); assert( minimalData(script), - 'Failed for ' + i + ' length script: ' + script.toString('hex'), + 'Failed for ' + num + ' length script: ' + script.toString('hex'), ); }); } - for (var i = 0; i < 520; ++i) { + for (let i = 0; i < 520; ++i) { testEncodingForSize(i); } }); diff --git a/test/script_signature.spec.ts b/test/script_signature.spec.ts index 3be52ad..54c416e 100644 --- a/test/script_signature.spec.ts +++ b/test/script_signature.spec.ts @@ -4,14 +4,19 @@ import { signature as bscriptSig } from '../src/script'; import * as fixtures from './fixtures/signature.json'; describe('Script Signatures', () => { - function fromRaw(signature: { r: string; s: string }) { + function fromRaw(signature: { r: string; s: string }): Buffer { return Buffer.concat( [Buffer.from(signature.r, 'hex'), Buffer.from(signature.s, 'hex')], 64, ); } - function toRaw(signature: Buffer) { + function toRaw( + signature: Buffer, + ): { + r: string; + s: string; + } { return { r: signature.slice(0, 32).toString('hex'), s: signature.slice(32, 64).toString('hex'), diff --git a/test/transaction.spec.ts b/test/transaction.spec.ts index c87d9e6..6744545 100644 --- a/test/transaction.spec.ts +++ b/test/transaction.spec.ts @@ -5,7 +5,7 @@ import * as bscript from '../src/script'; import * as fixtures from './fixtures/transaction.json'; describe('Transaction', () => { - function fromRaw(raw: any, noWitness?: boolean) { + function fromRaw(raw: any, noWitness?: boolean): Transaction { const tx = new Transaction(); tx.version = raw.version; tx.locktime = raw.locktime; @@ -47,7 +47,7 @@ describe('Transaction', () => { } describe('fromBuffer/fromHex', () => { - function importExport(f: any) { + function importExport(f: any): void { const id = f.id || f.hash; const txHex = f.hex || f.txHex; @@ -218,7 +218,7 @@ describe('Transaction', () => { }); describe('getHash/getId', () => { - function verify(f: any) { + function verify(f: any): void { it('should return the id for ' + f.id + '(' + f.description + ')', () => { const tx = Transaction.fromHex(f.whex || f.hex); @@ -231,7 +231,7 @@ describe('Transaction', () => { }); describe('isCoinbase', () => { - function verify(f: any) { + function verify(f: any): void { it( 'should return ' + f.coinbase + diff --git a/test/transaction_builder.spec.ts b/test/transaction_builder.spec.ts index 0daa103..b07462e 100644 --- a/test/transaction_builder.spec.ts +++ b/test/transaction_builder.spec.ts @@ -1,16 +1,18 @@ import * as assert from 'assert'; import { beforeEach, describe, it } from 'mocha'; -import * as baddress from '../src/address'; -import * as bscript from '../src/script'; -import * as payments from '../src/payments'; import { ECPair, networks as NETWORKS, Transaction, TransactionBuilder, } from '..'; +import * as baddress from '../src/address'; +import * as payments from '../src/payments'; +import * as bscript from '../src/script'; -console.warn = () => {}; // Silence the Deprecation Warning +console.warn = (): void => { + return; +}; // Silence the Deprecation Warning import * as fixtures from './fixtures/transaction_builder.json'; @@ -128,7 +130,7 @@ for (const useOldSignArgs of [false, true]) { if (useOldSignArgs) { consoleWarn = console.warn; // Silence console.warn during these tests - console.warn = () => undefined; + console.warn = (): undefined => undefined; } describe(`TransactionBuilder: useOldSignArgs === ${useOldSignArgs}`, () => { // constants @@ -425,13 +427,13 @@ for (const useOldSignArgs of [false, true]) { describe('sign', () => { it('supports the alternative abstract interface { publicKey, sign }', () => { - const keyPair = { + const innerKeyPair = { publicKey: ECPair.makeRandom({ - rng: () => { + rng: (): Buffer => { return Buffer.alloc(32, 1); }, }).publicKey, - sign: () => { + sign: (): Buffer => { return Buffer.alloc(64, 0x5f); }, }; @@ -446,11 +448,16 @@ for (const useOldSignArgs of [false, true]) { txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, - keyPair, + keyPair: innerKeyPair, }); assert.strictEqual( txb.build().toHex(), - '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', + '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + + 'ffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f' + + '5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f' + + '5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565' + + 'd71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914' + + '000000000000000000000000000000000000000088ac00000000', ); }); @@ -470,7 +477,12 @@ for (const useOldSignArgs of [false, true]) { // high R assert.strictEqual( txb.build().toHex(), - '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb4941f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', + '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + + 'ffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f' + + '32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb49' + + '41f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b' + + '07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a9' + + '14000000000000000000000000000000000000000088ac00000000', ); txb = new TransactionBuilder(); @@ -489,12 +501,17 @@ for (const useOldSignArgs of [false, true]) { // low R assert.strictEqual( txb.build().toHex(), - '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000', + '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + + 'ffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b' + + '49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee' + + '48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07' + + '029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914' + + '000000000000000000000000000000000000000088ac00000000', ); }); it('fails when missing required arguments', () => { - let txb = new TransactionBuilder(); + const txb = new TransactionBuilder(); txb.setVersion(1); txb.addInput( 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', @@ -663,7 +680,12 @@ for (const useOldSignArgs of [false, true]) { it('for incomplete with 0 signatures', () => { const randomTxData = - '0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f74d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000'; + '010000000001010001000000000000000000000000000000000000000000000000' + + '0000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4' + + '207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2' + + 'c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f7' + + '4d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d345102' + + '5c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000'; const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH'; const randomTx = Transaction.fromHex(randomTxData); @@ -676,7 +698,9 @@ for (const useOldSignArgs of [false, true]) { it('for incomplete P2SH with 0 signatures', () => { const inp = Buffer.from( - '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4fee489184c462a9b1b9237488700000000', + '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be9' + + '59391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4' + + 'fee489184c462a9b1b9237488700000000', 'hex', ); // arbitrary P2SH input const inpTx = Transaction.fromBuffer(inp); @@ -690,7 +714,9 @@ for (const useOldSignArgs of [false, true]) { it('for incomplete P2WPKH with 0 signatures', () => { const inp = Buffer.from( - '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9f68ccc887fca2e63547d794b00000000', + '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be9' + + '59391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9' + + 'f68ccc887fca2e63547d794b00000000', 'hex', ); const inpTx = Transaction.fromBuffer(inp); @@ -705,7 +731,9 @@ for (const useOldSignArgs of [false, true]) { it('for incomplete P2WSH with 0 signatures', () => { const inpTx = Transaction.fromBuffer( Buffer.from( - '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b231b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', + '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80b' + + 'e959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b2' + + '31b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', 'hex', ), ); @@ -800,12 +828,14 @@ for (const useOldSignArgs of [false, true]) { }); it('should classify witness inputs with witness = true during multisigning', () => { - const keyPair = ECPair.fromWIF( + const innerKeyPair = ECPair.fromWIF( 'cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', network, ); const witnessScript = Buffer.from( - '522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae', + '522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e3' + + '52e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532' + + 'b9ea1952ae', 'hex', ); const redeemScript = Buffer.from( @@ -828,7 +858,7 @@ for (const useOldSignArgs of [false, true]) { txb.sign({ prevOutScriptType: 'p2sh-p2wsh-p2ms', vin: 0, - keyPair, + keyPair: innerKeyPair, redeemScript, witnessValue: 100000, witnessScript, @@ -850,10 +880,24 @@ for (const useOldSignArgs of [false, true]) { it('should handle badly pre-filled OP_0s', () => { // OP_0 is used where a signature is missing const redeemScripSig = bscript.fromASM( - 'OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae', + 'OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17' + + 'be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621eb' + + 'd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bf' + + 'cdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b44' + + '8a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778' + + 'e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f6326' + + '53266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c' + + '845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99' + + '934c2231b6cb9fd7584b8e67253ae', ); const redeemScript = bscript.fromASM( - 'OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG', + 'OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f' + + '81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d' + + '4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c70' + + '9ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe5' + + '2a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce03' + + '6f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67' + + '2 OP_3 OP_CHECKMULTISIG', ); const tx = new Transaction(); @@ -895,7 +939,17 @@ for (const useOldSignArgs of [false, true]) { ); assert.strictEqual( bscript.toASM(tx2.ins[0].script), - 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae', + 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b' + + '63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691' + + 'd6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4' + + '466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881' + + 'd7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870' + + 'b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a' + + '8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07' + + 'cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceae' + + 'ef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5' + + '229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f35' + + '66500a99934c2231b6cb9fd7584b8e67253ae', ); }); @@ -908,7 +962,7 @@ for (const useOldSignArgs of [false, true]) { ); const incomplete = txb.buildIncomplete().toHex(); - const keyPair = ECPair.fromWIF( + const innerKeyPair = ECPair.fromWIF( 'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy', ); @@ -917,7 +971,7 @@ for (const useOldSignArgs of [false, true]) { txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, - keyPair, + keyPair: innerKeyPair, }); const txId = txb.build().getId(); assert.strictEqual( @@ -933,7 +987,7 @@ for (const useOldSignArgs of [false, true]) { txb.sign({ prevOutScriptType: 'p2pkh', vin: 0, - keyPair, + keyPair: innerKeyPair, }); const txId2 = txb.build().getId(); assert.strictEqual(txId, txId2); diff --git a/tslint.json b/tslint.json index 8a9cde6..d42da60 100644 --- a/tslint.json +++ b/tslint.json @@ -21,7 +21,7 @@ "no-var-requires": false, "no-unused-expression": false, "object-literal-sort-keys": false, - "quotemark": [true, "single"], + "quotemark": [true, "single", "avoid-escape"], "typedef": [ true, "call-signature", From e104a73e736401d1d131d1f9743e404cd318e726 Mon Sep 17 00:00:00 2001 From: d-yokoi <daiki.kouu@gmail.com> Date: Thu, 12 Sep 2019 17:47:39 +0900 Subject: [PATCH 461/568] chore: update type assertion --- test/psbt.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 5f52cab..4eb32dc 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -14,7 +14,7 @@ const initBuffers = (object: any): typeof preFixtures => const data = result[1]; const encoding = result[2]; - return Buffer.from(data, encoding as any); + return Buffer.from(data, encoding as BufferEncoding); }); const fixtures = initBuffers(preFixtures); From 7ef3fe49966fe54957533970f6356f29aafecf02 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 1 Oct 2019 14:54:57 +0900 Subject: [PATCH 462/568] Add sanity check for arguments --- src/psbt.js | 22 ++++++++++++++++++++++ test/fixtures/psbt.json | 2 +- ts_src/psbt.ts | 23 +++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/psbt.js b/src/psbt.js index 57a159c..708dd30 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -143,6 +143,17 @@ class Psbt { return this; } addInput(inputData) { + if ( + arguments.length > 1 || + !inputData || + inputData.hash === undefined || + inputData.index === undefined + ) { + throw new Error( + `Invalid arguments for Psbt.addInput. ` + + `Requires single object with at least [hash] and [index]`, + ); + } checkInputsForPartialSig(this.data.inputs, 'addInput'); const c = this.__CACHE; this.data.addInput(inputData); @@ -163,6 +174,17 @@ class Psbt { return this; } addOutput(outputData) { + if ( + arguments.length > 1 || + !outputData || + outputData.value === undefined || + (outputData.address === undefined && outputData.script === undefined) + ) { + throw new Error( + `Invalid arguments for Psbt.addOutput. ` + + `Requires single object with at least [script or address] and [value]`, + ); + } checkInputsForPartialSig(this.data.inputs, 'addOutput'); const { address } = outputData; if (typeof address === 'string') { diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index 95c980e..e3062e8 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -311,7 +311,7 @@ "inputData": { "hash": 42 }, - "exception": "Error adding input." + "exception": "Invalid arguments for Psbt\\.addInput\\. Requires single object with at least \\[hash\\] and \\[index\\]" }, { "description": "should be equal", diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d35fd4c..0a159c9 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -182,6 +182,17 @@ export class Psbt { } addInput(inputData: PsbtInputExtended): this { + if ( + arguments.length > 1 || + !inputData || + inputData.hash === undefined || + inputData.index === undefined + ) { + throw new Error( + `Invalid arguments for Psbt.addInput. ` + + `Requires single object with at least [hash] and [index]`, + ); + } checkInputsForPartialSig(this.data.inputs, 'addInput'); const c = this.__CACHE; this.data.addInput(inputData); @@ -205,6 +216,18 @@ export class Psbt { } addOutput(outputData: PsbtOutputExtended): this { + if ( + arguments.length > 1 || + !outputData || + outputData.value === undefined || + ((outputData as any).address === undefined && + (outputData as any).script === undefined) + ) { + throw new Error( + `Invalid arguments for Psbt.addOutput. ` + + `Requires single object with at least [script or address] and [value]`, + ); + } checkInputsForPartialSig(this.data.inputs, 'addOutput'); const { address } = outputData as any; if (typeof address === 'string') { From 22682fc2c335bc75f7f8aa8a926f2d2da0a129af Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 10 Oct 2019 11:01:54 +0900 Subject: [PATCH 463/568] Allow custom implementations of finalizers --- src/psbt.js | 19 ++++++++++++++---- ts_src/psbt.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++---- types/psbt.d.ts | 15 ++++++++++++-- 3 files changed, 76 insertions(+), 10 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 57a159c..3326995 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -204,7 +204,18 @@ class Psbt { range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx)); return this; } - finalizeInput(inputIndex) { + finalizeInput( + inputIndex, + { + classifyScript: classifyScriptF, + canFinalize: canFinalizeF, + getFinalScripts: getFinalScriptsF, + } = { + classifyScript, + canFinalize, + getFinalScripts, + }, + ) { const input = utils_1.checkForInput(this.data.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, @@ -212,11 +223,11 @@ class Psbt { this.__CACHE, ); if (!script) throw new Error(`No script found for input #${inputIndex}`); - const scriptType = classifyScript(script); - if (!canFinalize(input, script, scriptType)) + const scriptType = classifyScriptF(script); + if (!canFinalizeF(input, script, scriptType)) throw new Error(`Can not finalize input #${inputIndex}`); checkPartialSigSighashes(input); - const { finalScriptSig, finalScriptWitness } = getFinalScripts( + const { finalScriptSig, finalScriptWitness } = getFinalScriptsF( script, scriptType, input.partialSig, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d35fd4c..6fa6f43 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -251,7 +251,18 @@ export class Psbt { return this; } - finalizeInput(inputIndex: number): this { + finalizeInput( + inputIndex: number, + { + classifyScript: classifyScriptF, + canFinalize: canFinalizeF, + getFinalScripts: getFinalScriptsF, + }: IFinalizeFuncs = { + classifyScript, + canFinalize, + getFinalScripts, + }, + ): this { const input = checkForInput(this.data.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, @@ -260,13 +271,13 @@ export class Psbt { ); if (!script) throw new Error(`No script found for input #${inputIndex}`); - const scriptType = classifyScript(script); - if (!canFinalize(input, script, scriptType)) + const scriptType = classifyScriptF(script); + if (!canFinalizeF(input, script, scriptType)) throw new Error(`Can not finalize input #${inputIndex}`); checkPartialSigSighashes(input); - const { finalScriptSig, finalScriptWitness } = getFinalScripts( + const { finalScriptSig, finalScriptWitness } = getFinalScriptsF( script, scriptType, input.partialSig!, @@ -735,6 +746,39 @@ class PsbtTransaction implements ITransaction { } } +// This interface is added to allow for custom scripts to be finalized with PSBT. +interface IFinalizeFuncs { + classifyScript: FinalizeFuncClassifyScript; + canFinalize: FinalizeFuncCanFinalize; + getFinalScripts: FinalizeFuncGetFinalScripts; +} + +// Takes the meaningful script (redeemScript for P2SH and witnessScript for P2WSH) +// and returns a string to classify the script. +type FinalizeFuncClassifyScript = (script: Buffer) => string; +// Takes the Psbt data for the input and the meaningful script and its type name. +// returns true if we can finalize the input +type FinalizeFuncCanFinalize = ( + input: PsbtInput, + script: Buffer, + scriptType: string, +) => boolean; +// Takes the meaningful script, its type name, all the signatures from this input, +// and 3 booleans to tell you if it is segwit, P2SH, and P2WSH. +// it returns finalScriptSig and finalScriptWitness to be placed in the Psbt. +// if one is not needed, it should be undefined. (In TypeScript, it must be declared but undefined.) +type FinalizeFuncGetFinalScripts = ( + script: Buffer, + scriptType: string, + partialSig: PartialSig[], + isSegwit: boolean, + isP2SH: boolean, + isP2WSH: boolean, +) => { + finalScriptSig: Buffer | undefined; + finalScriptWitness: Buffer | undefined; +}; + function canFinalize( input: PsbtInput, script: Buffer, diff --git a/types/psbt.d.ts b/types/psbt.d.ts index b1bacea..516d72f 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,5 +1,5 @@ import { Psbt as PsbtBase } from 'bip174'; -import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; +import { KeyValue, PartialSig, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; @@ -58,7 +58,7 @@ export declare class Psbt { getFeeRate(): number; getFee(): number; finalizeAllInputs(): this; - finalizeInput(inputIndex: number): this; + finalizeInput(inputIndex: number, { classifyScript: classifyScriptF, canFinalize: canFinalizeF, getFinalScripts: getFinalScriptsF, }?: IFinalizeFuncs): this; validateSignaturesOfAllInputs(): boolean; validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean; signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; @@ -124,4 +124,15 @@ interface HDSignerAsync extends HDSignerBase { derivePath(path: string): HDSignerAsync; sign(hash: Buffer): Promise<Buffer>; } +interface IFinalizeFuncs { + classifyScript: FinalizeFuncClassifyScript; + canFinalize: FinalizeFuncCanFinalize; + getFinalScripts: FinalizeFuncGetFinalScripts; +} +declare type FinalizeFuncClassifyScript = (script: Buffer) => string; +declare type FinalizeFuncCanFinalize = (input: PsbtInput, script: Buffer, scriptType: string) => boolean; +declare type FinalizeFuncGetFinalScripts = (script: Buffer, scriptType: string, partialSig: PartialSig[], isSegwit: boolean, isP2SH: boolean, isP2WSH: boolean) => { + finalScriptSig: Buffer | undefined; + finalScriptWitness: Buffer | undefined; +}; export {}; From 4b5a519bfee242229b901a88a25c8a23d68c1d7a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 28 Oct 2019 11:40:56 +0900 Subject: [PATCH 464/568] Use single func instead of 3 --- src/psbt.js | 37 ++++++++++--------- ts_src/psbt.ts | 95 ++++++++++++++++++++++++------------------------- types/psbt.d.ts | 24 +++++++------ 3 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 3326995..e28cd49 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -204,18 +204,7 @@ class Psbt { range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx)); return this; } - finalizeInput( - inputIndex, - { - classifyScript: classifyScriptF, - canFinalize: canFinalizeF, - getFinalScripts: getFinalScriptsF, - } = { - classifyScript, - canFinalize, - getFinalScripts, - }, - ) { + finalizeInput(inputIndex, finalScriptsFunc = getFinalScripts) { const input = utils_1.checkForInput(this.data.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, @@ -223,14 +212,11 @@ class Psbt { this.__CACHE, ); if (!script) throw new Error(`No script found for input #${inputIndex}`); - const scriptType = classifyScriptF(script); - if (!canFinalizeF(input, script, scriptType)) - throw new Error(`Can not finalize input #${inputIndex}`); checkPartialSigSighashes(input); - const { finalScriptSig, finalScriptWitness } = getFinalScriptsF( + const { finalScriptSig, finalScriptWitness } = finalScriptsFunc( + inputIndex, + input, script, - scriptType, - input.partialSig, isSegwit, isP2SH, isP2WSH, @@ -749,7 +735,20 @@ function getTxCacheValue(key, name, inputs, c) { if (key === '__FEE_RATE') return c.__FEE_RATE; else if (key === '__FEE') return c.__FEE; } -function getFinalScripts( +function getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP2WSH) { + const scriptType = classifyScript(script); + if (!canFinalize(input, script, scriptType)) + throw new Error(`Can not finalize input #${inputIndex}`); + return prepareFinalScripts( + script, + scriptType, + input.partialSig, + isSegwit, + isP2SH, + isP2WSH, + ); +} +function prepareFinalScripts( script, scriptType, partialSig, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 6fa6f43..15a6ce8 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -253,15 +253,7 @@ export class Psbt { finalizeInput( inputIndex: number, - { - classifyScript: classifyScriptF, - canFinalize: canFinalizeF, - getFinalScripts: getFinalScriptsF, - }: IFinalizeFuncs = { - classifyScript, - canFinalize, - getFinalScripts, - }, + finalScriptsFunc: FinalScriptsFunc = getFinalScripts, ): this { const input = checkForInput(this.data.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( @@ -271,16 +263,12 @@ export class Psbt { ); if (!script) throw new Error(`No script found for input #${inputIndex}`); - const scriptType = classifyScriptF(script); - if (!canFinalizeF(input, script, scriptType)) - throw new Error(`Can not finalize input #${inputIndex}`); - checkPartialSigSighashes(input); - const { finalScriptSig, finalScriptWitness } = getFinalScriptsF( + const { finalScriptSig, finalScriptWitness } = finalScriptsFunc( + inputIndex, + input, script, - scriptType, - input.partialSig!, isSegwit, isP2SH, isP2WSH, @@ -746,39 +734,6 @@ class PsbtTransaction implements ITransaction { } } -// This interface is added to allow for custom scripts to be finalized with PSBT. -interface IFinalizeFuncs { - classifyScript: FinalizeFuncClassifyScript; - canFinalize: FinalizeFuncCanFinalize; - getFinalScripts: FinalizeFuncGetFinalScripts; -} - -// Takes the meaningful script (redeemScript for P2SH and witnessScript for P2WSH) -// and returns a string to classify the script. -type FinalizeFuncClassifyScript = (script: Buffer) => string; -// Takes the Psbt data for the input and the meaningful script and its type name. -// returns true if we can finalize the input -type FinalizeFuncCanFinalize = ( - input: PsbtInput, - script: Buffer, - scriptType: string, -) => boolean; -// Takes the meaningful script, its type name, all the signatures from this input, -// and 3 booleans to tell you if it is segwit, P2SH, and P2WSH. -// it returns finalScriptSig and finalScriptWitness to be placed in the Psbt. -// if one is not needed, it should be undefined. (In TypeScript, it must be declared but undefined.) -type FinalizeFuncGetFinalScripts = ( - script: Buffer, - scriptType: string, - partialSig: PartialSig[], - isSegwit: boolean, - isP2SH: boolean, - isP2WSH: boolean, -) => { - finalScriptSig: Buffer | undefined; - finalScriptWitness: Buffer | undefined; -}; - function canFinalize( input: PsbtInput, script: Buffer, @@ -996,7 +951,49 @@ function getTxCacheValue( else if (key === '__FEE') return c.__FEE!; } +/** + * This function must do two things: + * 1. Check if the `input` can be finalized. If it can not be finalized, throw. + * ie. `Can not finalize input #${inputIndex}` + * 2. Create the finalScriptSig and finalScriptWitness Buffers. + */ +type FinalScriptsFunc = ( + inputIndex: number, // Which input is it? + input: PsbtInput, // The PSBT input contents + script: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.) + isSegwit: boolean, // Is it segwit? + isP2SH: boolean, // Is it P2SH? + isP2WSH: boolean, // Is it P2WSH? +) => { + finalScriptSig: Buffer | undefined; + finalScriptWitness: Buffer | undefined; +}; + function getFinalScripts( + inputIndex: number, + input: PsbtInput, + script: Buffer, + isSegwit: boolean, + isP2SH: boolean, + isP2WSH: boolean, +): { + finalScriptSig: Buffer | undefined; + finalScriptWitness: Buffer | undefined; +} { + const scriptType = classifyScript(script); + if (!canFinalize(input, script, scriptType)) + throw new Error(`Can not finalize input #${inputIndex}`); + return prepareFinalScripts( + script, + scriptType, + input.partialSig!, + isSegwit, + isP2SH, + isP2WSH, + ); +} + +function prepareFinalScripts( script: Buffer, scriptType: string, partialSig: PartialSig[], diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 516d72f..44eb4d8 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,5 +1,5 @@ import { Psbt as PsbtBase } from 'bip174'; -import { KeyValue, PartialSig, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; +import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; @@ -58,7 +58,7 @@ export declare class Psbt { getFeeRate(): number; getFee(): number; finalizeAllInputs(): this; - finalizeInput(inputIndex: number, { classifyScript: classifyScriptF, canFinalize: canFinalizeF, getFinalScripts: getFinalScriptsF, }?: IFinalizeFuncs): this; + finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this; validateSignaturesOfAllInputs(): boolean; validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean; signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; @@ -124,14 +124,18 @@ interface HDSignerAsync extends HDSignerBase { derivePath(path: string): HDSignerAsync; sign(hash: Buffer): Promise<Buffer>; } -interface IFinalizeFuncs { - classifyScript: FinalizeFuncClassifyScript; - canFinalize: FinalizeFuncCanFinalize; - getFinalScripts: FinalizeFuncGetFinalScripts; -} -declare type FinalizeFuncClassifyScript = (script: Buffer) => string; -declare type FinalizeFuncCanFinalize = (input: PsbtInput, script: Buffer, scriptType: string) => boolean; -declare type FinalizeFuncGetFinalScripts = (script: Buffer, scriptType: string, partialSig: PartialSig[], isSegwit: boolean, isP2SH: boolean, isP2WSH: boolean) => { +/** + * This function must do two things: + * 1. Check if the `input` can be finalized. If it can not be finalized, throw. + * ie. `Can not finalize input #${inputIndex}` + * 2. Create the finalScriptSig and finalScriptWitness Buffers. + */ +declare type FinalScriptsFunc = (inputIndex: number, // Which input is it? +input: PsbtInput, // The PSBT input contents +script: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.) +isSegwit: boolean, // Is it segwit? +isP2SH: boolean, // Is it P2SH? +isP2WSH: boolean) => { finalScriptSig: Buffer | undefined; finalScriptWitness: Buffer | undefined; }; From f2224473237f9066c93421aa2317f03fab0efeee Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 28 Oct 2019 12:27:35 +0900 Subject: [PATCH 465/568] Add CSV example for custom finalizer --- test/integration/csv.spec.ts | 134 ++++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 26 deletions(-) diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts index d6f99c7..6f11a90 100644 --- a/test/integration/csv.spec.ts +++ b/test/integration/csv.spec.ts @@ -1,9 +1,11 @@ import * as assert from 'assert'; +import { PsbtInput } from 'bip174/src/lib/interfaces'; import { before, describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; const regtest = regtestUtils.network; const bip68 = require('bip68'); +const varuint = require('varuint-bitcoin'); function toOutputScript(address: string): Buffer { return bitcoin.address.toOutputScript(address, regtest); @@ -129,33 +131,28 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { // fund the P2SH(CSV) address const unspent = await regtestUtils.faucet(p2sh.address!, 1e5); + const utx = await regtestUtils.fetch(unspent.txId); + // for non segwit inputs, you must pass the full transaction buffer + const nonWitnessUtxo = Buffer.from(utx.txHex, 'hex'); - const tx = new bitcoin.Transaction(); - tx.version = 2; - tx.addInput(idToHash(unspent.txId), unspent.vout, sequence); - tx.addOutput(toOutputScript(regtestUtils.RANDOM_ADDRESS), 7e4); - - // {Alice's signature} OP_TRUE - const signatureHash = tx.hashForSignature( - 0, - p2sh.redeem!.output!, - hashType, - ); - const redeemScriptSig = bitcoin.payments.p2sh({ - network: regtest, - redeem: { - network: regtest, - output: p2sh.redeem!.output, - input: bitcoin.script.compile([ - bitcoin.script.signature.encode( - alice.sign(signatureHash), - hashType, - ), - bitcoin.opcodes.OP_TRUE, - ]), - }, - }).input; - tx.setInputScript(0, redeemScriptSig!); + // This is an example of using the finalizeInput second parameter to + // define how you finalize the inputs, allowing for any type of script. + const tx = new bitcoin.Psbt({ network: regtest }) + .setVersion(2) + .addInput({ + hash: unspent.txId, + index: unspent.vout, + sequence, + redeemScript: p2sh.redeem!.output!, + nonWitnessUtxo, + }) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 7e4, + }) + .signInput(0, alice) + .finalizeInput(0, csvGetFinalScripts) // See csvGetFinalScripts below + .extractTransaction(); // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently // ... @@ -430,3 +427,88 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { }, ); }); + +// This function is used to finalize a CSV transaction using PSBT. +// See first test above. +function csvGetFinalScripts( + inputIndex: number, + input: PsbtInput, + script: Buffer, + isSegwit: boolean, + isP2SH: boolean, + isP2WSH: boolean, +): { + finalScriptSig: Buffer | undefined; + finalScriptWitness: Buffer | undefined; +} { + // Step 1: Check to make sure the meaningful script matches what you expect. + const decompiled = bitcoin.script.decompile(script); + // Checking if first OP is OP_IF... should do better check in production! + // You may even want to check the public keys in the script against a + // whitelist depending on the circumstances!!! + // You also want to check the contents of the input to see if you have enough + // info to actually construct the scriptSig and Witnesses. + if (!decompiled || decompiled[0] !== bitcoin.opcodes.OP_IF) { + throw new Error(`Can not finalize input #${inputIndex}`); + } + + // Step 2: Create final scripts + let payment: bitcoin.Payment = { + network: regtest, + output: script, + // This logic should be more strict and make sure the pubkeys in the + // meaningful script are the ones signing in the PSBT etc. + input: bitcoin.script.compile([ + input.partialSig![0].signature, + bitcoin.opcodes.OP_TRUE, + ]), + }; + if (isP2WSH && isSegwit) + payment = bitcoin.payments.p2wsh({ + network: regtest, + redeem: payment, + }); + if (isP2SH) + payment = bitcoin.payments.p2sh({ + network: regtest, + redeem: payment, + }); + + function witnessStackToScriptWitness(witness: Buffer[]): Buffer { + let buffer = Buffer.allocUnsafe(0); + + function writeSlice(slice: Buffer): void { + buffer = Buffer.concat([buffer, Buffer.from(slice)]); + } + + function writeVarInt(i: number): void { + const currentLen = buffer.length; + const varintLen = varuint.encodingLength(i); + + buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); + varuint.encode(i, buffer, currentLen); + } + + function writeVarSlice(slice: Buffer): void { + writeVarInt(slice.length); + writeSlice(slice); + } + + function writeVector(vector: Buffer[]): void { + writeVarInt(vector.length); + vector.forEach(writeVarSlice); + } + + writeVector(witness); + + return buffer; + } + + return { + finalScriptSig: payment.input, + finalScriptWitness: + payment.witness && payment.witness.length > 0 + ? witnessStackToScriptWitness(payment.witness) + : undefined, + }; +} From 2aa392661637c95a5d26872049d528bdee5cb173 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 21 Nov 2019 16:35:40 +0900 Subject: [PATCH 466/568] Fix Transaction Output type Co-authored-by: longhoang.wkm <longhoang@wakumo.vn> --- ts_src/transaction.ts | 19 ++++++------------- types/transaction.d.ts | 8 +------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index 218d004..37b88f5 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -36,18 +36,13 @@ const ONE: Buffer = Buffer.from( 'hex', ); const VALUE_UINT64_MAX: Buffer = Buffer.from('ffffffffffffffff', 'hex'); -const BLANK_OUTPUT: BlankOutput = { +const BLANK_OUTPUT = { script: EMPTY_SCRIPT, valueBuffer: VALUE_UINT64_MAX, }; -function isOutput(out: Output | BlankOutput): out is Output { - return (out as Output).value !== undefined; -} - -export interface BlankOutput { - script: Buffer; - valueBuffer: Buffer; +function isOutput(out: Output): boolean { + return out.value !== undefined; } export interface Output { @@ -55,8 +50,6 @@ export interface Output { value: number; } -type OpenOutput = Output | BlankOutput; - export interface Input { hash: Buffer; index: number; @@ -185,7 +178,7 @@ export class Transaction { version: number = 1; locktime: number = 0; ins: Input[] = []; - outs: OpenOutput[] = []; + outs: Output[] = []; isCoinbase(): boolean { return ( @@ -333,7 +326,7 @@ export class Transaction { // "blank" outputs before for (let i = 0; i < inIndex; i++) { - txTmp.outs[i] = BLANK_OUTPUT; + (txTmp.outs as any)[i] = BLANK_OUTPUT; } // ignore sequence numbers (except at inIndex) @@ -602,7 +595,7 @@ export class Transaction { if (isOutput(txOut)) { writeUInt64(txOut.value); } else { - writeSlice(txOut.valueBuffer); + writeSlice((txOut as any).valueBuffer); } writeVarSlice(txOut.script); diff --git a/types/transaction.d.ts b/types/transaction.d.ts index d7462b4..f0db04e 100644 --- a/types/transaction.d.ts +++ b/types/transaction.d.ts @@ -1,12 +1,7 @@ -export interface BlankOutput { - script: Buffer; - valueBuffer: Buffer; -} export interface Output { script: Buffer; value: number; } -declare type OpenOutput = Output | BlankOutput; export interface Input { hash: Buffer; index: number; @@ -28,7 +23,7 @@ export declare class Transaction { version: number; locktime: number; ins: Input[]; - outs: OpenOutput[]; + outs: Output[]; isCoinbase(): boolean; addInput(hash: Buffer, index: number, sequence?: number, scriptSig?: Buffer): number; addOutput(scriptPubKey: Buffer, value: number): number; @@ -56,4 +51,3 @@ export declare class Transaction { private __byteLength; private __toBuffer; } -export {}; From 22d5831b9b25031a5a327722ba5932d67d1cb3bb Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 21 Nov 2019 18:01:04 +0900 Subject: [PATCH 467/568] Remove Output casts from Transaction class --- ts_src/transaction.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index 37b88f5..0419ba2 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -268,7 +268,7 @@ export class Transaction { newTx.outs = this.outs.map(txOut => { return { script: txOut.script, - value: (txOut as Output).value, + value: txOut.value, }; }); @@ -438,7 +438,7 @@ export class Transaction { toffset = 0; this.outs.forEach(out => { - writeUInt64((out as Output).value); + writeUInt64(out.value); writeVarSlice(out.script); }); @@ -451,7 +451,7 @@ export class Transaction { tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); toffset = 0; - writeUInt64((output as Output).value); + writeUInt64(output.value); writeVarSlice(output.script); hashOutputs = bcrypto.hash256(tbuffer); From 02772a4a0c06c2b7ecd52280f774349db65becf3 Mon Sep 17 00:00:00 2001 From: Marius Darila <marius.darila@gmail.com> Date: Mon, 25 Nov 2019 15:20:06 +0100 Subject: [PATCH 468/568] chore(): move @types under devDependencies --- package-lock.json | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c3dd29..5e364ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -158,7 +158,8 @@ "@types/node": { "version": "12.7.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz", - "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==" + "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==", + "dev": true }, "@types/proxyquire": { "version": "1.3.28", diff --git a/package.json b/package.json index 093a097..c239e9f 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "types" ], "dependencies": { - "@types/node": "12.7.5", "bech32": "^1.1.2", "bip174": "^1.0.1", "bip32": "^2.0.4", @@ -69,6 +68,7 @@ "devDependencies": { "@types/bs58": "^4.0.0", "@types/mocha": "^5.2.7", + "@types/node": "12.7.5", "@types/proxyquire": "^1.3.28", "bip39": "^3.0.2", "bip65": "^1.0.1", From 48bf08c0d3649fa36278637d4e16b8cf6231b767 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 29 Nov 2019 15:30:23 +0900 Subject: [PATCH 469/568] Add weight and ability to get strippedsize --- src/block.js | 10 ++++++++-- test/block.spec.ts | 5 +++++ test/fixtures/block.json | 5 ++++- ts_src/block.ts | 11 +++++++++-- types/block.d.ts | 3 ++- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/block.js b/src/block.js index 22449fd..18b49c7 100644 --- a/src/block.js +++ b/src/block.js @@ -127,12 +127,18 @@ class Block { hasWitness() { return anyTxHasWitness(this.transactions); } - byteLength(headersOnly) { + weight() { + const base = this.byteLength(false, false); + const total = this.byteLength(false, true); + return base * 3 + total; + } + byteLength(headersOnly, allowWitness = true) { if (headersOnly || !this.transactions) return 80; return ( 80 + varuint.encodingLength(this.transactions.length) + - this.transactions.reduce((a, x) => a + x.byteLength(), 0) + // @ts-ignore using the __byteLength private method on Transaction + this.transactions.reduce((a, x) => a + x.__byteLength(allowWitness), 0) ); } getHash() { diff --git a/test/block.spec.ts b/test/block.spec.ts index 0f74392..e93420c 100644 --- a/test/block.spec.ts +++ b/test/block.spec.ts @@ -48,6 +48,11 @@ describe('Block', () => { assert.strictEqual(block.bits, f.bits); assert.strictEqual(block.nonce, f.nonce); assert.strictEqual(!block.transactions, f.hex.length === 160); + if (f.size && f.strippedSize && f.weight) { + assert.strictEqual(block.byteLength(false, true), f.size); + assert.strictEqual(block.byteLength(false, false), f.strippedSize); + assert.strictEqual(block.weight(), f.weight); + } }); }); diff --git a/test/fixtures/block.json b/test/fixtures/block.json index c60685e..8ea521c 100644 --- a/test/fixtures/block.json +++ b/test/fixtures/block.json @@ -133,7 +133,10 @@ "prevHash": "8980ebb11236bacc66c447d5ad961bc546c0f9cc385a08000000000000000000", "timestamp": 1537429727, "valid": true, - "version": 536870912 + "version": 536870912, + "size": 2355, + "strippedSize": 2209, + "weight": 8982 } ], "invalid": [ diff --git a/ts_src/block.ts b/ts_src/block.ts index cf4ed51..0792edf 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -148,13 +148,20 @@ export class Block { return anyTxHasWitness(this.transactions!); } - byteLength(headersOnly?: boolean): number { + weight(): number { + const base = this.byteLength(false, false); + const total = this.byteLength(false, true); + return base * 3 + total; + } + + byteLength(headersOnly?: boolean, allowWitness: boolean = true): number { if (headersOnly || !this.transactions) return 80; return ( 80 + varuint.encodingLength(this.transactions.length) + - this.transactions.reduce((a, x) => a + x.byteLength(), 0) + // @ts-ignore using the __byteLength private method on Transaction + this.transactions.reduce((a, x) => a + x.__byteLength(allowWitness), 0) ); } diff --git a/types/block.d.ts b/types/block.d.ts index d77bb1b..7d8309c 100644 --- a/types/block.d.ts +++ b/types/block.d.ts @@ -15,7 +15,8 @@ export declare class Block { getWitnessCommit(): Buffer | null; hasWitnessCommit(): boolean; hasWitness(): boolean; - byteLength(headersOnly?: boolean): number; + weight(): number; + byteLength(headersOnly?: boolean, allowWitness?: boolean): number; getHash(): Buffer; getId(): string; getUTCDate(): Date; From e10324f850bcaf371d5363ae3bbb3e9370dee53a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 2 Dec 2019 15:58:04 +0900 Subject: [PATCH 470/568] PSBT Bugfix for multiple of same pubkey in p2ms --- src/psbt.js | 20 ++++++++++++++---- test/integration/transactions.spec.ts | 30 +++++++++++++++++++++++++++ ts_src/psbt.ts | 24 +++++++++++++++++---- 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 57a159c..163d6d2 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -568,15 +568,27 @@ function canFinalize(input, script, scriptType) { return hasSigs(1, input.partialSig); case 'multisig': const p2ms = payments.p2ms({ output: script }); - return hasSigs(p2ms.m, input.partialSig); + return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys); default: return false; } } -function hasSigs(neededSigs, partialSig) { +function hasSigs(neededSigs, partialSig, pubkeys) { if (!partialSig) return false; - if (partialSig.length > neededSigs) throw new Error('Too many signatures'); - return partialSig.length === neededSigs; + let sigs; + if (pubkeys) { + sigs = pubkeys + .map(pkey => { + const pubkey = ecpair_1.fromPublicKey(pkey, { compressed: true }) + .publicKey; + return partialSig.filter(pSig => pSig.pubkey.equals(pubkey))[0]; + }) + .filter(v => !!v); + } else { + sigs = partialSig; + } + if (sigs.length > neededSigs) throw new Error('Too many signatures'); + return sigs.length === neededSigs; } function isFinalized(input) { return !!input.finalScriptSig || !!input.finalScriptWitness; diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts index 2bcee3b..34feec5 100644 --- a/test/integration/transactions.spec.ts +++ b/test/integration/transactions.spec.ts @@ -530,6 +530,36 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }, ); + it( + 'can create (and broadcast via 3PBP) a Transaction, w/ a ' + + 'P2SH(P2MS(2 of 2)) input with nonWitnessUtxo', + async () => { + const myKey = bitcoin.ECPair.makeRandom({ network: regtest }); + const myKeys = [ + myKey, + bitcoin.ECPair.fromPrivateKey(myKey.privateKey!, { network: regtest }), + ]; + const p2sh = createPayment('p2sh-p2ms(2 of 2)', myKeys); + const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh'); + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInput(0, p2sh.keys[0]); + psbt.finalizeAllInputs(); + const tx = psbt.extractTransaction(); + await regtestUtils.broadcast(tx.toHex()); + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + 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; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d35fd4c..d0f2e6b 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -747,16 +747,32 @@ function canFinalize( return hasSigs(1, input.partialSig); case 'multisig': const p2ms = payments.p2ms({ output: script }); - return hasSigs(p2ms.m!, input.partialSig); + return hasSigs(p2ms.m!, input.partialSig, p2ms.pubkeys); default: return false; } } -function hasSigs(neededSigs: number, partialSig?: any[]): boolean { +function hasSigs( + neededSigs: number, + partialSig?: any[], + pubkeys?: Buffer[], +): boolean { if (!partialSig) return false; - if (partialSig.length > neededSigs) throw new Error('Too many signatures'); - return partialSig.length === neededSigs; + let sigs: any; + if (pubkeys) { + sigs = pubkeys + .map(pkey => { + const pubkey = ecPairFromPublicKey(pkey, { compressed: true }) + .publicKey; + return partialSig.filter(pSig => pSig.pubkey.equals(pubkey))[0]; + }) + .filter(v => !!v); + } else { + sigs = partialSig; + } + if (sigs.length > neededSigs) throw new Error('Too many signatures'); + return sigs.length === neededSigs; } function isFinalized(input: PsbtInput): boolean { From 10fcf3d9e1f1da010881c149ded29490b5821e87 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 9 Dec 2019 10:37:55 +0900 Subject: [PATCH 471/568] Remove private __byteLength from Transaction --- src/block.js | 3 +-- src/transaction.js | 49 +++++++++++++++++++-------------------- ts_src/block.ts | 3 +-- ts_src/transaction.ts | 52 +++++++++++++++++++----------------------- types/transaction.d.ts | 3 +-- 5 files changed, 50 insertions(+), 60 deletions(-) diff --git a/src/block.js b/src/block.js index 18b49c7..8ec6c29 100644 --- a/src/block.js +++ b/src/block.js @@ -137,8 +137,7 @@ class Block { return ( 80 + varuint.encodingLength(this.transactions.length) + - // @ts-ignore using the __byteLength private method on Transaction - this.transactions.reduce((a, x) => a + x.__byteLength(allowWitness), 0) + this.transactions.reduce((a, x) => a + x.byteLength(allowWitness), 0) ); } getHash() { diff --git a/src/transaction.js b/src/transaction.js index c4e6506..e2d6a6b 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -179,15 +179,31 @@ class Transaction { }); } weight() { - const base = this.__byteLength(false); - const total = this.__byteLength(true); + const base = this.byteLength(false); + const total = this.byteLength(true); return base * 3 + total; } virtualSize() { return Math.ceil(this.weight() / 4); } - byteLength() { - return this.__byteLength(true); + byteLength(_ALLOW_WITNESS = true) { + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); + return ( + (hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script); + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0) + + (hasWitnesses + ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) + : 0) + ); } clone() { const newTx = new Transaction(); @@ -269,7 +285,7 @@ class Transaction { txTmp.ins[inIndex].script = ourScript; } // serialize and hash - const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4); + const buffer = Buffer.allocUnsafe(txTmp.byteLength(false) + 4); buffer.writeInt32LE(hashType, buffer.length - 4); txTmp.__toBuffer(buffer, 0, false); return bcrypto.hash256(buffer); @@ -386,27 +402,8 @@ class Transaction { typeforce(types.tuple(types.Number, [types.Buffer]), arguments); this.ins[index].witness = witness; } - __byteLength(_ALLOW_WITNESS) { - const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); - return ( - (hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce((sum, input) => { - return sum + 40 + varSliceSize(input.script); - }, 0) + - this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script); - }, 0) + - (hasWitnesses - ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness); - }, 0) - : 0) - ); - } - __toBuffer(buffer, initialOffset, _ALLOW_WITNESS) { - if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS)); + __toBuffer(buffer, initialOffset, _ALLOW_WITNESS = false) { + if (!buffer) buffer = Buffer.allocUnsafe(this.byteLength(_ALLOW_WITNESS)); let offset = initialOffset || 0; function writeSlice(slice) { offset += slice.copy(buffer, offset); diff --git a/ts_src/block.ts b/ts_src/block.ts index 0792edf..9a4d675 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -160,8 +160,7 @@ export class Block { return ( 80 + varuint.encodingLength(this.transactions.length) + - // @ts-ignore using the __byteLength private method on Transaction - this.transactions.reduce((a, x) => a + x.__byteLength(allowWitness), 0) + this.transactions.reduce((a, x) => a + x.byteLength(allowWitness), 0) ); } diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index 0419ba2..88e9242 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -237,8 +237,8 @@ export class Transaction { } weight(): number { - const base = this.__byteLength(false); - const total = this.__byteLength(true); + const base = this.byteLength(false); + const total = this.byteLength(true); return base * 3 + total; } @@ -246,8 +246,25 @@ export class Transaction { return Math.ceil(this.weight() / 4); } - byteLength(): number { - return this.__byteLength(true); + byteLength(_ALLOW_WITNESS: boolean = true): number { + const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); + + return ( + (hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => { + return sum + 40 + varSliceSize(input.script); + }, 0) + + this.outs.reduce((sum, output) => { + return sum + 8 + varSliceSize(output.script); + }, 0) + + (hasWitnesses + ? this.ins.reduce((sum, input) => { + return sum + vectorSize(input.witness); + }, 0) + : 0) + ); } clone(): Transaction { @@ -352,7 +369,7 @@ export class Transaction { } // serialize and hash - const buffer: Buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4); + const buffer: Buffer = Buffer.allocUnsafe(txTmp.byteLength(false) + 4); buffer.writeInt32LE(hashType, buffer.length - 4); txTmp.__toBuffer(buffer, 0, false); @@ -506,34 +523,13 @@ export class Transaction { this.ins[index].witness = witness; } - private __byteLength(_ALLOW_WITNESS: boolean): number { - const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); - - return ( - (hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + - this.ins.reduce((sum, input) => { - return sum + 40 + varSliceSize(input.script); - }, 0) + - this.outs.reduce((sum, output) => { - return sum + 8 + varSliceSize(output.script); - }, 0) + - (hasWitnesses - ? this.ins.reduce((sum, input) => { - return sum + vectorSize(input.witness); - }, 0) - : 0) - ); - } - private __toBuffer( buffer?: Buffer, initialOffset?: number, - _ALLOW_WITNESS?: boolean, + _ALLOW_WITNESS: boolean = false, ): Buffer { if (!buffer) - buffer = Buffer.allocUnsafe(this.__byteLength(_ALLOW_WITNESS!)) as Buffer; + buffer = Buffer.allocUnsafe(this.byteLength(_ALLOW_WITNESS)) as Buffer; let offset = initialOffset || 0; diff --git a/types/transaction.d.ts b/types/transaction.d.ts index f0db04e..6846ea5 100644 --- a/types/transaction.d.ts +++ b/types/transaction.d.ts @@ -30,7 +30,7 @@ export declare class Transaction { hasWitnesses(): boolean; weight(): number; virtualSize(): number; - byteLength(): number; + byteLength(_ALLOW_WITNESS?: boolean): number; clone(): Transaction; /** * Hash transaction for signing a specific input. @@ -48,6 +48,5 @@ export declare class Transaction { toHex(): string; setInputScript(index: number, scriptSig: Buffer): void; setWitness(index: number, witness: Buffer[]): void; - private __byteLength; private __toBuffer; } From ad7577c4e2d74341ecedfee56b45b185b8d81c90 Mon Sep 17 00:00:00 2001 From: Jonathan Underwood <junderwood@bitcoinbank.co.jp> Date: Mon, 9 Dec 2019 10:40:05 +0900 Subject: [PATCH 472/568] Update ts_src/psbt.ts Co-Authored-By: d-yokoi <d-yokoi@users.noreply.github.com> --- ts_src/psbt.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d0f2e6b..207ec66 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -765,7 +765,7 @@ function hasSigs( .map(pkey => { const pubkey = ecPairFromPublicKey(pkey, { compressed: true }) .publicKey; - return partialSig.filter(pSig => pSig.pubkey.equals(pubkey))[0]; + return partialSig.find(pSig => pSig.pubkey.equals(pubkey)); }) .filter(v => !!v); } else { From 85e4512e08fa64700fe76f398ae665b0076e6547 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 9 Dec 2019 14:54:22 +0900 Subject: [PATCH 473/568] Add JS fixes too --- src/psbt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psbt.js b/src/psbt.js index 163d6d2..fa886c6 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -581,7 +581,7 @@ function hasSigs(neededSigs, partialSig, pubkeys) { .map(pkey => { const pubkey = ecpair_1.fromPublicKey(pkey, { compressed: true }) .publicKey; - return partialSig.filter(pSig => pSig.pubkey.equals(pubkey))[0]; + return partialSig.find(pSig => pSig.pubkey.equals(pubkey)); }) .filter(v => !!v); } else { From 82ce1a76921966f8c055b78108672d174f3e957f Mon Sep 17 00:00:00 2001 From: SondreB <sondre@outlook.com> Date: Sun, 15 Dec 2019 12:53:52 +0100 Subject: [PATCH 474/568] Update package.json to be compatible with Windows - Add double-quote to prettier execution. - Remove the logging output to /dev/null. This does cause a bit more logging to be outputted. - Closes #1523 --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c239e9f..6fc8291 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "coverage-html": "npm run build && npm run nobuild:coverage-html", "coverage": "npm run build && npm run nobuild:coverage", "format": "npm run prettier -- --write", - "formatjs": "npm run prettierjs -- --write > /dev/null 2>&1", + "formatjs": "npm run prettierjs -- --write", "format:ci": "npm run prettier -- --check && npm run prettierjs -- --check", "gitdiff:ci": "npm run build && git diff --exit-code", "integration": "npm run build && npm run nobuild:integration", @@ -35,8 +35,8 @@ "nobuild:coverage": "npm run build:tests && nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha && npm run clean:jstests", "nobuild:integration": "npm run mocha:ts -- --timeout 50000 'test/integration/*.ts'", "nobuild:unit": "npm run mocha:ts -- 'test/*.ts'", - "prettier": "prettier 'ts_src/**/*.ts' 'test/**/*.ts' --ignore-path ./.prettierignore", - "prettierjs": "prettier 'src/**/*.js' --ignore-path ./.prettierignore", + "prettier": "prettier \"ts_src/**/*.ts\" \"test/**/*.ts\" --ignore-path ./.prettierignore", + "prettierjs": "prettier \"src/**/*.js\" --ignore-path ./.prettierignore", "test": "npm run build && npm run format:ci && npm run lint && npm run nobuild:coverage", "unit": "npm run build && npm run nobuild:unit" }, From 737af04b0814ae1f525548a2d9b76c9e498b8466 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 6 Jan 2020 12:02:31 +0900 Subject: [PATCH 475/568] Update nyc for handlebars vuln --- package-lock.json | 1251 +++++++++++++++++++++++++++------------------ package.json | 2 +- 2 files changed, 764 insertions(+), 489 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e364ce..95bca8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,46 +13,98 @@ "@babel/highlight": "^7.0.0" } }, - "@babel/generator": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", - "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", + "@babel/core": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.7.tgz", + "integrity": "sha512-jlSjuj/7z138NLZALxVgrx13AOtqip42ATZP7+kYl53GvDV6+4dCek1mVUo8z8c8Xnw/mx2q3d9HWh3griuesQ==", "dev": true, "requires": { - "@babel/types": "^7.5.5", + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.7.7", + "@babel/helpers": "^7.7.4", + "@babel/parser": "^7.7.7", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "@babel/generator": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.7.tgz", + "integrity": "sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==", + "dev": true, + "requires": { + "@babel/types": "^7.7.4", "jsesc": "^2.5.1", "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", + "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-get-function-arity": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", + "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", + "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", "dev": true, "requires": { - "@babel/types": "^7.4.4" + "@babel/types": "^7.7.4" + } + }, + "@babel/helpers": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz", + "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==", + "dev": true, + "requires": { + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/highlight": { @@ -67,34 +119,34 @@ } }, "@babel/parser": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", - "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.7.tgz", + "integrity": "sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw==", "dev": true }, "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", + "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/traverse": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", - "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", + "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.5.5", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.5.5", - "@babel/types": "^7.5.5", + "@babel/generator": "^7.7.4", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -121,9 +173,9 @@ } }, "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", + "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -131,6 +183,60 @@ "to-fast-properties": "^2.0.0" } }, + "@istanbuljs/load-nyc-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", + "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, "@types/base-x": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/base-x/-/base-x-3.0.0.tgz", @@ -149,6 +255,12 @@ "@types/base-x": "*" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/mocha": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", @@ -167,6 +279,16 @@ "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==", "dev": true }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", @@ -189,12 +311,12 @@ } }, "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "dev": true, "requires": { - "default-require-extensions": "^2.0.0" + "default-require-extensions": "^3.0.0" } }, "archy": { @@ -373,15 +495,15 @@ "dev": true }, "caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "dev": true, "requires": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" } }, "camelcase": { @@ -410,6 +532,12 @@ "safe-buffer": "^5.0.1" } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -461,27 +589,14 @@ "dev": true }, "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" } }, - "cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" - } - }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -536,12 +651,12 @@ "dev": true }, "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", "dev": true, "requires": { - "strip-bom": "^3.0.0" + "strip-bom": "^4.0.0" } }, "define-properties": { @@ -597,15 +712,6 @@ "once": "^1.4.0" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, "es-abstract": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", @@ -686,14 +792,14 @@ } }, "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz", + "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==", "dev": true, "requires": { "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "make-dir": "^3.0.0", + "pkg-dir": "^4.1.0" } }, "find-up": { @@ -715,27 +821,64 @@ } }, "foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "dev": true, "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" }, "dependencies": { "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" } } } }, + "fromentries": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", + "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -784,9 +927,9 @@ "dev": true }, "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, "growl": { @@ -795,26 +938,6 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", - "dev": true, - "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -855,12 +978,21 @@ } }, "hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.1.0.tgz", + "integrity": "sha512-OFPDWmzPN1l7atOV1TgBVmNtBxaIysToK6Ve9DK+vT6pYuklw/nPNT+HJbZi0KDcI6vWB+9tgvZ5YD7fA3CXcA==", "dev": true, "requires": { - "is-stream": "^1.0.1" + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + } } }, "he": { @@ -885,10 +1017,10 @@ "integrity": "sha512-j1jog3tDfhpWlqbVbh29qc7FG7w+NT4ed+QQFGqvww83+50AzzretB7wykZGOe28mBdvCYH3GdHaVWJQ2lJ/4w==", "dev": true }, - "hosted-git-info": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", - "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "html-escaper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", + "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==", "dev": true }, "imurmurhash": { @@ -897,6 +1029,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -918,12 +1056,6 @@ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, "is-buffer": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", @@ -978,6 +1110,18 @@ "has-symbols": "^1.0.0" } }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -985,33 +1129,33 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true }, "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "dev": true, "requires": { - "append-transform": "^1.0.0" + "append-transform": "^2.0.0" } }, "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.0.tgz", + "integrity": "sha512-Nm4wVHdo7ZXSG30KjZ2Wl5SU/Bw7bDx1PdaiIFzEStdjs0H12mOTncn1GVYuqQSaZxpg87VGBRsVRPGD2cD1AQ==", "dev": true, "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "dependencies": { "semver": { @@ -1022,38 +1166,123 @@ } } }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" }, "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" } } } }, "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", "dev": true, "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", + "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" }, "dependencies": { @@ -1075,12 +1304,13 @@ } }, "istanbul-reports": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", - "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A==", "dev": true, "requires": { - "handlebars": "^4.1.2" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, "js-tokens": { @@ -1105,11 +1335,22 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } }, "lcid": { "version": "2.0.0", @@ -1120,26 +1361,6 @@ "invert-kv": "^2.0.0" } }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -1171,24 +1392,21 @@ "chalk": "^2.0.1" } }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", + "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", "dev": true, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "make-error": { @@ -1233,23 +1451,6 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "merkle-lib": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", @@ -1378,18 +1579,6 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "dev": true - }, - "nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", - "dev": true - }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -1406,27 +1595,13 @@ "semver": "^5.7.0" } }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } + "process-on-spawn": "^1.0.0" } }, "npm-run-path": { @@ -1445,42 +1620,103 @@ "dev": true }, "nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.0.tgz", + "integrity": "sha512-qcLBlNCKMDVuKb7d1fpxjPR8sHeMVX0CHarXAVzrVWoFrigCkYR8xcrjfXSPi5HXM7EU78L6ywO7w1c5rZNCNg==", "dev": true, "requires": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", + "make-dir": "^3.0.0", + "node-preload": "^0.2.0", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "uuid": "^3.3.3", + "yargs": "^15.0.2" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1490,6 +1726,105 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz", + "integrity": "sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^16.1.0" + } + }, + "yargs-parser": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz", + "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, @@ -1530,22 +1865,6 @@ "wrappy": "1" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, "os-locale": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", @@ -1593,6 +1912,15 @@ "p-limit": "^2.0.0" } }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -1600,27 +1928,17 @@ "dev": true }, "package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "dev": true, "requires": { "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", + "hasha": "^5.0.0", "lodash.flattendeep": "^4.4.0", "release-zalgo": "^1.0.0" } }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -1645,23 +1963,6 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, "pbkdf2": { "version": "3.0.17", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", @@ -1675,19 +1976,49 @@ "sha.js": "^2.4.8" } }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } } }, "prettier": { @@ -1696,6 +2027,15 @@ "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==", "dev": true }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, "proxyquire": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.0.tgz", @@ -1707,12 +2047,6 @@ "resolve": "~1.8.1" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -1739,27 +2073,6 @@ "safe-buffer": "^5.1.0" } }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, "regtest-client": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/regtest-client/-/regtest-client-0.2.0.tgz", @@ -1802,9 +2115,9 @@ } }, "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "rimraf": { @@ -1913,51 +2226,53 @@ } }, "spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "dev": true, "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", "signal-exit": "^3.0.2", - "which": "^1.3.0" + "which": "^2.0.1" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -1990,9 +2305,9 @@ } }, "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, "strip-eof": { @@ -2017,21 +2332,20 @@ } }, "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "dependencies": { "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2062,12 +2376,6 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, "ts-node": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", @@ -2125,6 +2433,21 @@ "tslib": "^1.8.1" } }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "typeforce": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", @@ -2136,49 +2459,12 @@ "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", "dev": true }, - "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", - "dev": true, - "optional": true, - "requires": { - "commander": "~2.20.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true, - "optional": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, "uuid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", "dev": true }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "varuint-bitcoin": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz", @@ -2219,12 +2505,6 @@ "bs58check": "<3.0.0" } }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", @@ -2279,14 +2559,15 @@ "dev": true }, "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", + "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "y18n": { @@ -2295,12 +2576,6 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, "yargs": { "version": "13.2.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", diff --git a/package.json b/package.json index c239e9f..7b87d7e 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^6.2.0", - "nyc": "^14.1.1", + "nyc": "^15.0.0", "prettier": "1.16.4", "proxyquire": "^2.0.1", "regtest-client": "0.2.0", From b31049061b529dab0bd7fd83eaa009e9581003dc Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 6 Jan 2020 12:31:00 +0900 Subject: [PATCH 476/568] CHANGELOG bump --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0f1e66..fecfc19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 5.1.7 +__fixed__ +- Fixed Transaction class Output interface typing for TypeScript (#1506) +- Add `weight()` to Block class, add optional includeWitness arg to Transaction byteLength method (#1515) +- Match the old TransactionBuilder behavior of allowing for multiple instances of the same pubkey to be in a p2ms script for PSBT (#1519) + +__added__ +- Allow the API consumer to pass in the finalizer functions to allow for any type of transaction to be finalized. It places the most crucial part of transaction construction on the consumer, and should be used with caution. (#1491) + # 5.1.6 __fixed__ - `PsbtOutputExtended` did not support using the address attribute properly. It is now fixed. From a8194e55871df08ea4b9ec3b315167f52a7d0ffa Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 6 Jan 2020 12:31:06 +0900 Subject: [PATCH 477/568] 5.1.7 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95bca8c..41f545c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.6", + "version": "5.1.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 7b87d7e..135402b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.6", + "version": "5.1.7", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 02398bee911df5463de2f20730ff5f239da92b84 Mon Sep 17 00:00:00 2001 From: serinuntius <wasab1gj@gmail.com> Date: Mon, 6 Jan 2020 16:58:59 +0900 Subject: [PATCH 478/568] Update License --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 064b885..0383ebc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2011-2019 bitcoinjs-lib contributors +Copyright (c) 2011-2020 bitcoinjs-lib contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From cec5fb53571cc3ca8283b9d315d56675c933a511 Mon Sep 17 00:00:00 2001 From: Otto Allmendinger <otto@bitgo.com> Date: Tue, 14 Jan 2020 09:53:32 +0100 Subject: [PATCH 479/568] Extract BufferWriter class Move various write methods to a class `BufferWriter`. This allows increased code reuse for libraries that want to implement different serialization formats. Also de-duplicates some code paths and increases test coverage. Original concept by idea by https://github.com/argjv: https://github.com/BitGo/bitgo-utxo-lib/blob/master/src/bufferWriter.js --- src/buffer_writer.js | 44 +++++++ src/transaction.js | 129 ++++++++------------- test/buffer_writer.spec.ts | 231 +++++++++++++++++++++++++++++++++++++ ts_src/buffer_writer.ts | 54 +++++++++ ts_src/transaction.ts | 139 +++++++--------------- types/buffer_writer.d.ts | 16 +++ 6 files changed, 432 insertions(+), 181 deletions(-) create mode 100644 src/buffer_writer.js create mode 100644 test/buffer_writer.spec.ts create mode 100644 ts_src/buffer_writer.ts create mode 100644 types/buffer_writer.d.ts diff --git a/src/buffer_writer.js b/src/buffer_writer.js new file mode 100644 index 0000000..19a2332 --- /dev/null +++ b/src/buffer_writer.js @@ -0,0 +1,44 @@ +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +const bufferutils = require('./bufferutils'); +const types = require('./types'); +const typeforce = require('typeforce'); +const varuint = require('varuint-bitcoin'); +/** + * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + */ +class BufferWriter { + constructor(buffer, offset = 0) { + typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); + this.buffer = buffer; + this.offset = offset; + } + writeUInt8(i) { + this.offset = this.buffer.writeUInt8(i, this.offset); + } + writeInt32(i) { + this.offset = this.buffer.writeInt32LE(i, this.offset); + } + writeUInt32(i) { + this.offset = this.buffer.writeUInt32LE(i, this.offset); + } + writeUInt64(i) { + this.offset = bufferutils.writeUInt64LE(this.buffer, i, this.offset); + } + writeVarInt(i) { + varuint.encode(i, this.buffer, this.offset); + this.offset += varuint.encode.bytes; + } + writeSlice(slice) { + this.offset += slice.copy(this.buffer, this.offset); + } + writeVarSlice(slice) { + this.writeVarInt(slice.length); + this.writeSlice(slice); + } + writeVector(vector) { + this.writeVarInt(vector.length); + vector.forEach(buf => this.writeVarSlice(buf)); + } +} +exports.BufferWriter = BufferWriter; diff --git a/src/transaction.js b/src/transaction.js index e2d6a6b..fc36a63 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +const buffer_writer_1 = require('./buffer_writer'); const bufferutils = require('./bufferutils'); const bufferutils_1 = require('./bufferutils'); const bcrypto = require('./crypto'); @@ -296,33 +297,16 @@ class Transaction { arguments, ); let tbuffer = Buffer.from([]); - let toffset = 0; - function writeSlice(slice) { - toffset += slice.copy(tbuffer, toffset); - } - function writeUInt32(i) { - toffset = tbuffer.writeUInt32LE(i, toffset); - } - function writeUInt64(i) { - toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset); - } - function writeVarInt(i) { - varuint.encode(i, tbuffer, toffset); - toffset += varuint.encode.bytes; - } - function writeVarSlice(slice) { - writeVarInt(slice.length); - writeSlice(slice); - } + let bufferWriter; let hashOutputs = ZERO; let hashPrevouts = ZERO; let hashSequence = ZERO; if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { tbuffer = Buffer.allocUnsafe(36 * this.ins.length); - toffset = 0; + bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); this.ins.forEach(txIn => { - writeSlice(txIn.hash); - writeUInt32(txIn.index); + bufferWriter.writeSlice(txIn.hash); + bufferWriter.writeUInt32(txIn.index); }); hashPrevouts = bcrypto.hash256(tbuffer); } @@ -332,9 +316,9 @@ class Transaction { (hashType & 0x1f) !== Transaction.SIGHASH_NONE ) { tbuffer = Buffer.allocUnsafe(4 * this.ins.length); - toffset = 0; + bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); this.ins.forEach(txIn => { - writeUInt32(txIn.sequence); + bufferWriter.writeUInt32(txIn.sequence); }); hashSequence = bcrypto.hash256(tbuffer); } @@ -346,10 +330,10 @@ class Transaction { return sum + 8 + varSliceSize(output.script); }, 0); tbuffer = Buffer.allocUnsafe(txOutsSize); - toffset = 0; + bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); this.outs.forEach(out => { - writeUInt64(out.value); - writeVarSlice(out.script); + bufferWriter.writeUInt64(out.value); + bufferWriter.writeVarSlice(out.script); }); hashOutputs = bcrypto.hash256(tbuffer); } else if ( @@ -358,25 +342,25 @@ class Transaction { ) { const output = this.outs[inIndex]; tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); - toffset = 0; - writeUInt64(output.value); - writeVarSlice(output.script); + bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); + bufferWriter.writeUInt64(output.value); + bufferWriter.writeVarSlice(output.script); hashOutputs = bcrypto.hash256(tbuffer); } tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)); - toffset = 0; + bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); const input = this.ins[inIndex]; - writeUInt32(this.version); - writeSlice(hashPrevouts); - writeSlice(hashSequence); - writeSlice(input.hash); - writeUInt32(input.index); - writeVarSlice(prevOutScript); - writeUInt64(value); - writeUInt32(input.sequence); - writeSlice(hashOutputs); - writeUInt32(this.locktime); - writeUInt32(hashType); + bufferWriter.writeUInt32(this.version); + bufferWriter.writeSlice(hashPrevouts); + bufferWriter.writeSlice(hashSequence); + bufferWriter.writeSlice(input.hash); + bufferWriter.writeUInt32(input.index); + bufferWriter.writeVarSlice(prevOutScript); + bufferWriter.writeUInt64(value); + bufferWriter.writeUInt32(input.sequence); + bufferWriter.writeSlice(hashOutputs); + bufferWriter.writeUInt32(this.locktime); + bufferWriter.writeUInt32(hashType); return bcrypto.hash256(tbuffer); } getHash(forWitness) { @@ -404,64 +388,41 @@ class Transaction { } __toBuffer(buffer, initialOffset, _ALLOW_WITNESS = false) { if (!buffer) buffer = Buffer.allocUnsafe(this.byteLength(_ALLOW_WITNESS)); - let offset = initialOffset || 0; - function writeSlice(slice) { - offset += slice.copy(buffer, offset); - } - function writeUInt8(i) { - offset = buffer.writeUInt8(i, offset); - } - function writeUInt32(i) { - offset = buffer.writeUInt32LE(i, offset); - } - function writeInt32(i) { - offset = buffer.writeInt32LE(i, offset); - } - function writeUInt64(i) { - offset = bufferutils.writeUInt64LE(buffer, i, offset); - } - function writeVarInt(i) { - varuint.encode(i, buffer, offset); - offset += varuint.encode.bytes; - } - function writeVarSlice(slice) { - writeVarInt(slice.length); - writeSlice(slice); - } - function writeVector(vector) { - writeVarInt(vector.length); - vector.forEach(writeVarSlice); - } - writeInt32(this.version); + const bufferWriter = new buffer_writer_1.BufferWriter( + buffer, + initialOffset || 0, + ); + bufferWriter.writeInt32(this.version); const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); if (hasWitnesses) { - writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); - writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); + bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); + bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); } - writeVarInt(this.ins.length); + bufferWriter.writeVarInt(this.ins.length); this.ins.forEach(txIn => { - writeSlice(txIn.hash); - writeUInt32(txIn.index); - writeVarSlice(txIn.script); - writeUInt32(txIn.sequence); + bufferWriter.writeSlice(txIn.hash); + bufferWriter.writeUInt32(txIn.index); + bufferWriter.writeVarSlice(txIn.script); + bufferWriter.writeUInt32(txIn.sequence); }); - writeVarInt(this.outs.length); + bufferWriter.writeVarInt(this.outs.length); this.outs.forEach(txOut => { if (isOutput(txOut)) { - writeUInt64(txOut.value); + bufferWriter.writeUInt64(txOut.value); } else { - writeSlice(txOut.valueBuffer); + bufferWriter.writeSlice(txOut.valueBuffer); } - writeVarSlice(txOut.script); + bufferWriter.writeVarSlice(txOut.script); }); if (hasWitnesses) { this.ins.forEach(input => { - writeVector(input.witness); + bufferWriter.writeVector(input.witness); }); } - writeUInt32(this.locktime); + bufferWriter.writeUInt32(this.locktime); // avoid slicing unless necessary - if (initialOffset !== undefined) return buffer.slice(initialOffset, offset); + if (initialOffset !== undefined) + return buffer.slice(initialOffset, bufferWriter.offset); return buffer; } } diff --git a/test/buffer_writer.spec.ts b/test/buffer_writer.spec.ts new file mode 100644 index 0000000..8b33e2e --- /dev/null +++ b/test/buffer_writer.spec.ts @@ -0,0 +1,231 @@ +import * as assert from 'assert'; +import { describe, it } from 'mocha'; +import { BufferWriter } from '../src/buffer_writer'; + +const varuint = require('varuint-bitcoin'); + +describe('BufferWriter', () => { + function concatToBuffer(values: number[][]): Buffer { + return Buffer.concat(values.map(data => Buffer.from(data))); + } + + function testBuffer( + bufferWriter: BufferWriter, + expectedBuffer: Buffer, + expectedOffset: number = expectedBuffer.length, + ): void { + assert.strictEqual(bufferWriter.offset, expectedOffset); + assert.deepStrictEqual( + bufferWriter.buffer.slice(0, expectedOffset), + expectedBuffer.slice(0, expectedOffset), + ); + } + + it('writeUint8', () => { + const values = [0, 1, 254, 255]; + const expectedBuffer = Buffer.from([0, 1, 0xfe, 0xff]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((v: number) => { + const expectedOffset = bufferWriter.offset + 1; + bufferWriter.writeUInt8(v); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeInt32', () => { + const values = [ + 0, + 1, + Math.pow(2, 31) - 2, + Math.pow(2, 31) - 1, + -1, + -Math.pow(2, 31), + ]; + const expectedBuffer = concatToBuffer([ + [0, 0, 0, 0], + [1, 0, 0, 0], + [0xfe, 0xff, 0xff, 0x7f], + [0xff, 0xff, 0xff, 0x7f], + [0xff, 0xff, 0xff, 0xff], + [0x00, 0x00, 0x00, 0x80], + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: number) => { + const expectedOffset = bufferWriter.offset + 4; + bufferWriter.writeInt32(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeUInt32', () => { + const maxUInt32 = Math.pow(2, 32) - 1; + const values = [0, 1, Math.pow(2, 16), maxUInt32]; + const expectedBuffer = concatToBuffer([ + [0, 0, 0, 0], + [1, 0, 0, 0], + [0, 0, 1, 0], + [0xff, 0xff, 0xff, 0xff], + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: number) => { + const expectedOffset = bufferWriter.offset + 4; + bufferWriter.writeUInt32(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeUInt64', () => { + const values = [ + 0, + 1, + Math.pow(2, 32), + Number.MAX_SAFE_INTEGER /* 2^53 - 1 */, + ]; + const expectedBuffer = concatToBuffer([ + [0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00], + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: number) => { + const expectedOffset = bufferWriter.offset + 8; + bufferWriter.writeUInt64(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeVarInt', () => { + const values = [ + 0, + 1, + 252, + 253, + 254, + 255, + 256, + Math.pow(2, 16) - 2, + Math.pow(2, 16) - 1, + Math.pow(2, 16), + Math.pow(2, 32) - 2, + Math.pow(2, 32) - 1, + Math.pow(2, 32), + Number.MAX_SAFE_INTEGER, + ]; + const expectedBuffer = concatToBuffer([ + [0x00], + [0x01], + [0xfc], + [0xfd, 0xfd, 0x00], + [0xfd, 0xfe, 0x00], + [0xfd, 0xff, 0x00], + [0xfd, 0x00, 0x01], + [0xfd, 0xfe, 0xff], + [0xfd, 0xff, 0xff], + [0xfe, 0x00, 0x00, 0x01, 0x00], + [0xfe, 0xfe, 0xff, 0xff, 0xff], + [0xfe, 0xff, 0xff, 0xff, 0xff], + [0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00], + [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00], + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: number) => { + const expectedOffset = + bufferWriter.offset + varuint.encodingLength(value); + bufferWriter.writeVarInt(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeSlice', () => { + const values = [[], [1], [1, 2, 3, 4], [254, 255]]; + const expectedBuffer = concatToBuffer(values); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((v: number[]) => { + const expectedOffset = bufferWriter.offset + v.length; + bufferWriter.writeSlice(Buffer.from(v)); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeVarSlice', () => { + const values = [ + Buffer.alloc(1, 1), + Buffer.alloc(252, 2), + Buffer.alloc(253, 3), + ]; + const expectedBuffer = Buffer.concat([ + Buffer.from([0x01, 0x01]), + Buffer.from([0xfc]), + Buffer.alloc(252, 0x02), + Buffer.from([0xfd, 0xfd, 0x00]), + Buffer.alloc(253, 0x03), + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: Buffer) => { + const expectedOffset = + bufferWriter.offset + + varuint.encodingLength(value.length) + + value.length; + bufferWriter.writeVarSlice(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeVector', () => { + const values = [ + [Buffer.alloc(1, 4), Buffer.alloc(253, 5)], + Array(253).fill(Buffer.alloc(1, 6)), + ]; + const expectedBuffer = Buffer.concat([ + Buffer.from([0x02]), + Buffer.from([0x01, 0x04]), + Buffer.from([0xfd, 0xfd, 0x00]), + Buffer.alloc(253, 5), + + Buffer.from([0xfd, 0xfd, 0x00]), + Buffer.concat( + Array(253) + .fill(0) + .map(() => Buffer.from([0x01, 0x06])), + ), + ]); + + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: Buffer[]) => { + const expectedOffset = + bufferWriter.offset + + varuint.encodingLength(value.length) + + value.reduce( + (sum: number, v) => sum + varuint.encodingLength(v.length) + v.length, + 0, + ); + bufferWriter.writeVector(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); +}); diff --git a/ts_src/buffer_writer.ts b/ts_src/buffer_writer.ts new file mode 100644 index 0000000..541457d --- /dev/null +++ b/ts_src/buffer_writer.ts @@ -0,0 +1,54 @@ +import * as bufferutils from './bufferutils'; +import * as types from './types'; + +const typeforce = require('typeforce'); +const varuint = require('varuint-bitcoin'); + +/** + * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + */ +export class BufferWriter { + buffer: Buffer; + offset: number; + + constructor(buffer: Buffer, offset: number = 0) { + typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); + this.buffer = buffer; + this.offset = offset; + } + + writeUInt8(i: number): void { + this.offset = this.buffer.writeUInt8(i, this.offset); + } + + writeInt32(i: number): void { + this.offset = this.buffer.writeInt32LE(i, this.offset); + } + + writeUInt32(i: number): void { + this.offset = this.buffer.writeUInt32LE(i, this.offset); + } + + writeUInt64(i: number): void { + this.offset = bufferutils.writeUInt64LE(this.buffer, i, this.offset); + } + + writeVarInt(i: number): void { + varuint.encode(i, this.buffer, this.offset); + this.offset += varuint.encode.bytes; + } + + writeSlice(slice: Buffer): void { + this.offset += slice.copy(this.buffer, this.offset); + } + + writeVarSlice(slice: Buffer): void { + this.writeVarInt(slice.length); + this.writeSlice(slice); + } + + writeVector(vector: Buffer[]): void { + this.writeVarInt(vector.length); + vector.forEach((buf: Buffer) => this.writeVarSlice(buf)); + } +} diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index 88e9242..ac5aea4 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -1,3 +1,4 @@ +import { BufferWriter } from './buffer_writer'; import * as bufferutils from './bufferutils'; import { reverseBuffer } from './bufferutils'; import * as bcrypto from './crypto'; @@ -388,29 +389,7 @@ export class Transaction { ); let tbuffer: Buffer = Buffer.from([]); - let toffset: number = 0; - - function writeSlice(slice: Buffer): void { - toffset += slice.copy(tbuffer, toffset); - } - - function writeUInt32(i: number): void { - toffset = tbuffer.writeUInt32LE(i, toffset); - } - - function writeUInt64(i: number): void { - toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset); - } - - function writeVarInt(i: number): void { - varuint.encode(i, tbuffer, toffset); - toffset += varuint.encode.bytes; - } - - function writeVarSlice(slice: Buffer): void { - writeVarInt(slice.length); - writeSlice(slice); - } + let bufferWriter: BufferWriter; let hashOutputs = ZERO; let hashPrevouts = ZERO; @@ -418,11 +397,11 @@ export class Transaction { if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { tbuffer = Buffer.allocUnsafe(36 * this.ins.length); - toffset = 0; + bufferWriter = new BufferWriter(tbuffer, 0); this.ins.forEach(txIn => { - writeSlice(txIn.hash); - writeUInt32(txIn.index); + bufferWriter.writeSlice(txIn.hash); + bufferWriter.writeUInt32(txIn.index); }); hashPrevouts = bcrypto.hash256(tbuffer); @@ -434,10 +413,10 @@ export class Transaction { (hashType & 0x1f) !== Transaction.SIGHASH_NONE ) { tbuffer = Buffer.allocUnsafe(4 * this.ins.length); - toffset = 0; + bufferWriter = new BufferWriter(tbuffer, 0); this.ins.forEach(txIn => { - writeUInt32(txIn.sequence); + bufferWriter.writeUInt32(txIn.sequence); }); hashSequence = bcrypto.hash256(tbuffer); @@ -452,11 +431,11 @@ export class Transaction { }, 0); tbuffer = Buffer.allocUnsafe(txOutsSize); - toffset = 0; + bufferWriter = new BufferWriter(tbuffer, 0); this.outs.forEach(out => { - writeUInt64(out.value); - writeVarSlice(out.script); + bufferWriter.writeUInt64(out.value); + bufferWriter.writeVarSlice(out.script); }); hashOutputs = bcrypto.hash256(tbuffer); @@ -467,28 +446,28 @@ export class Transaction { const output = this.outs[inIndex]; tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); - toffset = 0; - writeUInt64(output.value); - writeVarSlice(output.script); + bufferWriter = new BufferWriter(tbuffer, 0); + bufferWriter.writeUInt64(output.value); + bufferWriter.writeVarSlice(output.script); hashOutputs = bcrypto.hash256(tbuffer); } tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)); - toffset = 0; + bufferWriter = new BufferWriter(tbuffer, 0); const input = this.ins[inIndex]; - writeUInt32(this.version); - writeSlice(hashPrevouts); - writeSlice(hashSequence); - writeSlice(input.hash); - writeUInt32(input.index); - writeVarSlice(prevOutScript); - writeUInt64(value); - writeUInt32(input.sequence); - writeSlice(hashOutputs); - writeUInt32(this.locktime); - writeUInt32(hashType); + bufferWriter.writeUInt32(this.version); + bufferWriter.writeSlice(hashPrevouts); + bufferWriter.writeSlice(hashSequence); + bufferWriter.writeSlice(input.hash); + bufferWriter.writeUInt32(input.index); + bufferWriter.writeVarSlice(prevOutScript); + bufferWriter.writeUInt64(value); + bufferWriter.writeUInt32(input.sequence); + bufferWriter.writeSlice(hashOutputs); + bufferWriter.writeUInt32(this.locktime); + bufferWriter.writeUInt32(hashType); return bcrypto.hash256(tbuffer); } @@ -531,82 +510,48 @@ export class Transaction { if (!buffer) buffer = Buffer.allocUnsafe(this.byteLength(_ALLOW_WITNESS)) as Buffer; - let offset = initialOffset || 0; + const bufferWriter = new BufferWriter(buffer, initialOffset || 0); - function writeSlice(slice: Buffer): void { - offset += slice.copy(buffer!, offset); - } - - function writeUInt8(i: number): void { - offset = buffer!.writeUInt8(i, offset); - } - - function writeUInt32(i: number): void { - offset = buffer!.writeUInt32LE(i, offset); - } - - function writeInt32(i: number): void { - offset = buffer!.writeInt32LE(i, offset); - } - - function writeUInt64(i: number): void { - offset = bufferutils.writeUInt64LE(buffer!, i, offset); - } - - function writeVarInt(i: number): void { - varuint.encode(i, buffer, offset); - offset += varuint.encode.bytes; - } - - function writeVarSlice(slice: Buffer): void { - writeVarInt(slice.length); - writeSlice(slice); - } - - function writeVector(vector: Buffer[]): void { - writeVarInt(vector.length); - vector.forEach(writeVarSlice); - } - - writeInt32(this.version); + bufferWriter.writeInt32(this.version); const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); if (hasWitnesses) { - writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); - writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); + bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER); + bufferWriter.writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG); } - writeVarInt(this.ins.length); + bufferWriter.writeVarInt(this.ins.length); this.ins.forEach(txIn => { - writeSlice(txIn.hash); - writeUInt32(txIn.index); - writeVarSlice(txIn.script); - writeUInt32(txIn.sequence); + bufferWriter.writeSlice(txIn.hash); + bufferWriter.writeUInt32(txIn.index); + bufferWriter.writeVarSlice(txIn.script); + bufferWriter.writeUInt32(txIn.sequence); }); - writeVarInt(this.outs.length); + bufferWriter.writeVarInt(this.outs.length); this.outs.forEach(txOut => { if (isOutput(txOut)) { - writeUInt64(txOut.value); + bufferWriter.writeUInt64(txOut.value); } else { - writeSlice((txOut as any).valueBuffer); + bufferWriter.writeSlice((txOut as any).valueBuffer); } - writeVarSlice(txOut.script); + bufferWriter.writeVarSlice(txOut.script); }); if (hasWitnesses) { this.ins.forEach(input => { - writeVector(input.witness); + bufferWriter.writeVector(input.witness); }); } - writeUInt32(this.locktime); + bufferWriter.writeUInt32(this.locktime); // avoid slicing unless necessary - if (initialOffset !== undefined) return buffer.slice(initialOffset, offset); + if (initialOffset !== undefined) + return buffer.slice(initialOffset, bufferWriter.offset); return buffer; } } diff --git a/types/buffer_writer.d.ts b/types/buffer_writer.d.ts new file mode 100644 index 0000000..5ff07f9 --- /dev/null +++ b/types/buffer_writer.d.ts @@ -0,0 +1,16 @@ +/** + * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + */ +export declare class BufferWriter { + buffer: Buffer; + offset: number; + constructor(buffer: Buffer, offset?: number); + writeUInt8(i: number): void; + writeInt32(i: number): void; + writeUInt32(i: number): void; + writeUInt64(i: number): void; + writeVarInt(i: number): void; + writeSlice(slice: Buffer): void; + writeVarSlice(slice: Buffer): void; + writeVector(vector: Buffer[]): void; +} From 91e6c8abc3517571eca08fefe13adc433394d2bb Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 15 Jan 2020 10:44:29 +0900 Subject: [PATCH 480/568] Move to bufferutils and add BufferReader --- src/buffer_writer.js | 44 ----------------- src/bufferutils.js | 90 +++++++++++++++++++++++++++++++++ src/transaction.js | 13 +++-- ts_src/buffer_writer.ts | 54 -------------------- ts_src/bufferutils.ts | 104 +++++++++++++++++++++++++++++++++++++++ ts_src/transaction.ts | 3 +- types/buffer_writer.d.ts | 16 ------ types/bufferutils.d.ts | 32 ++++++++++++ 8 files changed, 233 insertions(+), 123 deletions(-) delete mode 100644 src/buffer_writer.js delete mode 100644 ts_src/buffer_writer.ts delete mode 100644 types/buffer_writer.d.ts diff --git a/src/buffer_writer.js b/src/buffer_writer.js deleted file mode 100644 index 19a2332..0000000 --- a/src/buffer_writer.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const bufferutils = require('./bufferutils'); -const types = require('./types'); -const typeforce = require('typeforce'); -const varuint = require('varuint-bitcoin'); -/** - * Helper class for serialization of bitcoin data types into a pre-allocated buffer. - */ -class BufferWriter { - constructor(buffer, offset = 0) { - typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); - this.buffer = buffer; - this.offset = offset; - } - writeUInt8(i) { - this.offset = this.buffer.writeUInt8(i, this.offset); - } - writeInt32(i) { - this.offset = this.buffer.writeInt32LE(i, this.offset); - } - writeUInt32(i) { - this.offset = this.buffer.writeUInt32LE(i, this.offset); - } - writeUInt64(i) { - this.offset = bufferutils.writeUInt64LE(this.buffer, i, this.offset); - } - writeVarInt(i) { - varuint.encode(i, this.buffer, this.offset); - this.offset += varuint.encode.bytes; - } - writeSlice(slice) { - this.offset += slice.copy(this.buffer, this.offset); - } - writeVarSlice(slice) { - this.writeVarInt(slice.length); - this.writeSlice(slice); - } - writeVector(vector) { - this.writeVarInt(vector.length); - vector.forEach(buf => this.writeVarSlice(buf)); - } -} -exports.BufferWriter = BufferWriter; diff --git a/src/bufferutils.js b/src/bufferutils.js index 54ce1c9..19e8763 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -1,5 +1,8 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +const types = require('./types'); +const typeforce = require('typeforce'); +const varuint = require('varuint-bitcoin'); // https://github.com/feross/buffer/blob/master/index.js#L1127 function verifuint(value, max) { if (typeof value !== 'number') @@ -38,3 +41,90 @@ function reverseBuffer(buffer) { return buffer; } exports.reverseBuffer = reverseBuffer; +/** + * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + */ +class BufferWriter { + constructor(buffer, offset = 0) { + this.buffer = buffer; + this.offset = offset; + typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); + } + writeUInt8(i) { + this.offset = this.buffer.writeUInt8(i, this.offset); + } + writeInt32(i) { + this.offset = this.buffer.writeInt32LE(i, this.offset); + } + writeUInt32(i) { + this.offset = this.buffer.writeUInt32LE(i, this.offset); + } + writeUInt64(i) { + this.offset = writeUInt64LE(this.buffer, i, this.offset); + } + writeVarInt(i) { + varuint.encode(i, this.buffer, this.offset); + this.offset += varuint.encode.bytes; + } + writeSlice(slice) { + this.offset += slice.copy(this.buffer, this.offset); + } + writeVarSlice(slice) { + this.writeVarInt(slice.length); + this.writeSlice(slice); + } + writeVector(vector) { + this.writeVarInt(vector.length); + vector.forEach(buf => this.writeVarSlice(buf)); + } +} +exports.BufferWriter = BufferWriter; +/** + * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + */ +class BufferReader { + constructor(buffer, offset = 0) { + this.buffer = buffer; + this.offset = offset; + typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); + } + readUInt8() { + const result = this.buffer.readUInt8(this.offset); + this.offset++; + return result; + } + readInt32() { + const result = this.buffer.readInt32LE(this.offset); + this.offset += 4; + return result; + } + readUInt32() { + const result = this.buffer.readUInt32LE(this.offset); + this.offset += 4; + return result; + } + readUInt64() { + const result = readUInt64LE(this.buffer, this.offset); + this.offset += 8; + return result; + } + readVarInt() { + const vi = varuint.decode(this.buffer, this.offset); + this.offset += varuint.decode.bytes; + return vi; + } + readSlice(n) { + this.offset += n; + return this.buffer.slice(this.offset - n, this.offset); + } + readVarSlice() { + return this.readSlice(this.readVarInt()); + } + readVector() { + const count = this.readVarInt(); + const vector = []; + for (let i = 0; i < count; i++) vector.push(this.readVarSlice()); + return vector; + } +} +exports.BufferReader = BufferReader; diff --git a/src/transaction.js b/src/transaction.js index fc36a63..ef97900 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,6 +1,5 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); -const buffer_writer_1 = require('./buffer_writer'); const bufferutils = require('./bufferutils'); const bufferutils_1 = require('./bufferutils'); const bcrypto = require('./crypto'); @@ -303,7 +302,7 @@ class Transaction { let hashSequence = ZERO; if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { tbuffer = Buffer.allocUnsafe(36 * this.ins.length); - bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); + bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0); this.ins.forEach(txIn => { bufferWriter.writeSlice(txIn.hash); bufferWriter.writeUInt32(txIn.index); @@ -316,7 +315,7 @@ class Transaction { (hashType & 0x1f) !== Transaction.SIGHASH_NONE ) { tbuffer = Buffer.allocUnsafe(4 * this.ins.length); - bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); + bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0); this.ins.forEach(txIn => { bufferWriter.writeUInt32(txIn.sequence); }); @@ -330,7 +329,7 @@ class Transaction { return sum + 8 + varSliceSize(output.script); }, 0); tbuffer = Buffer.allocUnsafe(txOutsSize); - bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); + bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0); this.outs.forEach(out => { bufferWriter.writeUInt64(out.value); bufferWriter.writeVarSlice(out.script); @@ -342,13 +341,13 @@ class Transaction { ) { const output = this.outs[inIndex]; tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)); - bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); + bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0); bufferWriter.writeUInt64(output.value); bufferWriter.writeVarSlice(output.script); hashOutputs = bcrypto.hash256(tbuffer); } tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)); - bufferWriter = new buffer_writer_1.BufferWriter(tbuffer, 0); + bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0); const input = this.ins[inIndex]; bufferWriter.writeUInt32(this.version); bufferWriter.writeSlice(hashPrevouts); @@ -388,7 +387,7 @@ class Transaction { } __toBuffer(buffer, initialOffset, _ALLOW_WITNESS = false) { if (!buffer) buffer = Buffer.allocUnsafe(this.byteLength(_ALLOW_WITNESS)); - const bufferWriter = new buffer_writer_1.BufferWriter( + const bufferWriter = new bufferutils_1.BufferWriter( buffer, initialOffset || 0, ); diff --git a/ts_src/buffer_writer.ts b/ts_src/buffer_writer.ts deleted file mode 100644 index 541457d..0000000 --- a/ts_src/buffer_writer.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as bufferutils from './bufferutils'; -import * as types from './types'; - -const typeforce = require('typeforce'); -const varuint = require('varuint-bitcoin'); - -/** - * Helper class for serialization of bitcoin data types into a pre-allocated buffer. - */ -export class BufferWriter { - buffer: Buffer; - offset: number; - - constructor(buffer: Buffer, offset: number = 0) { - typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); - this.buffer = buffer; - this.offset = offset; - } - - writeUInt8(i: number): void { - this.offset = this.buffer.writeUInt8(i, this.offset); - } - - writeInt32(i: number): void { - this.offset = this.buffer.writeInt32LE(i, this.offset); - } - - writeUInt32(i: number): void { - this.offset = this.buffer.writeUInt32LE(i, this.offset); - } - - writeUInt64(i: number): void { - this.offset = bufferutils.writeUInt64LE(this.buffer, i, this.offset); - } - - writeVarInt(i: number): void { - varuint.encode(i, this.buffer, this.offset); - this.offset += varuint.encode.bytes; - } - - writeSlice(slice: Buffer): void { - this.offset += slice.copy(this.buffer, this.offset); - } - - writeVarSlice(slice: Buffer): void { - this.writeVarInt(slice.length); - this.writeSlice(slice); - } - - writeVector(vector: Buffer[]): void { - this.writeVarInt(vector.length); - vector.forEach((buf: Buffer) => this.writeVarSlice(buf)); - } -} diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index adb6060..6904b73 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -1,3 +1,8 @@ +import * as types from './types'; + +const typeforce = require('typeforce'); +const varuint = require('varuint-bitcoin'); + // https://github.com/feross/buffer/blob/master/index.js#L1127 function verifuint(value: number, max: number): void { if (typeof value !== 'number') @@ -42,3 +47,102 @@ export function reverseBuffer(buffer: Buffer): Buffer { } return buffer; } + +/** + * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + */ +export class BufferWriter { + constructor(private buffer: Buffer, public offset: number = 0) { + typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); + } + + writeUInt8(i: number): void { + this.offset = this.buffer.writeUInt8(i, this.offset); + } + + writeInt32(i: number): void { + this.offset = this.buffer.writeInt32LE(i, this.offset); + } + + writeUInt32(i: number): void { + this.offset = this.buffer.writeUInt32LE(i, this.offset); + } + + writeUInt64(i: number): void { + this.offset = writeUInt64LE(this.buffer, i, this.offset); + } + + writeVarInt(i: number): void { + varuint.encode(i, this.buffer, this.offset); + this.offset += varuint.encode.bytes; + } + + writeSlice(slice: Buffer): void { + this.offset += slice.copy(this.buffer, this.offset); + } + + writeVarSlice(slice: Buffer): void { + this.writeVarInt(slice.length); + this.writeSlice(slice); + } + + writeVector(vector: Buffer[]): void { + this.writeVarInt(vector.length); + vector.forEach((buf: Buffer) => this.writeVarSlice(buf)); + } +} + +/** + * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + */ +export class BufferReader { + constructor(private buffer: Buffer, public offset: number = 0) { + typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); + } + + readUInt8(): number { + const result = this.buffer.readUInt8(this.offset); + this.offset++; + return result; + } + + readInt32(): number { + const result = this.buffer.readInt32LE(this.offset); + this.offset += 4; + return result; + } + + readUInt32(): number { + const result = this.buffer.readUInt32LE(this.offset); + this.offset += 4; + return result; + } + + readUInt64(): number { + const result = readUInt64LE(this.buffer, this.offset); + this.offset += 8; + return result; + } + + readVarInt(): number { + const vi = varuint.decode(this.buffer, this.offset); + this.offset += varuint.decode.bytes; + return vi; + } + + readSlice(n: number): Buffer { + this.offset += n; + return this.buffer.slice(this.offset - n, this.offset); + } + + readVarSlice(): Buffer { + return this.readSlice(this.readVarInt()); + } + + readVector(): Buffer[] { + const count = this.readVarInt(); + const vector: Buffer[] = []; + for (let i = 0; i < count; i++) vector.push(this.readVarSlice()); + return vector; + } +} diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index ac5aea4..3b792ec 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -1,6 +1,5 @@ -import { BufferWriter } from './buffer_writer'; import * as bufferutils from './bufferutils'; -import { reverseBuffer } from './bufferutils'; +import { BufferWriter, reverseBuffer } from './bufferutils'; import * as bcrypto from './crypto'; import * as bscript from './script'; import { OPS as opcodes } from './script'; diff --git a/types/buffer_writer.d.ts b/types/buffer_writer.d.ts deleted file mode 100644 index 5ff07f9..0000000 --- a/types/buffer_writer.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Helper class for serialization of bitcoin data types into a pre-allocated buffer. - */ -export declare class BufferWriter { - buffer: Buffer; - offset: number; - constructor(buffer: Buffer, offset?: number); - writeUInt8(i: number): void; - writeInt32(i: number): void; - writeUInt32(i: number): void; - writeUInt64(i: number): void; - writeVarInt(i: number): void; - writeSlice(slice: Buffer): void; - writeVarSlice(slice: Buffer): void; - writeVector(vector: Buffer[]): void; -} diff --git a/types/bufferutils.d.ts b/types/bufferutils.d.ts index 1eff78d..d9e6dc0 100644 --- a/types/bufferutils.d.ts +++ b/types/bufferutils.d.ts @@ -1,3 +1,35 @@ export declare function readUInt64LE(buffer: Buffer, offset: number): number; export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number; export declare function reverseBuffer(buffer: Buffer): Buffer; +/** + * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + */ +export declare class BufferWriter { + private buffer; + offset: number; + constructor(buffer: Buffer, offset?: number); + writeUInt8(i: number): void; + writeInt32(i: number): void; + writeUInt32(i: number): void; + writeUInt64(i: number): void; + writeVarInt(i: number): void; + writeSlice(slice: Buffer): void; + writeVarSlice(slice: Buffer): void; + writeVector(vector: Buffer[]): void; +} +/** + * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + */ +export declare class BufferReader { + private buffer; + offset: number; + constructor(buffer: Buffer, offset?: number); + readUInt8(): number; + readInt32(): number; + readUInt32(): number; + readUInt64(): number; + readVarInt(): number; + readSlice(n: number): Buffer; + readVarSlice(): Buffer; + readVector(): Buffer[]; +} From 06674b19fe9e146fa547d94b9ef67bd2425ad71b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 15 Jan 2020 10:50:33 +0900 Subject: [PATCH 481/568] Add BufferReader to Transaction and fix tests --- src/transaction.js | 67 +++++++++------------------------ test/buffer_writer.spec.ts | 2 +- ts_src/bufferutils.ts | 4 +- ts_src/transaction.ts | 76 +++++++++----------------------------- types/bufferutils.d.ts | 4 +- 5 files changed, 40 insertions(+), 113 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index ef97900..b47cfe9 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,6 +1,5 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); -const bufferutils = require('./bufferutils'); const bufferutils_1 = require('./bufferutils'); const bcrypto = require('./crypto'); const bscript = require('./script'); @@ -47,80 +46,48 @@ class Transaction { this.outs = []; } static fromBuffer(buffer, _NO_STRICT) { - let offset = 0; - function readSlice(n) { - offset += n; - return buffer.slice(offset - n, offset); - } - function readUInt32() { - const i = buffer.readUInt32LE(offset); - offset += 4; - return i; - } - function readInt32() { - const i = buffer.readInt32LE(offset); - offset += 4; - return i; - } - function readUInt64() { - const i = bufferutils.readUInt64LE(buffer, offset); - offset += 8; - return i; - } - function readVarInt() { - const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; - return vi; - } - function readVarSlice() { - return readSlice(readVarInt()); - } - function readVector() { - const count = readVarInt(); - const vector = []; - for (let i = 0; i < count; i++) vector.push(readVarSlice()); - return vector; - } + const bufferReader = new bufferutils_1.BufferReader(buffer); const tx = new Transaction(); - tx.version = readInt32(); - const marker = buffer.readUInt8(offset); - const flag = buffer.readUInt8(offset + 1); + tx.version = bufferReader.readInt32(); + const marker = bufferReader.readUInt8(); + const flag = bufferReader.readUInt8(); let hasWitnesses = false; if ( marker === Transaction.ADVANCED_TRANSACTION_MARKER && flag === Transaction.ADVANCED_TRANSACTION_FLAG ) { - offset += 2; hasWitnesses = true; + } else { + bufferReader.offset -= 2; } - const vinLen = readVarInt(); + const vinLen = bufferReader.readVarInt(); for (let i = 0; i < vinLen; ++i) { tx.ins.push({ - hash: readSlice(32), - index: readUInt32(), - script: readVarSlice(), - sequence: readUInt32(), + hash: bufferReader.readSlice(32), + index: bufferReader.readUInt32(), + script: bufferReader.readVarSlice(), + sequence: bufferReader.readUInt32(), witness: EMPTY_WITNESS, }); } - const voutLen = readVarInt(); + const voutLen = bufferReader.readVarInt(); for (let i = 0; i < voutLen; ++i) { tx.outs.push({ - value: readUInt64(), - script: readVarSlice(), + value: bufferReader.readUInt64(), + script: bufferReader.readVarSlice(), }); } if (hasWitnesses) { for (let i = 0; i < vinLen; ++i) { - tx.ins[i].witness = readVector(); + tx.ins[i].witness = bufferReader.readVector(); } // was this pointless? if (!tx.hasWitnesses()) throw new Error('Transaction has superfluous witness data'); } - tx.locktime = readUInt32(); + tx.locktime = bufferReader.readUInt32(); if (_NO_STRICT) return tx; - if (offset !== buffer.length) + if (bufferReader.offset !== buffer.length) throw new Error('Transaction has unexpected data'); return tx; } diff --git a/test/buffer_writer.spec.ts b/test/buffer_writer.spec.ts index 8b33e2e..af35cba 100644 --- a/test/buffer_writer.spec.ts +++ b/test/buffer_writer.spec.ts @@ -1,6 +1,6 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; -import { BufferWriter } from '../src/buffer_writer'; +import { BufferWriter } from '../src/bufferutils'; const varuint = require('varuint-bitcoin'); diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 6904b73..8a5c839 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -52,7 +52,7 @@ export function reverseBuffer(buffer: Buffer): Buffer { * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ export class BufferWriter { - constructor(private buffer: Buffer, public offset: number = 0) { + constructor(public buffer: Buffer, public offset: number = 0) { typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); } @@ -96,7 +96,7 @@ export class BufferWriter { * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ export class BufferReader { - constructor(private buffer: Buffer, public offset: number = 0) { + constructor(public buffer: Buffer, public offset: number = 0) { typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); } diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index 3b792ec..561ee8a 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -1,5 +1,4 @@ -import * as bufferutils from './bufferutils'; -import { BufferWriter, reverseBuffer } from './bufferutils'; +import { BufferReader, BufferWriter, reverseBuffer } from './bufferutils'; import * as bcrypto from './crypto'; import * as bscript from './script'; import { OPS as opcodes } from './script'; @@ -68,85 +67,46 @@ export class Transaction { static readonly ADVANCED_TRANSACTION_FLAG = 0x01; static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction { - let offset: number = 0; - - function readSlice(n: number): Buffer { - offset += n; - return buffer.slice(offset - n, offset); - } - - function readUInt32(): number { - const i = buffer.readUInt32LE(offset); - offset += 4; - return i; - } - - function readInt32(): number { - const i = buffer.readInt32LE(offset); - offset += 4; - return i; - } - - function readUInt64(): number { - const i = bufferutils.readUInt64LE(buffer, offset); - offset += 8; - return i; - } - - function readVarInt(): number { - const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; - return vi; - } - - function readVarSlice(): Buffer { - return readSlice(readVarInt()); - } - - function readVector(): Buffer[] { - const count = readVarInt(); - const vector: Buffer[] = []; - for (let i = 0; i < count; i++) vector.push(readVarSlice()); - return vector; - } + const bufferReader = new BufferReader(buffer); const tx = new Transaction(); - tx.version = readInt32(); + tx.version = bufferReader.readInt32(); - const marker = buffer.readUInt8(offset); - const flag = buffer.readUInt8(offset + 1); + const marker = bufferReader.readUInt8(); + const flag = bufferReader.readUInt8(); let hasWitnesses = false; if ( marker === Transaction.ADVANCED_TRANSACTION_MARKER && flag === Transaction.ADVANCED_TRANSACTION_FLAG ) { - offset += 2; hasWitnesses = true; + } else { + bufferReader.offset -= 2; } - const vinLen = readVarInt(); + const vinLen = bufferReader.readVarInt(); for (let i = 0; i < vinLen; ++i) { tx.ins.push({ - hash: readSlice(32), - index: readUInt32(), - script: readVarSlice(), - sequence: readUInt32(), + hash: bufferReader.readSlice(32), + index: bufferReader.readUInt32(), + script: bufferReader.readVarSlice(), + sequence: bufferReader.readUInt32(), witness: EMPTY_WITNESS, }); } - const voutLen = readVarInt(); + const voutLen = bufferReader.readVarInt(); for (let i = 0; i < voutLen; ++i) { tx.outs.push({ - value: readUInt64(), - script: readVarSlice(), + value: bufferReader.readUInt64(), + script: bufferReader.readVarSlice(), }); } if (hasWitnesses) { for (let i = 0; i < vinLen; ++i) { - tx.ins[i].witness = readVector(); + tx.ins[i].witness = bufferReader.readVector(); } // was this pointless? @@ -154,10 +114,10 @@ export class Transaction { throw new Error('Transaction has superfluous witness data'); } - tx.locktime = readUInt32(); + tx.locktime = bufferReader.readUInt32(); if (_NO_STRICT) return tx; - if (offset !== buffer.length) + if (bufferReader.offset !== buffer.length) throw new Error('Transaction has unexpected data'); return tx; diff --git a/types/bufferutils.d.ts b/types/bufferutils.d.ts index d9e6dc0..424bbaf 100644 --- a/types/bufferutils.d.ts +++ b/types/bufferutils.d.ts @@ -5,7 +5,7 @@ export declare function reverseBuffer(buffer: Buffer): Buffer; * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ export declare class BufferWriter { - private buffer; + buffer: Buffer; offset: number; constructor(buffer: Buffer, offset?: number); writeUInt8(i: number): void; @@ -21,7 +21,7 @@ export declare class BufferWriter { * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ export declare class BufferReader { - private buffer; + buffer: Buffer; offset: number; constructor(buffer: Buffer, offset?: number); readUInt8(): number; From c8fdfae9558b208ef68da358e196f660bd89a46b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 15 Jan 2020 11:25:53 +0900 Subject: [PATCH 482/568] Add BufferReader tests --- src/block.js | 73 ++---- test/buffer_writer.spec.ts | 231 ------------------- test/bufferutils.spec.ts | 440 +++++++++++++++++++++++++++++++++++++ ts_src/block.ts | 82 +++---- 4 files changed, 486 insertions(+), 340 deletions(-) delete mode 100644 test/buffer_writer.spec.ts diff --git a/src/block.js b/src/block.js index 8ec6c29..cb3ee4b 100644 --- a/src/block.js +++ b/src/block.js @@ -26,43 +26,24 @@ class Block { } static fromBuffer(buffer) { if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); - let offset = 0; - const readSlice = n => { - offset += n; - return buffer.slice(offset - n, offset); - }; - const readUInt32 = () => { - const i = buffer.readUInt32LE(offset); - offset += 4; - return i; - }; - const readInt32 = () => { - const i = buffer.readInt32LE(offset); - offset += 4; - return i; - }; + const bufferReader = new bufferutils_1.BufferReader(buffer); const block = new Block(); - block.version = readInt32(); - block.prevHash = readSlice(32); - block.merkleRoot = readSlice(32); - block.timestamp = readUInt32(); - block.bits = readUInt32(); - block.nonce = readUInt32(); + block.version = bufferReader.readInt32(); + block.prevHash = bufferReader.readSlice(32); + block.merkleRoot = bufferReader.readSlice(32); + block.timestamp = bufferReader.readUInt32(); + block.bits = bufferReader.readUInt32(); + block.nonce = bufferReader.readUInt32(); if (buffer.length === 80) return block; - const readVarInt = () => { - const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; - return vi; - }; const readTransaction = () => { const tx = transaction_1.Transaction.fromBuffer( - buffer.slice(offset), + bufferReader.buffer.slice(bufferReader.offset), true, ); - offset += tx.byteLength(); + bufferReader.offset += tx.byteLength(); return tx; }; - const nTransactions = readVarInt(); + const nTransactions = bufferReader.readVarInt(); block.transactions = []; for (let i = 0; i < nTransactions; ++i) { const tx = readTransaction(); @@ -154,32 +135,20 @@ class Block { // TODO: buffer, offset compatibility toBuffer(headersOnly) { const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); - let offset = 0; - const writeSlice = slice => { - slice.copy(buffer, offset); - offset += slice.length; - }; - const writeInt32 = i => { - buffer.writeInt32LE(i, offset); - offset += 4; - }; - const writeUInt32 = i => { - buffer.writeUInt32LE(i, offset); - offset += 4; - }; - writeInt32(this.version); - writeSlice(this.prevHash); - writeSlice(this.merkleRoot); - writeUInt32(this.timestamp); - writeUInt32(this.bits); - writeUInt32(this.nonce); + const bufferWriter = new bufferutils_1.BufferWriter(buffer); + bufferWriter.writeInt32(this.version); + bufferWriter.writeSlice(this.prevHash); + bufferWriter.writeSlice(this.merkleRoot); + bufferWriter.writeUInt32(this.timestamp); + bufferWriter.writeUInt32(this.bits); + bufferWriter.writeUInt32(this.nonce); if (headersOnly || !this.transactions) return buffer; - varuint.encode(this.transactions.length, buffer, offset); - offset += varuint.encode.bytes; + varuint.encode(this.transactions.length, buffer, bufferWriter.offset); + bufferWriter.offset += varuint.encode.bytes; this.transactions.forEach(tx => { const txSize = tx.byteLength(); // TODO: extract from toBuffer? - tx.toBuffer(buffer, offset); - offset += txSize; + tx.toBuffer(buffer, bufferWriter.offset); + bufferWriter.offset += txSize; }); return buffer; } diff --git a/test/buffer_writer.spec.ts b/test/buffer_writer.spec.ts deleted file mode 100644 index af35cba..0000000 --- a/test/buffer_writer.spec.ts +++ /dev/null @@ -1,231 +0,0 @@ -import * as assert from 'assert'; -import { describe, it } from 'mocha'; -import { BufferWriter } from '../src/bufferutils'; - -const varuint = require('varuint-bitcoin'); - -describe('BufferWriter', () => { - function concatToBuffer(values: number[][]): Buffer { - return Buffer.concat(values.map(data => Buffer.from(data))); - } - - function testBuffer( - bufferWriter: BufferWriter, - expectedBuffer: Buffer, - expectedOffset: number = expectedBuffer.length, - ): void { - assert.strictEqual(bufferWriter.offset, expectedOffset); - assert.deepStrictEqual( - bufferWriter.buffer.slice(0, expectedOffset), - expectedBuffer.slice(0, expectedOffset), - ); - } - - it('writeUint8', () => { - const values = [0, 1, 254, 255]; - const expectedBuffer = Buffer.from([0, 1, 0xfe, 0xff]); - const bufferWriter = new BufferWriter( - Buffer.allocUnsafe(expectedBuffer.length), - ); - values.forEach((v: number) => { - const expectedOffset = bufferWriter.offset + 1; - bufferWriter.writeUInt8(v); - testBuffer(bufferWriter, expectedBuffer, expectedOffset); - }); - testBuffer(bufferWriter, expectedBuffer); - }); - - it('writeInt32', () => { - const values = [ - 0, - 1, - Math.pow(2, 31) - 2, - Math.pow(2, 31) - 1, - -1, - -Math.pow(2, 31), - ]; - const expectedBuffer = concatToBuffer([ - [0, 0, 0, 0], - [1, 0, 0, 0], - [0xfe, 0xff, 0xff, 0x7f], - [0xff, 0xff, 0xff, 0x7f], - [0xff, 0xff, 0xff, 0xff], - [0x00, 0x00, 0x00, 0x80], - ]); - const bufferWriter = new BufferWriter( - Buffer.allocUnsafe(expectedBuffer.length), - ); - values.forEach((value: number) => { - const expectedOffset = bufferWriter.offset + 4; - bufferWriter.writeInt32(value); - testBuffer(bufferWriter, expectedBuffer, expectedOffset); - }); - testBuffer(bufferWriter, expectedBuffer); - }); - - it('writeUInt32', () => { - const maxUInt32 = Math.pow(2, 32) - 1; - const values = [0, 1, Math.pow(2, 16), maxUInt32]; - const expectedBuffer = concatToBuffer([ - [0, 0, 0, 0], - [1, 0, 0, 0], - [0, 0, 1, 0], - [0xff, 0xff, 0xff, 0xff], - ]); - const bufferWriter = new BufferWriter( - Buffer.allocUnsafe(expectedBuffer.length), - ); - values.forEach((value: number) => { - const expectedOffset = bufferWriter.offset + 4; - bufferWriter.writeUInt32(value); - testBuffer(bufferWriter, expectedBuffer, expectedOffset); - }); - testBuffer(bufferWriter, expectedBuffer); - }); - - it('writeUInt64', () => { - const values = [ - 0, - 1, - Math.pow(2, 32), - Number.MAX_SAFE_INTEGER /* 2^53 - 1 */, - ]; - const expectedBuffer = concatToBuffer([ - [0, 0, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], - [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00], - ]); - const bufferWriter = new BufferWriter( - Buffer.allocUnsafe(expectedBuffer.length), - ); - values.forEach((value: number) => { - const expectedOffset = bufferWriter.offset + 8; - bufferWriter.writeUInt64(value); - testBuffer(bufferWriter, expectedBuffer, expectedOffset); - }); - testBuffer(bufferWriter, expectedBuffer); - }); - - it('writeVarInt', () => { - const values = [ - 0, - 1, - 252, - 253, - 254, - 255, - 256, - Math.pow(2, 16) - 2, - Math.pow(2, 16) - 1, - Math.pow(2, 16), - Math.pow(2, 32) - 2, - Math.pow(2, 32) - 1, - Math.pow(2, 32), - Number.MAX_SAFE_INTEGER, - ]; - const expectedBuffer = concatToBuffer([ - [0x00], - [0x01], - [0xfc], - [0xfd, 0xfd, 0x00], - [0xfd, 0xfe, 0x00], - [0xfd, 0xff, 0x00], - [0xfd, 0x00, 0x01], - [0xfd, 0xfe, 0xff], - [0xfd, 0xff, 0xff], - [0xfe, 0x00, 0x00, 0x01, 0x00], - [0xfe, 0xfe, 0xff, 0xff, 0xff], - [0xfe, 0xff, 0xff, 0xff, 0xff], - [0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00], - [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00], - ]); - const bufferWriter = new BufferWriter( - Buffer.allocUnsafe(expectedBuffer.length), - ); - values.forEach((value: number) => { - const expectedOffset = - bufferWriter.offset + varuint.encodingLength(value); - bufferWriter.writeVarInt(value); - testBuffer(bufferWriter, expectedBuffer, expectedOffset); - }); - testBuffer(bufferWriter, expectedBuffer); - }); - - it('writeSlice', () => { - const values = [[], [1], [1, 2, 3, 4], [254, 255]]; - const expectedBuffer = concatToBuffer(values); - const bufferWriter = new BufferWriter( - Buffer.allocUnsafe(expectedBuffer.length), - ); - values.forEach((v: number[]) => { - const expectedOffset = bufferWriter.offset + v.length; - bufferWriter.writeSlice(Buffer.from(v)); - testBuffer(bufferWriter, expectedBuffer, expectedOffset); - }); - testBuffer(bufferWriter, expectedBuffer); - }); - - it('writeVarSlice', () => { - const values = [ - Buffer.alloc(1, 1), - Buffer.alloc(252, 2), - Buffer.alloc(253, 3), - ]; - const expectedBuffer = Buffer.concat([ - Buffer.from([0x01, 0x01]), - Buffer.from([0xfc]), - Buffer.alloc(252, 0x02), - Buffer.from([0xfd, 0xfd, 0x00]), - Buffer.alloc(253, 0x03), - ]); - const bufferWriter = new BufferWriter( - Buffer.allocUnsafe(expectedBuffer.length), - ); - values.forEach((value: Buffer) => { - const expectedOffset = - bufferWriter.offset + - varuint.encodingLength(value.length) + - value.length; - bufferWriter.writeVarSlice(value); - testBuffer(bufferWriter, expectedBuffer, expectedOffset); - }); - testBuffer(bufferWriter, expectedBuffer); - }); - - it('writeVector', () => { - const values = [ - [Buffer.alloc(1, 4), Buffer.alloc(253, 5)], - Array(253).fill(Buffer.alloc(1, 6)), - ]; - const expectedBuffer = Buffer.concat([ - Buffer.from([0x02]), - Buffer.from([0x01, 0x04]), - Buffer.from([0xfd, 0xfd, 0x00]), - Buffer.alloc(253, 5), - - Buffer.from([0xfd, 0xfd, 0x00]), - Buffer.concat( - Array(253) - .fill(0) - .map(() => Buffer.from([0x01, 0x06])), - ), - ]); - - const bufferWriter = new BufferWriter( - Buffer.allocUnsafe(expectedBuffer.length), - ); - values.forEach((value: Buffer[]) => { - const expectedOffset = - bufferWriter.offset + - varuint.encodingLength(value.length) + - value.reduce( - (sum: number, v) => sum + varuint.encodingLength(v.length) + v.length, - 0, - ); - bufferWriter.writeVector(value); - testBuffer(bufferWriter, expectedBuffer, expectedOffset); - }); - testBuffer(bufferWriter, expectedBuffer); - }); -}); diff --git a/test/bufferutils.spec.ts b/test/bufferutils.spec.ts index 33ad8f5..2ed80e6 100644 --- a/test/bufferutils.spec.ts +++ b/test/bufferutils.spec.ts @@ -1,10 +1,16 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; import * as bufferutils from '../src/bufferutils'; +import { BufferReader, BufferWriter } from '../src/bufferutils'; import * as fixtures from './fixtures/bufferutils.json'; +const varuint = require('varuint-bitcoin'); describe('bufferutils', () => { + function concatToBuffer(values: number[][]): Buffer { + return Buffer.concat(values.map(data => Buffer.from(data))); + } + describe('readUInt64LE', () => { fixtures.valid.forEach(f => { it('decodes ' + f.hex, () => { @@ -46,4 +52,438 @@ describe('bufferutils', () => { }); }); }); + + describe('BufferWriter', () => { + function testBuffer( + bufferWriter: BufferWriter, + expectedBuffer: Buffer, + expectedOffset: number = expectedBuffer.length, + ): void { + assert.strictEqual(bufferWriter.offset, expectedOffset); + assert.deepStrictEqual( + bufferWriter.buffer.slice(0, expectedOffset), + expectedBuffer.slice(0, expectedOffset), + ); + } + + it('writeUint8', () => { + const values = [0, 1, 254, 255]; + const expectedBuffer = Buffer.from([0, 1, 0xfe, 0xff]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((v: number) => { + const expectedOffset = bufferWriter.offset + 1; + bufferWriter.writeUInt8(v); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeInt32', () => { + const values = [ + 0, + 1, + Math.pow(2, 31) - 2, + Math.pow(2, 31) - 1, + -1, + -Math.pow(2, 31), + ]; + const expectedBuffer = concatToBuffer([ + [0, 0, 0, 0], + [1, 0, 0, 0], + [0xfe, 0xff, 0xff, 0x7f], + [0xff, 0xff, 0xff, 0x7f], + [0xff, 0xff, 0xff, 0xff], + [0x00, 0x00, 0x00, 0x80], + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: number) => { + const expectedOffset = bufferWriter.offset + 4; + bufferWriter.writeInt32(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeUInt32', () => { + const maxUInt32 = Math.pow(2, 32) - 1; + const values = [0, 1, Math.pow(2, 16), maxUInt32]; + const expectedBuffer = concatToBuffer([ + [0, 0, 0, 0], + [1, 0, 0, 0], + [0, 0, 1, 0], + [0xff, 0xff, 0xff, 0xff], + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: number) => { + const expectedOffset = bufferWriter.offset + 4; + bufferWriter.writeUInt32(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeUInt64', () => { + const values = [ + 0, + 1, + Math.pow(2, 32), + Number.MAX_SAFE_INTEGER /* 2^53 - 1 */, + ]; + const expectedBuffer = concatToBuffer([ + [0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00], + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: number) => { + const expectedOffset = bufferWriter.offset + 8; + bufferWriter.writeUInt64(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeVarInt', () => { + const values = [ + 0, + 1, + 252, + 253, + 254, + 255, + 256, + Math.pow(2, 16) - 2, + Math.pow(2, 16) - 1, + Math.pow(2, 16), + Math.pow(2, 32) - 2, + Math.pow(2, 32) - 1, + Math.pow(2, 32), + Number.MAX_SAFE_INTEGER, + ]; + const expectedBuffer = concatToBuffer([ + [0x00], + [0x01], + [0xfc], + [0xfd, 0xfd, 0x00], + [0xfd, 0xfe, 0x00], + [0xfd, 0xff, 0x00], + [0xfd, 0x00, 0x01], + [0xfd, 0xfe, 0xff], + [0xfd, 0xff, 0xff], + [0xfe, 0x00, 0x00, 0x01, 0x00], + [0xfe, 0xfe, 0xff, 0xff, 0xff], + [0xfe, 0xff, 0xff, 0xff, 0xff], + [0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00], + [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00], + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: number) => { + const expectedOffset = + bufferWriter.offset + varuint.encodingLength(value); + bufferWriter.writeVarInt(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeSlice', () => { + const values = [[], [1], [1, 2, 3, 4], [254, 255]]; + const expectedBuffer = concatToBuffer(values); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((v: number[]) => { + const expectedOffset = bufferWriter.offset + v.length; + bufferWriter.writeSlice(Buffer.from(v)); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeVarSlice', () => { + const values = [ + Buffer.alloc(1, 1), + Buffer.alloc(252, 2), + Buffer.alloc(253, 3), + ]; + const expectedBuffer = Buffer.concat([ + Buffer.from([0x01, 0x01]), + Buffer.from([0xfc]), + Buffer.alloc(252, 0x02), + Buffer.from([0xfd, 0xfd, 0x00]), + Buffer.alloc(253, 0x03), + ]); + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: Buffer) => { + const expectedOffset = + bufferWriter.offset + + varuint.encodingLength(value.length) + + value.length; + bufferWriter.writeVarSlice(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + + it('writeVector', () => { + const values = [ + [Buffer.alloc(1, 4), Buffer.alloc(253, 5)], + Array(253).fill(Buffer.alloc(1, 6)), + ]; + const expectedBuffer = Buffer.concat([ + Buffer.from([0x02]), + Buffer.from([0x01, 0x04]), + Buffer.from([0xfd, 0xfd, 0x00]), + Buffer.alloc(253, 5), + + Buffer.from([0xfd, 0xfd, 0x00]), + Buffer.concat( + Array(253) + .fill(0) + .map(() => Buffer.from([0x01, 0x06])), + ), + ]); + + const bufferWriter = new BufferWriter( + Buffer.allocUnsafe(expectedBuffer.length), + ); + values.forEach((value: Buffer[]) => { + const expectedOffset = + bufferWriter.offset + + varuint.encodingLength(value.length) + + value.reduce( + (sum: number, v) => + sum + varuint.encodingLength(v.length) + v.length, + 0, + ); + bufferWriter.writeVector(value); + testBuffer(bufferWriter, expectedBuffer, expectedOffset); + }); + testBuffer(bufferWriter, expectedBuffer); + }); + }); + + describe('BufferReader', () => { + function testValue( + bufferReader: BufferReader, + value: Buffer | number, + expectedValue: Buffer | number, + expectedOffset: number = Buffer.isBuffer(expectedValue) + ? expectedValue.length + : 0, + ): void { + assert.strictEqual(bufferReader.offset, expectedOffset); + if (Buffer.isBuffer(expectedValue)) { + assert.deepStrictEqual( + (value as Buffer).slice(0, expectedOffset), + expectedValue.slice(0, expectedOffset), + ); + } else { + assert.strictEqual(value as number, expectedValue); + } + } + + it('readUint8', () => { + const values = [0, 1, 0xfe, 0xff]; + const buffer = Buffer.from([0, 1, 0xfe, 0xff]); + const bufferReader = new BufferReader(buffer); + values.forEach((v: number) => { + const expectedOffset = bufferReader.offset + 1; + const val = bufferReader.readUInt8(); + testValue(bufferReader, val, v, expectedOffset); + }); + }); + + it('readInt32', () => { + const values = [ + 0, + 1, + Math.pow(2, 31) - 2, + Math.pow(2, 31) - 1, + -1, + -Math.pow(2, 31), + ]; + const buffer = concatToBuffer([ + [0, 0, 0, 0], + [1, 0, 0, 0], + [0xfe, 0xff, 0xff, 0x7f], + [0xff, 0xff, 0xff, 0x7f], + [0xff, 0xff, 0xff, 0xff], + [0x00, 0x00, 0x00, 0x80], + ]); + const bufferReader = new BufferReader(buffer); + values.forEach((value: number) => { + const expectedOffset = bufferReader.offset + 4; + const val = bufferReader.readInt32(); + testValue(bufferReader, val, value, expectedOffset); + }); + }); + + it('readUInt32', () => { + const maxUInt32 = Math.pow(2, 32) - 1; + const values = [0, 1, Math.pow(2, 16), maxUInt32]; + const buffer = concatToBuffer([ + [0, 0, 0, 0], + [1, 0, 0, 0], + [0, 0, 1, 0], + [0xff, 0xff, 0xff, 0xff], + ]); + const bufferReader = new BufferReader(buffer); + values.forEach((value: number) => { + const expectedOffset = bufferReader.offset + 4; + const val = bufferReader.readUInt32(); + testValue(bufferReader, val, value, expectedOffset); + }); + }); + + it('readUInt64', () => { + const values = [ + 0, + 1, + Math.pow(2, 32), + Number.MAX_SAFE_INTEGER /* 2^53 - 1 */, + ]; + const buffer = concatToBuffer([ + [0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00], + ]); + const bufferReader = new BufferReader(buffer); + values.forEach((value: number) => { + const expectedOffset = bufferReader.offset + 8; + const val = bufferReader.readUInt64(); + testValue(bufferReader, val, value, expectedOffset); + }); + }); + + it('readVarInt', () => { + const values = [ + 0, + 1, + 252, + 253, + 254, + 255, + 256, + Math.pow(2, 16) - 2, + Math.pow(2, 16) - 1, + Math.pow(2, 16), + Math.pow(2, 32) - 2, + Math.pow(2, 32) - 1, + Math.pow(2, 32), + Number.MAX_SAFE_INTEGER, + ]; + const buffer = concatToBuffer([ + [0x00], + [0x01], + [0xfc], + [0xfd, 0xfd, 0x00], + [0xfd, 0xfe, 0x00], + [0xfd, 0xff, 0x00], + [0xfd, 0x00, 0x01], + [0xfd, 0xfe, 0xff], + [0xfd, 0xff, 0xff], + [0xfe, 0x00, 0x00, 0x01, 0x00], + [0xfe, 0xfe, 0xff, 0xff, 0xff], + [0xfe, 0xff, 0xff, 0xff, 0xff], + [0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00], + [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00], + ]); + const bufferReader = new BufferReader(buffer); + values.forEach((value: number) => { + const expectedOffset = + bufferReader.offset + varuint.encodingLength(value); + const val = bufferReader.readVarInt(); + testValue(bufferReader, val, value, expectedOffset); + }); + }); + + it('readSlice', () => { + const values = [[1], [1, 2, 3, 4], [254, 255]]; + const buffer = concatToBuffer(values); + const bufferReader = new BufferReader(buffer); + values.forEach((v: number[]) => { + const expectedOffset = bufferReader.offset + v.length; + const val = bufferReader.readSlice(v.length); + testValue(bufferReader, val, Buffer.from(v), expectedOffset); + }); + }); + + it('readVarSlice', () => { + const values = [ + Buffer.alloc(1, 1), + Buffer.alloc(252, 2), + Buffer.alloc(253, 3), + ]; + const buffer = Buffer.concat([ + Buffer.from([0x01, 0x01]), + Buffer.from([0xfc]), + Buffer.alloc(252, 0x02), + Buffer.from([0xfd, 0xfd, 0x00]), + Buffer.alloc(253, 0x03), + ]); + const bufferReader = new BufferReader(buffer); + values.forEach((value: Buffer) => { + const expectedOffset = + bufferReader.offset + + varuint.encodingLength(value.length) + + value.length; + const val = bufferReader.readVarSlice(); + testValue(bufferReader, val, value, expectedOffset); + }); + }); + + it('readVector', () => { + const values = [ + [Buffer.alloc(1, 4), Buffer.alloc(253, 5)], + Array(253).fill(Buffer.alloc(1, 6)), + ]; + const buffer = Buffer.concat([ + Buffer.from([0x02]), + Buffer.from([0x01, 0x04]), + Buffer.from([0xfd, 0xfd, 0x00]), + Buffer.alloc(253, 5), + + Buffer.from([0xfd, 0xfd, 0x00]), + Buffer.concat( + Array(253) + .fill(0) + .map(() => Buffer.from([0x01, 0x06])), + ), + ]); + + const bufferReader = new BufferReader(buffer); + values.forEach((value: Buffer[]) => { + const expectedOffset = + bufferReader.offset + + varuint.encodingLength(value.length) + + value.reduce( + (sum: number, v) => + sum + varuint.encodingLength(v.length) + v.length, + 0, + ); + const val = bufferReader.readVector(); + testValue( + bufferReader, + Buffer.concat(val), + Buffer.concat(value), + expectedOffset, + ); + }); + }); + }); }); diff --git a/ts_src/block.ts b/ts_src/block.ts index 9a4d675..2760e5e 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -1,4 +1,4 @@ -import { reverseBuffer } from './bufferutils'; +import { BufferReader, BufferWriter, reverseBuffer } from './bufferutils'; import * as bcrypto from './crypto'; import { Transaction } from './transaction'; import * as types from './types'; @@ -18,47 +18,28 @@ export class Block { static fromBuffer(buffer: Buffer): Block { if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); - let offset: number = 0; - const readSlice = (n: number): Buffer => { - offset += n; - return buffer.slice(offset - n, offset); - }; - - const readUInt32 = (): number => { - const i = buffer.readUInt32LE(offset); - offset += 4; - return i; - }; - - const readInt32 = (): number => { - const i = buffer.readInt32LE(offset); - offset += 4; - return i; - }; + const bufferReader = new BufferReader(buffer); const block = new Block(); - block.version = readInt32(); - block.prevHash = readSlice(32); - block.merkleRoot = readSlice(32); - block.timestamp = readUInt32(); - block.bits = readUInt32(); - block.nonce = readUInt32(); + block.version = bufferReader.readInt32(); + block.prevHash = bufferReader.readSlice(32); + block.merkleRoot = bufferReader.readSlice(32); + block.timestamp = bufferReader.readUInt32(); + block.bits = bufferReader.readUInt32(); + block.nonce = bufferReader.readUInt32(); if (buffer.length === 80) return block; - const readVarInt = (): number => { - const vi = varuint.decode(buffer, offset); - offset += varuint.decode.bytes; - return vi; - }; - const readTransaction = (): any => { - const tx = Transaction.fromBuffer(buffer.slice(offset), true); - offset += tx.byteLength(); + const tx = Transaction.fromBuffer( + bufferReader.buffer.slice(bufferReader.offset), + true, + ); + bufferReader.offset += tx.byteLength(); return tx; }; - const nTransactions = readVarInt(); + const nTransactions = bufferReader.readVarInt(); block.transactions = []; for (let i = 0; i < nTransactions; ++i) { @@ -183,37 +164,24 @@ export class Block { toBuffer(headersOnly?: boolean): Buffer { const buffer: Buffer = Buffer.allocUnsafe(this.byteLength(headersOnly)); - let offset: number = 0; - const writeSlice = (slice: Buffer): void => { - slice.copy(buffer, offset); - offset += slice.length; - }; + const bufferWriter = new BufferWriter(buffer); - const writeInt32 = (i: number): void => { - buffer.writeInt32LE(i, offset); - offset += 4; - }; - const writeUInt32 = (i: number): void => { - buffer.writeUInt32LE(i, offset); - offset += 4; - }; - - writeInt32(this.version); - writeSlice(this.prevHash!); - writeSlice(this.merkleRoot!); - writeUInt32(this.timestamp); - writeUInt32(this.bits); - writeUInt32(this.nonce); + bufferWriter.writeInt32(this.version); + bufferWriter.writeSlice(this.prevHash!); + bufferWriter.writeSlice(this.merkleRoot!); + bufferWriter.writeUInt32(this.timestamp); + bufferWriter.writeUInt32(this.bits); + bufferWriter.writeUInt32(this.nonce); if (headersOnly || !this.transactions) return buffer; - varuint.encode(this.transactions.length, buffer, offset); - offset += varuint.encode.bytes; + varuint.encode(this.transactions.length, buffer, bufferWriter.offset); + bufferWriter.offset += varuint.encode.bytes; this.transactions.forEach(tx => { const txSize = tx.byteLength(); // TODO: extract from toBuffer? - tx.toBuffer(buffer, offset); - offset += txSize; + tx.toBuffer(buffer, bufferWriter.offset); + bufferWriter.offset += txSize; }); return buffer; From 717166e66833ae16d4575beac7fbe6a8462f6baf Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 15 Jan 2020 11:28:56 +0900 Subject: [PATCH 483/568] Fix comment --- src/bufferutils.js | 2 +- ts_src/bufferutils.ts | 2 +- types/bufferutils.d.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bufferutils.js b/src/bufferutils.js index 19e8763..98adbfb 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -80,7 +80,7 @@ class BufferWriter { } exports.BufferWriter = BufferWriter; /** - * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + * Helper class for reading of bitcoin data types from a buffer. */ class BufferReader { constructor(buffer, offset = 0) { diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 8a5c839..502745d 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -93,7 +93,7 @@ export class BufferWriter { } /** - * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + * Helper class for reading of bitcoin data types from a buffer. */ export class BufferReader { constructor(public buffer: Buffer, public offset: number = 0) { diff --git a/types/bufferutils.d.ts b/types/bufferutils.d.ts index 424bbaf..36e31fe 100644 --- a/types/bufferutils.d.ts +++ b/types/bufferutils.d.ts @@ -18,7 +18,7 @@ export declare class BufferWriter { writeVector(vector: Buffer[]): void; } /** - * Helper class for serialization of bitcoin data types into a pre-allocated buffer. + * Helper class for reading of bitcoin data types from a buffer. */ export declare class BufferReader { buffer: Buffer; From 5679a4b455dcb8e584daaf92d37e1dbab43da53d Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 15 Jan 2020 14:14:02 +0900 Subject: [PATCH 484/568] Check write/read Slice out of bounds --- src/bufferutils.js | 9 ++++++++- test/bufferutils.spec.ts | 6 ++++++ ts_src/bufferutils.ts | 9 ++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/bufferutils.js b/src/bufferutils.js index 98adbfb..103ca5e 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -67,6 +67,9 @@ class BufferWriter { this.offset += varuint.encode.bytes; } writeSlice(slice) { + if (this.buffer.length < this.offset + slice.length) { + throw new Error('Cannot write slice out of bounds'); + } this.offset += slice.copy(this.buffer, this.offset); } writeVarSlice(slice) { @@ -114,8 +117,12 @@ class BufferReader { return vi; } readSlice(n) { + if (this.buffer.length < this.offset + n) { + throw new Error('Cannot read slice out of bounds'); + } + const result = this.buffer.slice(this.offset, this.offset + n); this.offset += n; - return this.buffer.slice(this.offset - n, this.offset); + return result; } readVarSlice() { return this.readSlice(this.readVarInt()); diff --git a/test/bufferutils.spec.ts b/test/bufferutils.spec.ts index 2ed80e6..b8f86ab 100644 --- a/test/bufferutils.spec.ts +++ b/test/bufferutils.spec.ts @@ -209,6 +209,9 @@ describe('bufferutils', () => { testBuffer(bufferWriter, expectedBuffer, expectedOffset); }); testBuffer(bufferWriter, expectedBuffer); + assert.throws(() => { + bufferWriter.writeSlice(Buffer.from([0, 0])); + }, /^Error: Cannot write slice out of bounds$/); }); it('writeVarSlice', () => { @@ -421,6 +424,9 @@ describe('bufferutils', () => { const val = bufferReader.readSlice(v.length); testValue(bufferReader, val, Buffer.from(v), expectedOffset); }); + assert.throws(() => { + bufferReader.readSlice(2); + }, /^Error: Cannot read slice out of bounds$/); }); it('readVarSlice', () => { diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 502745d..23e1c0d 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -78,6 +78,9 @@ export class BufferWriter { } writeSlice(slice: Buffer): void { + if (this.buffer.length < this.offset + slice.length) { + throw new Error('Cannot write slice out of bounds'); + } this.offset += slice.copy(this.buffer, this.offset); } @@ -131,8 +134,12 @@ export class BufferReader { } readSlice(n: number): Buffer { + if (this.buffer.length < this.offset + n) { + throw new Error('Cannot read slice out of bounds'); + } + const result = this.buffer.slice(this.offset, this.offset + n); this.offset += n; - return this.buffer.slice(this.offset - n, this.offset); + return result; } readVarSlice(): Buffer { From a3f41a2b073f7871c719eaf52b6cdf844276b2bd Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 19 Mar 2020 08:06:12 +0900 Subject: [PATCH 485/568] Fix dependencies for vuln --- package-lock.json | 1073 +++++++++++++++++++-------------------------- package.json | 4 +- 2 files changed, 464 insertions(+), 613 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41f545c..ae2d378 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,29 +5,30 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "^7.8.3" } }, "@babel/core": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.7.tgz", - "integrity": "sha512-jlSjuj/7z138NLZALxVgrx13AOtqip42ATZP7+kYl53GvDV6+4dCek1mVUo8z8c8Xnw/mx2q3d9HWh3griuesQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", "dev": true, "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.7", - "@babel/helpers": "^7.7.4", - "@babel/parser": "^7.7.7", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", "json5": "^2.1.0", "lodash": "^4.17.13", "resolve": "^1.3.2", @@ -35,15 +36,6 @@ "source-map": "^0.5.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -56,61 +48,61 @@ } }, "@babel/generator": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.7.tgz", - "integrity": "sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==", + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.8.tgz", + "integrity": "sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg==", "dev": true, "requires": { - "@babel/types": "^7.7.4", + "@babel/types": "^7.8.7", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "dev": true, "requires": { - "@babel/types": "^7.7.4" + "@babel/types": "^7.8.3" } }, "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", "dev": true, "requires": { - "@babel/types": "^7.7.4" + "@babel/types": "^7.8.3" } }, "@babel/helpers": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz", - "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", "dev": true, "requires": { - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" } }, "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", "dev": true, "requires": { "chalk": "^2.0.0", @@ -119,48 +111,39 @@ } }, "@babel/parser": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.7.tgz", - "integrity": "sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw==", + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.8.tgz", + "integrity": "sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==", "dev": true }, "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", "dev": true, "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" }, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -173,9 +156,9 @@ } }, "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -310,6 +293,16 @@ "color-convert": "^1.9.0" } }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "append-transform": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", @@ -359,6 +352,12 @@ "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.3.tgz", "integrity": "sha512-yuVFUvrNcoJi0sv5phmqc6P+Fl1HjRDRNOOkHY2X/3LBy2bIGNSFx4fZ95HMaXHupuS7cZR15AsvtmCIF4UEyg==" }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, "bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -453,6 +452,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -521,6 +529,33 @@ "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" } }, "cipher-base": { @@ -539,22 +574,44 @@ "dev": true }, "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -571,9 +628,9 @@ "dev": true }, "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, "commondir": { @@ -623,16 +680,25 @@ } }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "debug": { @@ -703,33 +769,29 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" } }, "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -756,26 +818,11 @@ "dev": true }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -791,14 +838,23 @@ "merge-descriptors": "~1.0.0" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "find-cache-dir": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz", - "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", "dev": true, "requires": { "commondir": "^1.0.1", - "make-dir": "^3.0.0", + "make-dir": "^3.0.2", "pkg-dir": "^4.1.0" } }, @@ -828,49 +884,6 @@ "requires": { "cross-spawn": "^7.0.0", "signal-exit": "^3.0.2" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", - "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } } }, "fromentries": { @@ -885,31 +898,35 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -920,6 +937,15 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -954,9 +980,9 @@ "dev": true }, "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, "hash-base": { @@ -978,21 +1004,13 @@ } }, "hasha": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.1.0.tgz", - "integrity": "sha512-OFPDWmzPN1l7atOV1TgBVmNtBxaIysToK6Ve9DK+vT6pYuklw/nPNT+HJbZi0KDcI6vWB+9tgvZ5YD7fA3CXcA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", + "integrity": "sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw==", "dev": true, "requires": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" - }, - "dependencies": { - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - } } }, "he": { @@ -1050,28 +1068,37 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } }, "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", "dev": true }, "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", "dev": true }, "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, "is-fullwidth-code-point": { @@ -1080,6 +1107,21 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", @@ -1087,27 +1129,27 @@ "dev": true }, "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "dev": true, "requires": { - "has": "^1.0.1" + "has": "^1.0.3" } }, "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.1" } }, "is-typedarray": { @@ -1144,9 +1186,9 @@ } }, "istanbul-lib-instrument": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.0.tgz", - "integrity": "sha512-Nm4wVHdo7ZXSG30KjZ2Wl5SU/Bw7bDx1PdaiIFzEStdjs0H12mOTncn1GVYuqQSaZxpg87VGBRsVRPGD2cD1AQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", "dev": true, "requires": { "@babel/core": "^7.7.5", @@ -1181,69 +1223,14 @@ "uuid": "^3.3.3" }, "dependencies": { - "cross-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", - "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, "rimraf": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", - "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } } } }, @@ -1336,29 +1323,12 @@ "dev": true }, "json5": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", + "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", "dev": true, "requires": { - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" + "minimist": "^1.2.5" } }, "locate-path": { @@ -1384,18 +1354,18 @@ "dev": true }, "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", "dev": true, "requires": { - "chalk": "^2.0.1" + "chalk": "^2.4.2" } }, "make-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", - "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", "dev": true, "requires": { "semver": "^6.0.0" @@ -1415,15 +1385,6 @@ "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", "dev": true }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -1434,17 +1395,6 @@ "safe-buffer": "^5.1.2" } }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -1456,12 +1406,6 @@ "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", "integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY=" }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, "minimaldata": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/minimaldata/-/minimaldata-1.0.2.tgz", @@ -1492,28 +1436,29 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", + "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", "dev": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "mocha": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", - "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", + "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==", "dev": true, "requires": { "ansi-colors": "3.2.3", "browser-stdout": "1.3.1", + "chokidar": "3.3.0", "debug": "3.2.6", "diff": "3.5.0", "escape-string-regexp": "1.0.5", @@ -1522,44 +1467,19 @@ "growl": "1.10.5", "he": "1.2.0", "js-yaml": "3.13.1", - "log-symbols": "2.2.0", + "log-symbols": "3.0.0", "minimatch": "3.0.4", - "mkdirp": "0.5.1", + "mkdirp": "0.5.3", "ms": "2.1.1", - "node-environment-flags": "1.0.5", + "node-environment-flags": "1.0.6", "object.assign": "4.1.0", "strip-json-comments": "2.0.1", "supports-color": "6.0.0", "which": "1.3.1", "wide-align": "1.1.3", - "yargs": "13.2.2", - "yargs-parser": "13.0.0", - "yargs-unparser": "1.5.0" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" } }, "module-not-found-error": { @@ -1579,16 +1499,10 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "node-environment-flags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", - "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", "dev": true, "requires": { "object.getownpropertydescriptors": "^2.0.3", @@ -1604,19 +1518,10 @@ "process-on-spawn": "^1.0.0" } }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "nyc": { @@ -1758,9 +1663,9 @@ "dev": true }, "rimraf": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", - "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -1798,9 +1703,9 @@ } }, "yargs": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz", - "integrity": "sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -1813,13 +1718,13 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^16.1.0" + "yargs-parser": "^18.1.1" } }, "yargs-parser": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz", - "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==", + "version": "18.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.1.tgz", + "integrity": "sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -1828,6 +1733,12 @@ } } }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -1847,13 +1758,13 @@ } }, "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" } }, "once": { @@ -1865,39 +1776,10 @@ "wrappy": "1" } }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -1952,9 +1834,9 @@ "dev": true }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { @@ -1976,6 +1858,12 @@ "sha.js": "^2.4.8" } }, + "picomatch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", + "dev": true + }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -2047,16 +1935,6 @@ "resolve": "~1.8.1" } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "pushdata-bitcoin": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz", @@ -2073,6 +1951,15 @@ "safe-buffer": "^5.1.0" } }, + "readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.4" + } + }, "regtest-client": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/regtest-client/-/regtest-client-0.2.0.tgz", @@ -2160,9 +2047,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "set-blocking": { @@ -2181,18 +2068,18 @@ } }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "signal-exit": { @@ -2239,24 +2126,10 @@ "which": "^2.0.1" }, "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "rimraf": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", - "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -2295,6 +2168,26 @@ "strip-ansi": "^4.0.0" } }, + "string.prototype.trimleft": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -2310,12 +2203,6 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -2323,9 +2210,9 @@ "dev": true }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -2376,6 +2263,15 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "ts-node": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", @@ -2398,30 +2294,38 @@ } }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", "dev": true }, "tslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.16.0.tgz", - "integrity": "sha512-UxG2yNxJ5pgGwmMzPMYh/CCnCnh0HfPgtlVRDs1ykZklufFBL1ZoTlWFRz2NQjcoEiDoRp+JyT0lhBbbH/obyA==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", + "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", "chalk": "^2.3.0", "commander": "^2.12.1", - "diff": "^3.2.0", + "diff": "^4.0.1", "glob": "^7.1.1", - "js-yaml": "^3.13.0", + "js-yaml": "^3.13.1", "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "resolve": "^1.3.2", "semver": "^5.3.0", "tslib": "^1.8.0", "tsutils": "^2.29.0" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } } }, "tsutils": { @@ -2460,9 +2364,9 @@ "dev": true }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, "varuint-bitcoin": { @@ -2506,48 +2410,40 @@ } }, "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" }, "dependencies": { "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^4.1.0" } } } @@ -2559,9 +2455,9 @@ "dev": true }, "write-file-atomic": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", - "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { "imurmurhash": "^0.1.4", @@ -2577,22 +2473,21 @@ "dev": true }, "yargs": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", - "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { - "cliui": "^4.0.0", + "cliui": "^5.0.0", "find-up": "^3.0.0", "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.0.0" + "yargs-parser": "^13.1.2" }, "dependencies": { "ansi-regex": { @@ -2624,9 +2519,9 @@ } }, "yargs-parser": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", - "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -2634,58 +2529,14 @@ } }, "yargs-unparser": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", - "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", "dev": true, "requires": { "flat": "^4.1.0", - "lodash": "^4.17.11", - "yargs": "^12.0.5" - }, - "dependencies": { - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "lodash": "^4.17.15", + "yargs": "^13.3.0" } }, "yn": { diff --git a/package.json b/package.json index 03b8267..e94b78b 100644 --- a/package.json +++ b/package.json @@ -78,14 +78,14 @@ "dhttp": "^3.0.0", "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", - "mocha": "^6.2.0", + "mocha": "^7.1.1", "nyc": "^15.0.0", "prettier": "1.16.4", "proxyquire": "^2.0.1", "regtest-client": "0.2.0", "rimraf": "^2.6.3", "ts-node": "^8.3.0", - "tslint": "^5.16.0", + "tslint": "^5.20.1", "typescript": "3.2.2" }, "license": "MIT" From 6cfbea5b07943d9afde1fca7a5c9401e5c7a1751 Mon Sep 17 00:00:00 2001 From: "ITMASTER\\em.dagostini" <em.dagostini@almaviva.it> Date: Sat, 21 Mar 2020 16:22:20 +0100 Subject: [PATCH 486/568] Add "no user generated mnemonics" explanation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b034f20..0d6f2a0 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ We are not an authorative source of best practice, but, at the very least: * Don't share BIP32 extended public keys ('xpubs'). [They are a liability](https://bitcoin.stackexchange.com/questions/56916/derivation-of-parent-private-key-from-non-hardened-child), and it only takes 1 misplaced private key (or a buggy implementation!) and you are vulnerable to **catastrophic fund loss**. * [Don't use `Math.random`](https://security.stackexchange.com/questions/181580/why-is-math-random-not-designed-to-be-cryptographically-secure) - in any way - don't. * Enforce that users always verify (manually) a freshly-decoded human-readable version of their intended transaction before broadcast. -* Don't *ask* users to generate mnemonics, or 'brain wallets', humans are terrible random number generators. +* [Don't *ask* users to generate mnemonics](https://en.bitcoin.it/wiki/Brainwallet#cite_note-1), or 'brain wallets', humans are terrible random number generators. * Lastly, if you can, use [Typescript](https://www.typescriptlang.org/) or similar. From 854c6013420fca71f051adf03d65fd13a8dfbda7 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Sun, 26 Apr 2020 14:30:13 +0700 Subject: [PATCH 487/568] Add getters for Psbt.{txVersion,txLocktime,txInputs,txOutputs} --- src/psbt.js | 17 +++++++++++++++++ ts_src/psbt.ts | 22 ++++++++++++++++++++++ types/psbt.d.ts | 4 ++++ 3 files changed, 43 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index 5719586..95b344f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -97,6 +97,18 @@ class Psbt { get inputCount() { return this.data.inputs.length; } + get txVersion() { + return this.__CACHE.__TX.version; + } + get txLocktime() { + return this.__CACHE.__TX.locktime; + } + get txInputs() { + return deepClone(this.__CACHE.__TX.ins); + } + get txOutputs() { + return deepClone(this.__CACHE.__TX.outs); + } combine(...those) { this.data.combine(...those.map(o => o.data)); return this; @@ -579,6 +591,11 @@ class PsbtTransaction { return this.tx.toBuffer(); } } +function deepClone(obj) { + return JSON.parse(JSON.stringify(obj), (_, value) => + value.type === 'Buffer' ? Buffer.from(value.data) : value, + ); +} function canFinalize(input, script, scriptType) { switch (scriptType) { case 'pubkey': diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 54fe380..d86c714 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -129,6 +129,22 @@ export class Psbt { return this.data.inputs.length; } + get txVersion(): number { + return this.__CACHE.__TX.version; + } + + get txLocktime(): number { + return this.__CACHE.__TX.locktime; + } + + get txInputs(): TransactionInput[] { + return deepClone(this.__CACHE.__TX.ins); + } + + get txOutputs(): TransactionInput[] { + return deepClone(this.__CACHE.__TX.outs); + } + combine(...those: Psbt[]): this { this.data.combine(...those.map(o => o.data)); return this; @@ -757,6 +773,12 @@ class PsbtTransaction implements ITransaction { } } +function deepClone(obj: any): any { + return JSON.parse(JSON.stringify(obj), (_, value) => + value.type === 'Buffer' ? Buffer.from(value.data) : value, + ); +} + function canFinalize( input: PsbtInput, script: Buffer, diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 44eb4d8..fd8f8dc 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -44,6 +44,10 @@ export declare class Psbt { private opts; constructor(opts?: PsbtOptsOptional, data?: PsbtBase); readonly inputCount: number; + readonly txVersion: number; + readonly txLocktime: number; + readonly txInputs: TransactionInput[]; + readonly txOutputs: TransactionInput[]; combine(...those: Psbt[]): this; clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void; From 5d81b943624bea470e4f092d2d363c68f60081d4 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Sun, 26 Apr 2020 15:01:14 +0700 Subject: [PATCH 488/568] Update tests to use Psbt getters --- test/psbt.spec.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 4eb32dc..e0eba81 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -523,12 +523,9 @@ describe(`Psbt`, () => { }); assert.strictEqual(psbt.inputCount, 1); - assert.strictEqual( - (psbt as any).__CACHE.__TX.ins[0].sequence, - 0xffffffff, - ); + assert.strictEqual(psbt.txInputs[0].sequence, 0xffffffff); psbt.setInputSequence(0, 0); - assert.strictEqual((psbt as any).__CACHE.__TX.ins[0].sequence, 0); + assert.strictEqual(psbt.txInputs[0].sequence, 0); }); it('throws if input index is too high', () => { From f7034350e927743637658da303e835c21244df4f Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Sun, 26 Apr 2020 15:22:38 +0700 Subject: [PATCH 489/568] Clone transaction data more efficiently --- src/bufferutils.js | 6 ++++++ src/psbt.js | 22 +++++++++++++++------- ts_src/bufferutils.ts | 6 ++++++ ts_src/psbt.ts | 31 +++++++++++++++++++------------ types/bufferutils.d.ts | 1 + types/psbt.d.ts | 6 +++--- 6 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/bufferutils.js b/src/bufferutils.js index 103ca5e..87645c6 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -41,6 +41,12 @@ function reverseBuffer(buffer) { return buffer; } exports.reverseBuffer = reverseBuffer; +function cloneBuffer(buffer) { + const clone = Buffer.alloc(buffer.length); + buffer.copy(clone); + return buffer; +} +exports.cloneBuffer = cloneBuffer; /** * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ diff --git a/src/psbt.js b/src/psbt.js index 95b344f..4519197 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -104,10 +104,23 @@ class Psbt { return this.__CACHE.__TX.locktime; } get txInputs() { - return deepClone(this.__CACHE.__TX.ins); + return this.__CACHE.__TX.ins.map(input => { + return { + hash: bufferutils_1.cloneBuffer(input.hash), + index: input.index, + script: bufferutils_1.cloneBuffer(input.script), + sequence: input.sequence, + witness: input.witness.map(buffer => bufferutils_1.cloneBuffer(buffer)), + }; + }); } get txOutputs() { - return deepClone(this.__CACHE.__TX.outs); + return this.__CACHE.__TX.outs.map(output => { + return { + script: bufferutils_1.cloneBuffer(output.script), + value: output.value, + }; + }); } combine(...those) { this.data.combine(...those.map(o => o.data)); @@ -591,11 +604,6 @@ class PsbtTransaction { return this.tx.toBuffer(); } } -function deepClone(obj) { - return JSON.parse(JSON.stringify(obj), (_, value) => - value.type === 'Buffer' ? Buffer.from(value.data) : value, - ); -} function canFinalize(input, script, scriptType) { switch (scriptType) { case 'pubkey': diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 23e1c0d..087162f 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -48,6 +48,12 @@ export function reverseBuffer(buffer: Buffer): Buffer { return buffer; } +export function cloneBuffer(buffer: Buffer): Buffer { + const clone = Buffer.alloc(buffer.length); + buffer.copy(clone); + return buffer; +} + /** * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d86c714..f5f4e4d 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -14,7 +14,7 @@ import { } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; import { toOutputScript } from './address'; -import { reverseBuffer } from './bufferutils'; +import { cloneBuffer, reverseBuffer } from './bufferutils'; import { hash160 } from './crypto'; import { fromPublicKey as ecPairFromPublicKey, @@ -24,7 +24,7 @@ import { import { bitcoin as btcNetwork, Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; -import { Output, Transaction } from './transaction'; +import { Input, Output, Transaction } from './transaction'; /** * These are the default arguments for a Psbt instance. @@ -137,12 +137,25 @@ export class Psbt { return this.__CACHE.__TX.locktime; } - get txInputs(): TransactionInput[] { - return deepClone(this.__CACHE.__TX.ins); + get txInputs(): Input[] { + return this.__CACHE.__TX.ins.map(input => { + return { + hash: cloneBuffer(input.hash), + index: input.index, + script: cloneBuffer(input.script), + sequence: input.sequence, + witness: input.witness.map(buffer => cloneBuffer(buffer)), + }; + }); } - get txOutputs(): TransactionInput[] { - return deepClone(this.__CACHE.__TX.outs); + get txOutputs(): Output[] { + return this.__CACHE.__TX.outs.map(output => { + return { + script: cloneBuffer(output.script), + value: output.value, + }; + }); } combine(...those: Psbt[]): this { @@ -773,12 +786,6 @@ class PsbtTransaction implements ITransaction { } } -function deepClone(obj: any): any { - return JSON.parse(JSON.stringify(obj), (_, value) => - value.type === 'Buffer' ? Buffer.from(value.data) : value, - ); -} - function canFinalize( input: PsbtInput, script: Buffer, diff --git a/types/bufferutils.d.ts b/types/bufferutils.d.ts index 36e31fe..95a48ba 100644 --- a/types/bufferutils.d.ts +++ b/types/bufferutils.d.ts @@ -1,6 +1,7 @@ export declare function readUInt64LE(buffer: Buffer, offset: number): number; export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number; export declare function reverseBuffer(buffer: Buffer): Buffer; +export declare function cloneBuffer(buffer: Buffer): Buffer; /** * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ diff --git a/types/psbt.d.ts b/types/psbt.d.ts index fd8f8dc..a2373c0 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -2,7 +2,7 @@ import { Psbt as PsbtBase } from 'bip174'; import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; -import { Transaction } from './transaction'; +import { Input, Output, Transaction } from './transaction'; /** * Psbt class can parse and generate a PSBT binary based off of the BIP174. * There are 6 roles that this class fulfills. (Explained in BIP174) @@ -46,8 +46,8 @@ export declare class Psbt { readonly inputCount: number; readonly txVersion: number; readonly txLocktime: number; - readonly txInputs: TransactionInput[]; - readonly txOutputs: TransactionInput[]; + readonly txInputs: Input[]; + readonly txOutputs: Output[]; combine(...those: Psbt[]): this; clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void; From c68986231cd9934ef286ed91c0418adfee523ee0 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Sun, 26 Apr 2020 15:34:11 +0700 Subject: [PATCH 490/568] Improve property names --- src/psbt.js | 8 ++++---- test/psbt.spec.ts | 4 ++-- ts_src/psbt.ts | 8 ++++---- types/psbt.d.ts | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 4519197..4331ae5 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -97,13 +97,13 @@ class Psbt { get inputCount() { return this.data.inputs.length; } - get txVersion() { + get version() { return this.__CACHE.__TX.version; } - get txLocktime() { + get locktime() { return this.__CACHE.__TX.locktime; } - get txInputs() { + get inputs() { return this.__CACHE.__TX.ins.map(input => { return { hash: bufferutils_1.cloneBuffer(input.hash), @@ -114,7 +114,7 @@ class Psbt { }; }); } - get txOutputs() { + get outputs() { return this.__CACHE.__TX.outs.map(output => { return { script: bufferutils_1.cloneBuffer(output.script), diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index e0eba81..6b4fe0f 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -523,9 +523,9 @@ describe(`Psbt`, () => { }); assert.strictEqual(psbt.inputCount, 1); - assert.strictEqual(psbt.txInputs[0].sequence, 0xffffffff); + assert.strictEqual(psbt.inputs[0].sequence, 0xffffffff); psbt.setInputSequence(0, 0); - assert.strictEqual(psbt.txInputs[0].sequence, 0); + assert.strictEqual(psbt.inputs[0].sequence, 0); }); it('throws if input index is too high', () => { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index f5f4e4d..705e4be 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -129,15 +129,15 @@ export class Psbt { return this.data.inputs.length; } - get txVersion(): number { + get version(): number { return this.__CACHE.__TX.version; } - get txLocktime(): number { + get locktime(): number { return this.__CACHE.__TX.locktime; } - get txInputs(): Input[] { + get inputs(): Input[] { return this.__CACHE.__TX.ins.map(input => { return { hash: cloneBuffer(input.hash), @@ -149,7 +149,7 @@ export class Psbt { }); } - get txOutputs(): Output[] { + get outputs(): Output[] { return this.__CACHE.__TX.outs.map(output => { return { script: cloneBuffer(output.script), diff --git a/types/psbt.d.ts b/types/psbt.d.ts index a2373c0..a2c5501 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -44,10 +44,10 @@ export declare class Psbt { private opts; constructor(opts?: PsbtOptsOptional, data?: PsbtBase); readonly inputCount: number; - readonly txVersion: number; - readonly txLocktime: number; - readonly txInputs: Input[]; - readonly txOutputs: Output[]; + readonly version: number; + readonly locktime: number; + readonly inputs: Input[]; + readonly outputs: Output[]; combine(...those: Psbt[]): this; clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void; From c9b29645460f9d56075c07ed8107552814f1c880 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Sun, 26 Apr 2020 15:37:57 +0700 Subject: [PATCH 491/568] Remove extra return statement --- src/psbt.js | 26 +++++++++++--------------- ts_src/psbt.ts | 26 +++++++++++--------------- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 4331ae5..a3ee29b 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -104,23 +104,19 @@ class Psbt { return this.__CACHE.__TX.locktime; } get inputs() { - return this.__CACHE.__TX.ins.map(input => { - return { - hash: bufferutils_1.cloneBuffer(input.hash), - index: input.index, - script: bufferutils_1.cloneBuffer(input.script), - sequence: input.sequence, - witness: input.witness.map(buffer => bufferutils_1.cloneBuffer(buffer)), - }; - }); + return this.__CACHE.__TX.ins.map(input => ({ + hash: bufferutils_1.cloneBuffer(input.hash), + index: input.index, + script: bufferutils_1.cloneBuffer(input.script), + sequence: input.sequence, + witness: input.witness.map(buffer => bufferutils_1.cloneBuffer(buffer)), + })); } get outputs() { - return this.__CACHE.__TX.outs.map(output => { - return { - script: bufferutils_1.cloneBuffer(output.script), - value: output.value, - }; - }); + return this.__CACHE.__TX.outs.map(output => ({ + script: bufferutils_1.cloneBuffer(output.script), + value: output.value, + })); } combine(...those) { this.data.combine(...those.map(o => o.data)); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 705e4be..0fd177b 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -138,24 +138,20 @@ export class Psbt { } get inputs(): Input[] { - return this.__CACHE.__TX.ins.map(input => { - return { - hash: cloneBuffer(input.hash), - index: input.index, - script: cloneBuffer(input.script), - sequence: input.sequence, - witness: input.witness.map(buffer => cloneBuffer(buffer)), - }; - }); + return this.__CACHE.__TX.ins.map(input => ({ + hash: cloneBuffer(input.hash), + index: input.index, + script: cloneBuffer(input.script), + sequence: input.sequence, + witness: input.witness.map(buffer => cloneBuffer(buffer)), + })); } get outputs(): Output[] { - return this.__CACHE.__TX.outs.map(output => { - return { - script: cloneBuffer(output.script), - value: output.value, - }; - }); + return this.__CACHE.__TX.outs.map(output => ({ + script: cloneBuffer(output.script), + value: output.value, + })); } combine(...those: Psbt[]): this { From 58cdb3a36237732d512bedb7f09be713c9cdc330 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Sun, 26 Apr 2020 17:43:58 +0700 Subject: [PATCH 492/568] Add setters for version and locktime --- src/psbt.js | 6 ++++++ ts_src/psbt.ts | 8 ++++++++ types/psbt.d.ts | 4 ++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index a3ee29b..af7aa3d 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -100,9 +100,15 @@ class Psbt { get version() { return this.__CACHE.__TX.version; } + set version(version) { + this.setVersion(version); + } get locktime() { return this.__CACHE.__TX.locktime; } + set locktime(locktime) { + this.setLocktime(locktime); + } get inputs() { return this.__CACHE.__TX.ins.map(input => ({ hash: bufferutils_1.cloneBuffer(input.hash), diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 0fd177b..2395190 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -133,10 +133,18 @@ export class Psbt { return this.__CACHE.__TX.version; } + set version(version: number) { + this.setVersion(version); + } + get locktime(): number { return this.__CACHE.__TX.locktime; } + set locktime(locktime: number) { + this.setLocktime(locktime); + } + get inputs(): Input[] { return this.__CACHE.__TX.ins.map(input => ({ hash: cloneBuffer(input.hash), diff --git a/types/psbt.d.ts b/types/psbt.d.ts index a2c5501..b48d828 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -44,8 +44,8 @@ export declare class Psbt { private opts; constructor(opts?: PsbtOptsOptional, data?: PsbtBase); readonly inputCount: number; - readonly version: number; - readonly locktime: number; + version: number; + locktime: number; readonly inputs: Input[]; readonly outputs: Output[]; combine(...those: Psbt[]): this; From 6c616bff78a6e685b150df0d73be7aec48016df3 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Sun, 26 Apr 2020 18:05:18 +0700 Subject: [PATCH 493/568] Add address to output data --- src/psbt.js | 1 + ts_src/psbt.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/psbt.js b/src/psbt.js index af7aa3d..56b5275 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -122,6 +122,7 @@ class Psbt { return this.__CACHE.__TX.outs.map(output => ({ script: bufferutils_1.cloneBuffer(output.script), value: output.value, + address: address_1.fromOutputScript(output.script, this.opts.network), })); } combine(...those) { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 2395190..8d35563 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -13,7 +13,7 @@ import { TransactionInput, } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; -import { toOutputScript } from './address'; +import { fromOutputScript, toOutputScript } from './address'; import { cloneBuffer, reverseBuffer } from './bufferutils'; import { hash160 } from './crypto'; import { @@ -159,6 +159,7 @@ export class Psbt { return this.__CACHE.__TX.outs.map(output => ({ script: cloneBuffer(output.script), value: output.value, + address: fromOutputScript(output.script, this.opts.network), })); } From e7345d54465c78a302ace893befd56dde5243f0c Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Sun, 26 Apr 2020 18:07:09 +0700 Subject: [PATCH 494/568] {in,out}puts => tx{In,Out}puts --- src/psbt.js | 4 ++-- test/psbt.spec.ts | 4 ++-- ts_src/psbt.ts | 4 ++-- types/psbt.d.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 56b5275..2fe816a 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -109,7 +109,7 @@ class Psbt { set locktime(locktime) { this.setLocktime(locktime); } - get inputs() { + get txInputs() { return this.__CACHE.__TX.ins.map(input => ({ hash: bufferutils_1.cloneBuffer(input.hash), index: input.index, @@ -118,7 +118,7 @@ class Psbt { witness: input.witness.map(buffer => bufferutils_1.cloneBuffer(buffer)), })); } - get outputs() { + get txOutputs() { return this.__CACHE.__TX.outs.map(output => ({ script: bufferutils_1.cloneBuffer(output.script), value: output.value, diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 6b4fe0f..e0eba81 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -523,9 +523,9 @@ describe(`Psbt`, () => { }); assert.strictEqual(psbt.inputCount, 1); - assert.strictEqual(psbt.inputs[0].sequence, 0xffffffff); + assert.strictEqual(psbt.txInputs[0].sequence, 0xffffffff); psbt.setInputSequence(0, 0); - assert.strictEqual(psbt.inputs[0].sequence, 0); + assert.strictEqual(psbt.txInputs[0].sequence, 0); }); it('throws if input index is too high', () => { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 8d35563..d6fc8ea 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -145,7 +145,7 @@ export class Psbt { this.setLocktime(locktime); } - get inputs(): Input[] { + get txInputs(): Input[] { return this.__CACHE.__TX.ins.map(input => ({ hash: cloneBuffer(input.hash), index: input.index, @@ -155,7 +155,7 @@ export class Psbt { })); } - get outputs(): Output[] { + get txOutputs(): Output[] { return this.__CACHE.__TX.outs.map(output => ({ script: cloneBuffer(output.script), value: output.value, diff --git a/types/psbt.d.ts b/types/psbt.d.ts index b48d828..66f7202 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -46,8 +46,8 @@ export declare class Psbt { readonly inputCount: number; version: number; locktime: number; - readonly inputs: Input[]; - readonly outputs: Output[]; + readonly txInputs: Input[]; + readonly txOutputs: Output[]; combine(...those: Psbt[]): this; clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void; From 2d4a3b9354ef032b7bc62331409567ba9074c4cd Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Sun, 26 Apr 2020 18:15:06 +0700 Subject: [PATCH 495/568] Don't expose witness on Psbt.txInputs --- src/psbt.js | 1 - ts_src/psbt.ts | 8 ++++---- types/psbt.d.ts | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 2fe816a..7b2928e 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -115,7 +115,6 @@ class Psbt { index: input.index, script: bufferutils_1.cloneBuffer(input.script), sequence: input.sequence, - witness: input.witness.map(buffer => bufferutils_1.cloneBuffer(buffer)), })); } get txOutputs() { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d6fc8ea..da19497 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -11,6 +11,7 @@ import { Transaction as ITransaction, TransactionFromBuffer, TransactionInput, + TransactionOutput, } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; import { fromOutputScript, toOutputScript } from './address'; @@ -24,7 +25,7 @@ import { import { bitcoin as btcNetwork, Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; -import { Input, Output, Transaction } from './transaction'; +import { Output, Transaction } from './transaction'; /** * These are the default arguments for a Psbt instance. @@ -145,17 +146,16 @@ export class Psbt { this.setLocktime(locktime); } - get txInputs(): Input[] { + get txInputs(): TransactionInput[] { return this.__CACHE.__TX.ins.map(input => ({ hash: cloneBuffer(input.hash), index: input.index, script: cloneBuffer(input.script), sequence: input.sequence, - witness: input.witness.map(buffer => cloneBuffer(buffer)), })); } - get txOutputs(): Output[] { + get txOutputs(): TransactionOutput[] { return this.__CACHE.__TX.outs.map(output => ({ script: cloneBuffer(output.script), value: output.value, diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 66f7202..0a898d8 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,8 +1,8 @@ import { Psbt as PsbtBase } from 'bip174'; -import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; +import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; -import { Input, Output, Transaction } from './transaction'; +import { Transaction } from './transaction'; /** * Psbt class can parse and generate a PSBT binary based off of the BIP174. * There are 6 roles that this class fulfills. (Explained in BIP174) @@ -46,8 +46,8 @@ export declare class Psbt { readonly inputCount: number; version: number; locktime: number; - readonly txInputs: Input[]; - readonly txOutputs: Output[]; + readonly txInputs: TransactionInput[]; + readonly txOutputs: TransactionOutput[]; combine(...those: Psbt[]): this; clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void; From fde6025480c16dc150eb6948c14fcaf4cf0d50d4 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Mon, 27 Apr 2020 10:23:53 +0700 Subject: [PATCH 496/568] Remove script from txInputs --- src/psbt.js | 1 - ts_src/psbt.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 7b2928e..134be87 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -113,7 +113,6 @@ class Psbt { return this.__CACHE.__TX.ins.map(input => ({ hash: bufferutils_1.cloneBuffer(input.hash), index: input.index, - script: bufferutils_1.cloneBuffer(input.script), sequence: input.sequence, })); } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index da19497..89491b9 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -150,7 +150,6 @@ export class Psbt { return this.__CACHE.__TX.ins.map(input => ({ hash: cloneBuffer(input.hash), index: input.index, - script: cloneBuffer(input.script), sequence: input.sequence, })); } From 361ea7c098489545253ba1824202c41a27d22596 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 27 Apr 2020 17:10:11 +0900 Subject: [PATCH 497/568] Add inputHasPubkey and outputHasPubkey methods --- src/psbt.js | 100 +++++++++++++++++++++++++++++--- test/psbt.spec.ts | 141 +++++++++++++++++++++++++++++++++++++++++++++- ts_src/psbt.ts | 127 +++++++++++++++++++++++++++++++++++++---- types/psbt.d.ts | 2 + 4 files changed, 349 insertions(+), 21 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 134be87..48fb798 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -277,6 +277,14 @@ class Psbt { this.data.clearFinalizedInput(inputIndex); return this; } + inputHasPubkey(inputIndex, pubkey) { + const input = utils_1.checkForInput(this.data.inputs, inputIndex); + return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE); + } + outputHasPubkey(outputIndex, pubkey) { + const output = utils_1.checkForOutput(this.data.outputs, outputIndex); + return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE); + } validateSignaturesOfAllInputs() { utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one const results = range(this.data.inputs.length).map(idx => @@ -653,6 +661,7 @@ const isP2PK = isPaymentFactory(payments.p2pk); const isP2PKH = isPaymentFactory(payments.p2pkh); const isP2WPKH = isPaymentFactory(payments.p2wpkh); const isP2WSHScript = isPaymentFactory(payments.p2wsh); +const isP2SHScript = isPaymentFactory(payments.p2sh); function check32Bit(num) { if ( typeof num !== 'number' || @@ -723,14 +732,7 @@ function checkPartialSigSighashes(input) { }); } function checkScriptForPubkey(pubkey, script, action) { - const pubkeyHash = crypto_1.hash160(pubkey); - const decompiled = bscript.decompile(script); - if (decompiled === null) throw new Error('Unknown script error'); - const hasKey = decompiled.some(element => { - if (typeof element === 'number') return false; - return element.equals(pubkey) || element.equals(pubkeyHash); - }); - if (!hasKey) { + if (!pubkeyInScript(pubkey, script)) { throw new Error( `Can not ${action} for this input with the key ${pubkey.toString('hex')}`, ); @@ -1219,6 +1221,88 @@ function nonWitnessUtxoTxFromCache(cache, input, inputIndex) { } return c[inputIndex]; } +function pubkeyInInput(pubkey, input, inputIndex, cache) { + let script; + if (input.witnessUtxo !== undefined) { + script = input.witnessUtxo.script; + } else if (input.nonWitnessUtxo !== undefined) { + const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( + cache, + input, + inputIndex, + ); + script = nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script; + } else { + throw new Error("Can't find pubkey in input without Utxo data"); + } + const meaningfulScript = checkScripts( + script, + input.redeemScript, + input.witnessScript, + ); + return pubkeyInScript(pubkey, meaningfulScript); +} +function pubkeyInOutput(pubkey, output, outputIndex, cache) { + const script = cache.__TX.outs[outputIndex].script; + const meaningfulScript = checkScripts( + script, + output.redeemScript, + output.witnessScript, + ); + return pubkeyInScript(pubkey, meaningfulScript); +} +function checkScripts(script, redeemScript, witnessScript) { + let fail = false; + if (isP2SHScript(script)) { + if (redeemScript === undefined) { + fail = true; + } else if (isP2WSHScript(redeemScript)) { + if (witnessScript === undefined) { + fail = true; + } else { + fail = !payments + .p2sh({ + redeem: payments.p2wsh({ + redeem: { output: witnessScript }, + }), + }) + .output.equals(script); + if (!fail) return witnessScript; + } + } else { + fail = !payments + .p2sh({ + redeem: { output: redeemScript }, + }) + .output.equals(script); + if (!fail) return redeemScript; + } + } else if (isP2WSHScript(script)) { + if (witnessScript === undefined) { + fail = true; + } else { + fail = !payments + .p2wsh({ + redeem: { output: witnessScript }, + }) + .output.equals(script); + if (!fail) return witnessScript; + } + } + if (fail) { + throw new Error('Incomplete script information'); + } + return script; +} +function pubkeyInScript(pubkey, script) { + const pubkeyHash = crypto_1.hash160(pubkey); + const decompiled = bscript.decompile(script); + if (decompiled === null) throw new Error('Unknown script error'); + return decompiled.some(element => { + if (typeof element === 'number') return false; + return element.equals(pubkey) || element.equals(pubkeyHash); + }); +} function classifyScript(script) { if (isP2WPKH(script)) return 'witnesspubkeyhash'; if (isP2PKH(script)) return 'pubkeyhash'; diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index e0eba81..9400a5e 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; -import { bip32, ECPair, networks as NETWORKS, Psbt } from '..'; +import { bip32, ECPair, networks as NETWORKS, Psbt, payments } from '..'; import * as preFixtures from './fixtures/psbt.json'; @@ -542,6 +542,143 @@ describe(`Psbt`, () => { }); }); + describe('inputHasPubkey', () => { + it('should throw', () => { + const psbt = new Psbt(); + psbt.addInput({ + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, + }); + + assert.throws(() => { + psbt.inputHasPubkey(0, Buffer.from([])); + }, new RegExp("Can't find pubkey in input without Utxo data")); + + psbt.updateInput(0, { + witnessUtxo: { + value: 1337, + script: payments.p2sh({ + redeem: { output: Buffer.from([0x51]) }, + }).output!, + }, + }); + + assert.throws(() => { + psbt.inputHasPubkey(0, Buffer.from([])); + }, new RegExp('Incomplete script information')); + + delete psbt.data.inputs[0].witnessUtxo; + + psbt.updateInput(0, { + witnessUtxo: { + value: 1337, + script: payments.p2wsh({ + redeem: { output: Buffer.from([0x51]) }, + }).output!, + }, + }); + + assert.throws(() => { + psbt.inputHasPubkey(0, Buffer.from([])); + }, new RegExp('Incomplete script information')); + + delete psbt.data.inputs[0].witnessUtxo; + + psbt.updateInput(0, { + witnessUtxo: { + value: 1337, + script: payments.p2sh({ + redeem: payments.p2wsh({ + redeem: { output: Buffer.from([0x51]) }, + }), + }).output!, + }, + redeemScript: payments.p2wsh({ + redeem: { output: Buffer.from([0x51]) }, + }).output!, + }); + + assert.throws(() => { + psbt.inputHasPubkey(0, Buffer.from([])); + }, new RegExp('Incomplete script information')); + + psbt.updateInput(0, { + witnessScript: Buffer.from([0x51]), + }); + + assert.doesNotThrow(() => { + psbt.inputHasPubkey(0, Buffer.from([0x51])); + }); + }); + }); + + describe('outputHasPubkey', () => { + it('should throw', () => { + const psbt = new Psbt(); + psbt + .addInput({ + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, + }) + .addOutput({ + script: payments.p2sh({ + redeem: { output: Buffer.from([0x51]) }, + }).output!, + value: 1337, + }); + + assert.throws(() => { + psbt.outputHasPubkey(0, Buffer.from([])); + }, new RegExp('Incomplete script information')); + + (psbt as any).__CACHE.__TX.outs[0].script = payments.p2wsh({ + redeem: { output: Buffer.from([0x51]) }, + }).output!; + + assert.throws(() => { + psbt.outputHasPubkey(0, Buffer.from([])); + }, new RegExp('Incomplete script information')); + + (psbt as any).__CACHE.__TX.outs[0].script = payments.p2sh({ + redeem: payments.p2wsh({ + redeem: { output: Buffer.from([0x51]) }, + }), + }).output!; + + psbt.updateOutput(0, { + redeemScript: payments.p2wsh({ + redeem: { output: Buffer.from([0x51]) }, + }).output!, + }); + + assert.throws(() => { + psbt.outputHasPubkey(0, Buffer.from([])); + }, new RegExp('Incomplete script information')); + + delete psbt.data.outputs[0].redeemScript; + + psbt.updateOutput(0, { + witnessScript: Buffer.from([0x51]), + }); + + assert.throws(() => { + psbt.outputHasPubkey(0, Buffer.from([])); + }, new RegExp('Incomplete script information')); + + psbt.updateOutput(0, { + redeemScript: payments.p2wsh({ + redeem: { output: Buffer.from([0x51]) }, + }).output!, + }); + + assert.doesNotThrow(() => { + psbt.outputHasPubkey(0, Buffer.from([0x51])); + }); + }); + }); + describe('clone', () => { it('Should clone a psbt exactly with no reference', () => { const f = fixtures.clone; @@ -643,6 +780,8 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.setVersion(3); }, new RegExp('Can not modify transaction, signatures exist.')); + assert.strictEqual(psbt.inputHasPubkey(0, alice.publicKey), true); + assert.strictEqual(psbt.outputHasPubkey(0, alice.publicKey), false); assert.strictEqual( psbt.extractTransaction().toHex(), '02000000013ebc8203037dda39d482bf41ff3be955996c50d9d4f7cfc3d2097a694a7' + diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 89491b9..301596a 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -13,7 +13,7 @@ import { TransactionInput, TransactionOutput, } from 'bip174/src/lib/interfaces'; -import { checkForInput } from 'bip174/src/lib/utils'; +import { checkForInput, checkForOutput } from 'bip174/src/lib/utils'; import { fromOutputScript, toOutputScript } from './address'; import { cloneBuffer, reverseBuffer } from './bufferutils'; import { hash160 } from './crypto'; @@ -340,6 +340,16 @@ export class Psbt { return this; } + inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean { + const input = checkForInput(this.data.inputs, inputIndex); + return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE); + } + + outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean { + const output = checkForOutput(this.data.outputs, outputIndex); + return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE); + } + validateSignaturesOfAllInputs(): boolean { checkForInput(this.data.inputs, 0); // making sure we have at least one const results = range(this.data.inputs.length).map(idx => @@ -849,6 +859,7 @@ const isP2PK = isPaymentFactory(payments.p2pk); const isP2PKH = isPaymentFactory(payments.p2pkh); const isP2WPKH = isPaymentFactory(payments.p2wpkh); const isP2WSHScript = isPaymentFactory(payments.p2wsh); +const isP2SHScript = isPaymentFactory(payments.p2sh); function check32Bit(num: number): void { if ( @@ -927,17 +938,7 @@ function checkScriptForPubkey( script: Buffer, action: string, ): void { - const pubkeyHash = hash160(pubkey); - - const decompiled = bscript.decompile(script); - if (decompiled === null) throw new Error('Unknown script error'); - - const hasKey = decompiled.some(element => { - if (typeof element === 'number') return false; - return element.equals(pubkey) || element.equals(pubkeyHash); - }); - - if (!hasKey) { + if (!pubkeyInScript(pubkey, script)) { throw new Error( `Can not ${action} for this input with the key ${pubkey.toString('hex')}`, ); @@ -1560,6 +1561,108 @@ function nonWitnessUtxoTxFromCache( return c[inputIndex]; } +function pubkeyInInput( + pubkey: Buffer, + input: PsbtInput, + inputIndex: number, + cache: PsbtCache, +): boolean { + let script: Buffer; + if (input.witnessUtxo !== undefined) { + script = input.witnessUtxo.script; + } else if (input.nonWitnessUtxo !== undefined) { + const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( + cache, + input, + inputIndex, + ); + script = nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script; + } else { + throw new Error("Can't find pubkey in input without Utxo data"); + } + const meaningfulScript = checkScripts( + script, + input.redeemScript, + input.witnessScript, + ); + return pubkeyInScript(pubkey, meaningfulScript); +} + +function pubkeyInOutput( + pubkey: Buffer, + output: PsbtOutput, + outputIndex: number, + cache: PsbtCache, +): boolean { + const script = cache.__TX.outs[outputIndex].script; + const meaningfulScript = checkScripts( + script, + output.redeemScript, + output.witnessScript, + ); + return pubkeyInScript(pubkey, meaningfulScript); +} + +function checkScripts( + script: Buffer, + redeemScript?: Buffer, + witnessScript?: Buffer, +): Buffer { + let fail = false; + if (isP2SHScript(script)) { + if (redeemScript === undefined) { + fail = true; + } else if (isP2WSHScript(redeemScript)) { + if (witnessScript === undefined) { + fail = true; + } else { + fail = !payments + .p2sh({ + redeem: payments.p2wsh({ + redeem: { output: witnessScript }, + }), + }) + .output!.equals(script); + if (!fail) return witnessScript; + } + } else { + fail = !payments + .p2sh({ + redeem: { output: redeemScript }, + }) + .output!.equals(script); + if (!fail) return redeemScript; + } + } else if (isP2WSHScript(script)) { + if (witnessScript === undefined) { + fail = true; + } else { + fail = !payments + .p2wsh({ + redeem: { output: witnessScript }, + }) + .output!.equals(script); + if (!fail) return witnessScript; + } + } + if (fail) { + throw new Error('Incomplete script information'); + } + return script; +} + +function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean { + const pubkeyHash = hash160(pubkey); + + const decompiled = bscript.decompile(script); + if (decompiled === null) throw new Error('Unknown script error'); + + return decompiled.some(element => { + if (typeof element === 'number') return false; + return element.equals(pubkey) || element.equals(pubkeyHash); + }); +} + function classifyScript(script: Buffer): string { if (isP2WPKH(script)) return 'witnesspubkeyhash'; if (isP2PKH(script)) return 'pubkeyhash'; diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 0a898d8..c47ce74 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -63,6 +63,8 @@ export declare class Psbt { getFee(): number; finalizeAllInputs(): this; finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this; + inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean; + outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean; validateSignaturesOfAllInputs(): boolean; validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean; signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; From de0bbf51e59e74dad62d4a15f4913e6bd6a9f3c6 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Mon, 27 Apr 2020 16:46:07 +0700 Subject: [PATCH 498/568] Export PSBT getter types --- ts_src/index.ts | 2 +- ts_src/psbt.ts | 13 ++++++++++--- types/index.d.ts | 2 +- types/psbt.d.ts | 14 ++++++++++---- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/ts_src/index.ts b/ts_src/index.ts index 505407f..b9aa49c 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -9,7 +9,7 @@ import * as script from './script'; export { ECPair, address, bip32, crypto, networks, payments, script }; export { Block } from './block'; -export { Psbt } from './psbt'; +export { Psbt, PsbtTxInput, PsbtTxOutput } from './psbt'; export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 301596a..31d15c7 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -11,7 +11,6 @@ import { Transaction as ITransaction, TransactionFromBuffer, TransactionInput, - TransactionOutput, } from 'bip174/src/lib/interfaces'; import { checkForInput, checkForOutput } from 'bip174/src/lib/utils'; import { fromOutputScript, toOutputScript } from './address'; @@ -27,6 +26,14 @@ import * as payments from './payments'; import * as bscript from './script'; import { Output, Transaction } from './transaction'; +export interface PsbtTxInput extends TransactionInput { + hash: Buffer; +} + +export interface PsbtTxOutput extends Output { + address: string; +} + /** * These are the default arguments for a Psbt instance. */ @@ -146,7 +153,7 @@ export class Psbt { this.setLocktime(locktime); } - get txInputs(): TransactionInput[] { + get txInputs(): PsbtTxInput[] { return this.__CACHE.__TX.ins.map(input => ({ hash: cloneBuffer(input.hash), index: input.index, @@ -154,7 +161,7 @@ export class Psbt { })); } - get txOutputs(): TransactionOutput[] { + get txOutputs(): PsbtTxOutput[] { return this.__CACHE.__TX.outs.map(output => ({ script: cloneBuffer(output.script), value: output.value, diff --git a/types/index.d.ts b/types/index.d.ts index 68da119..c8f2a00 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -7,7 +7,7 @@ import * as payments from './payments'; import * as script from './script'; export { ECPair, address, bip32, crypto, networks, payments, script }; export { Block } from './block'; -export { Psbt } from './psbt'; +export { Psbt, PsbtTxInput, PsbtTxOutput } from './psbt'; export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; export { TransactionBuilder } from './transaction_builder'; diff --git a/types/psbt.d.ts b/types/psbt.d.ts index c47ce74..127ef0f 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,8 +1,14 @@ import { Psbt as PsbtBase } from 'bip174'; -import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; +import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; -import { Transaction } from './transaction'; +import { Output, Transaction } from './transaction'; +export interface PsbtTxInput extends TransactionInput { + hash: Buffer; +} +export interface PsbtTxOutput extends Output { + address: string; +} /** * Psbt class can parse and generate a PSBT binary based off of the BIP174. * There are 6 roles that this class fulfills. (Explained in BIP174) @@ -46,8 +52,8 @@ export declare class Psbt { readonly inputCount: number; version: number; locktime: number; - readonly txInputs: TransactionInput[]; - readonly txOutputs: TransactionOutput[]; + readonly txInputs: PsbtTxInput[]; + readonly txOutputs: PsbtTxOutput[]; combine(...those: Psbt[]): this; clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void; From 9fd13f3a43c99273644c0ba205794864d2749f71 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Mon, 27 Apr 2020 17:18:05 +0700 Subject: [PATCH 499/568] Fix lint error --- test/psbt.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 9400a5e..f755ba7 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; -import { bip32, ECPair, networks as NETWORKS, Psbt, payments } from '..'; +import { bip32, ECPair, networks as NETWORKS, payments, Psbt } from '..'; import * as preFixtures from './fixtures/psbt.json'; From e9382ebea26875dea19ce3888cd2aaf37e5fc783 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Mon, 27 Apr 2020 20:37:32 +0700 Subject: [PATCH 500/568] Fix horrific bug! --- src/bufferutils.js | 2 +- ts_src/bufferutils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bufferutils.js b/src/bufferutils.js index 87645c6..2ee3542 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -44,7 +44,7 @@ exports.reverseBuffer = reverseBuffer; function cloneBuffer(buffer) { const clone = Buffer.alloc(buffer.length); buffer.copy(clone); - return buffer; + return clone; } exports.cloneBuffer = cloneBuffer; /** diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 087162f..2025f88 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -51,7 +51,7 @@ export function reverseBuffer(buffer: Buffer): Buffer { export function cloneBuffer(buffer: Buffer): Buffer { const clone = Buffer.alloc(buffer.length); buffer.copy(clone); - return buffer; + return clone; } /** From e3bf997d64dea29efcdd24f9d0441dfb844d6285 Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Mon, 27 Apr 2020 20:38:04 +0700 Subject: [PATCH 501/568] Improve test coverage --- test/psbt.spec.ts | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index f755ba7..de83fe3 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -846,4 +846,71 @@ describe(`Psbt`, () => { assert.ok((psbt as any).data.inputs[index].nonWitnessUtxo.equals(value)); }); }); + + describe('Transaction properties', () => { + it('.version is exposed and is settable', () => { + const psbt = new Psbt(); + + assert.strictEqual(psbt.version, 2); + assert.strictEqual(psbt.version, (psbt as any).__CACHE.__TX.version); + + psbt.version = 1; + assert.strictEqual(psbt.version, 1); + assert.strictEqual(psbt.version, (psbt as any).__CACHE.__TX.version); + }); + + it('.locktime is exposed and is settable', () => { + const psbt = new Psbt(); + + assert.strictEqual(psbt.locktime, 0); + assert.strictEqual(psbt.locktime, (psbt as any).__CACHE.__TX.locktime); + + psbt.locktime = 123; + assert.strictEqual(psbt.locktime, 123); + assert.strictEqual(psbt.locktime, (psbt as any).__CACHE.__TX.locktime); + }); + + it('.txInputs is exposed as a readonly clone', () => { + const psbt = new Psbt(); + const hash = Buffer.alloc(32); + const index = 0; + psbt.addInput({ hash, index }); + + const input = psbt.txInputs[0]; + const internalInput = (psbt as any).__CACHE.__TX.ins[0]; + + assert.ok(input.hash.equals(internalInput.hash)); + assert.strictEqual(input.index, internalInput.index); + assert.strictEqual(input.sequence, internalInput.sequence); + + input.hash[0] = 123; + input.index = 123; + input.sequence = 123; + + assert.ok(!input.hash.equals(internalInput.hash)); + assert.notEqual(input.index, internalInput.index); + assert.notEqual(input.sequence, internalInput.sequence); + }); + + it('.txOutputs is exposed as a readonly clone', () => { + const psbt = new Psbt(); + const address = '1LukeQU5jwebXbMLDVydeH4vFSobRV9rkj'; + const value = 100000; + psbt.addOutput({ address, value }); + + const output = psbt.txOutputs[0]; + const internalInput = (psbt as any).__CACHE.__TX.outs[0]; + + assert.strictEqual(output.address, address); + + assert.ok(output.script.equals(internalInput.script)); + assert.strictEqual(output.value, internalInput.value); + + output.script[0] = 123; + output.value = 123; + + assert.ok(!output.script.equals(internalInput.script)); + assert.notEqual(output.value, internalInput.value); + }); + }); }); From 97074f8a649e9b2733d6c24ac9e18b9b09517211 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 28 Apr 2020 14:41:48 +0900 Subject: [PATCH 502/568] Refactor getMeaningfulScript --- src/psbt.js | 80 +++++++++++++++++++++------------------------ test/psbt.spec.ts | 14 ++++---- ts_src/psbt.ts | 83 +++++++++++++++++++++++------------------------ 3 files changed, 84 insertions(+), 93 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 48fb798..91e0438 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -1235,7 +1235,7 @@ function pubkeyInInput(pubkey, input, inputIndex, cache) { } else { throw new Error("Can't find pubkey in input without Utxo data"); } - const meaningfulScript = checkScripts( + const meaningfulScript = getMeaningfulScript( script, input.redeemScript, input.witnessScript, @@ -1244,55 +1244,49 @@ function pubkeyInInput(pubkey, input, inputIndex, cache) { } function pubkeyInOutput(pubkey, output, outputIndex, cache) { const script = cache.__TX.outs[outputIndex].script; - const meaningfulScript = checkScripts( + const meaningfulScript = getMeaningfulScript( script, output.redeemScript, output.witnessScript, ); return pubkeyInScript(pubkey, meaningfulScript); } -function checkScripts(script, redeemScript, witnessScript) { - let fail = false; - if (isP2SHScript(script)) { - if (redeemScript === undefined) { - fail = true; - } else if (isP2WSHScript(redeemScript)) { - if (witnessScript === undefined) { - fail = true; - } else { - fail = !payments - .p2sh({ - redeem: payments.p2wsh({ - redeem: { output: witnessScript }, - }), - }) - .output.equals(script); - if (!fail) return witnessScript; - } - } else { - fail = !payments - .p2sh({ - redeem: { output: redeemScript }, - }) - .output.equals(script); - if (!fail) return redeemScript; - } - } else if (isP2WSHScript(script)) { - if (witnessScript === undefined) { - fail = true; - } else { - fail = !payments - .p2wsh({ - redeem: { output: witnessScript }, - }) - .output.equals(script); - if (!fail) return witnessScript; - } +function getMeaningfulScript(script, redeemScript, witnessScript) { + const { p2sh, p2wsh } = payments; + const isP2SH = isP2SHScript(script); + const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript); + const isP2WSH = isP2WSHScript(script); + if (isP2SH && redeemScript === undefined) + throw new Error('scriptPubkey is P2SH but redeemScript missing'); + if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined) + throw new Error( + 'scriptPubkey or redeemScript is P2WSH but witnessScript missing', + ); + let payment; + let meaningfulScript; + if (isP2SHP2WSH) { + meaningfulScript = witnessScript; + payment = p2sh({ redeem: p2wsh({ redeem: { output: meaningfulScript } }) }); + if (!payment.redeem.output.equals(redeemScript)) + throw new Error('P2SHP2WSH witnessScript and redeemScript do not match'); + if (!payment.output.equals(script)) + throw new Error( + 'P2SHP2WSH witnessScript+redeemScript and scriptPubkey do not match', + ); + } else if (isP2WSH) { + meaningfulScript = witnessScript; + payment = p2wsh({ redeem: { output: meaningfulScript } }); + if (!payment.output.equals(script)) + throw new Error('P2WSH witnessScript and scriptPubkey do not match'); + } else if (isP2SH) { + meaningfulScript = redeemScript; + payment = p2sh({ redeem: { output: meaningfulScript } }); + if (!payment.output.equals(script)) + throw new Error('P2SH redeemScript and scriptPubkey do not match'); + } else { + meaningfulScript = script; } - if (fail) { - throw new Error('Incomplete script information'); - } - return script; + return meaningfulScript; } function pubkeyInScript(pubkey, script) { const pubkeyHash = crypto_1.hash160(pubkey); diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index de83fe3..ff2131b 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -566,7 +566,7 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.inputHasPubkey(0, Buffer.from([])); - }, new RegExp('Incomplete script information')); + }, new RegExp('scriptPubkey is P2SH but redeemScript missing')); delete psbt.data.inputs[0].witnessUtxo; @@ -581,7 +581,7 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.inputHasPubkey(0, Buffer.from([])); - }, new RegExp('Incomplete script information')); + }, new RegExp('scriptPubkey or redeemScript is P2WSH but witnessScript missing')); delete psbt.data.inputs[0].witnessUtxo; @@ -601,7 +601,7 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.inputHasPubkey(0, Buffer.from([])); - }, new RegExp('Incomplete script information')); + }, new RegExp('scriptPubkey or redeemScript is P2WSH but witnessScript missing')); psbt.updateInput(0, { witnessScript: Buffer.from([0x51]), @@ -631,7 +631,7 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.outputHasPubkey(0, Buffer.from([])); - }, new RegExp('Incomplete script information')); + }, new RegExp('scriptPubkey is P2SH but redeemScript missing')); (psbt as any).__CACHE.__TX.outs[0].script = payments.p2wsh({ redeem: { output: Buffer.from([0x51]) }, @@ -639,7 +639,7 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.outputHasPubkey(0, Buffer.from([])); - }, new RegExp('Incomplete script information')); + }, new RegExp('scriptPubkey or redeemScript is P2WSH but witnessScript missing')); (psbt as any).__CACHE.__TX.outs[0].script = payments.p2sh({ redeem: payments.p2wsh({ @@ -655,7 +655,7 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.outputHasPubkey(0, Buffer.from([])); - }, new RegExp('Incomplete script information')); + }, new RegExp('scriptPubkey or redeemScript is P2WSH but witnessScript missing')); delete psbt.data.outputs[0].redeemScript; @@ -665,7 +665,7 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.outputHasPubkey(0, Buffer.from([])); - }, new RegExp('Incomplete script information')); + }, new RegExp('scriptPubkey is P2SH but redeemScript missing')); psbt.updateOutput(0, { redeemScript: payments.p2wsh({ diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 31d15c7..7749827 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1587,7 +1587,7 @@ function pubkeyInInput( } else { throw new Error("Can't find pubkey in input without Utxo data"); } - const meaningfulScript = checkScripts( + const meaningfulScript = getMeaningfulScript( script, input.redeemScript, input.witnessScript, @@ -1602,7 +1602,7 @@ function pubkeyInOutput( cache: PsbtCache, ): boolean { const script = cache.__TX.outs[outputIndex].script; - const meaningfulScript = checkScripts( + const meaningfulScript = getMeaningfulScript( script, output.redeemScript, output.witnessScript, @@ -1610,52 +1610,49 @@ function pubkeyInOutput( return pubkeyInScript(pubkey, meaningfulScript); } -function checkScripts( +function getMeaningfulScript( script: Buffer, redeemScript?: Buffer, witnessScript?: Buffer, ): Buffer { - let fail = false; - if (isP2SHScript(script)) { - if (redeemScript === undefined) { - fail = true; - } else if (isP2WSHScript(redeemScript)) { - if (witnessScript === undefined) { - fail = true; - } else { - fail = !payments - .p2sh({ - redeem: payments.p2wsh({ - redeem: { output: witnessScript }, - }), - }) - .output!.equals(script); - if (!fail) return witnessScript; - } - } else { - fail = !payments - .p2sh({ - redeem: { output: redeemScript }, - }) - .output!.equals(script); - if (!fail) return redeemScript; - } - } else if (isP2WSHScript(script)) { - if (witnessScript === undefined) { - fail = true; - } else { - fail = !payments - .p2wsh({ - redeem: { output: witnessScript }, - }) - .output!.equals(script); - if (!fail) return witnessScript; - } + const { p2sh, p2wsh } = payments; + const isP2SH = isP2SHScript(script); + const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript); + const isP2WSH = isP2WSHScript(script); + + if (isP2SH && redeemScript === undefined) + throw new Error('scriptPubkey is P2SH but redeemScript missing'); + if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined) + throw new Error( + 'scriptPubkey or redeemScript is P2WSH but witnessScript missing', + ); + + let payment: payments.Payment; + let meaningfulScript: Buffer; + + if (isP2SHP2WSH) { + meaningfulScript = witnessScript!; + payment = p2sh({ redeem: p2wsh({ redeem: { output: meaningfulScript } }) }); + if (!payment.redeem!.output!.equals(redeemScript!)) + throw new Error('P2SHP2WSH witnessScript and redeemScript do not match'); + if (!payment.output!.equals(script!)) + throw new Error( + 'P2SHP2WSH witnessScript+redeemScript and scriptPubkey do not match', + ); + } else if (isP2WSH) { + meaningfulScript = witnessScript!; + payment = p2wsh({ redeem: { output: meaningfulScript } }); + if (!payment.output!.equals(script!)) + throw new Error('P2WSH witnessScript and scriptPubkey do not match'); + } else if (isP2SH) { + meaningfulScript = redeemScript!; + payment = p2sh({ redeem: { output: meaningfulScript } }); + if (!payment.output!.equals(script!)) + throw new Error('P2SH redeemScript and scriptPubkey do not match'); + } else { + meaningfulScript = script; } - if (fail) { - throw new Error('Incomplete script information'); - } - return script; + return meaningfulScript; } function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean { From 7d09fe5dcb8c8835570ff9341bc48c21d975307a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 28 Apr 2020 18:50:00 +0900 Subject: [PATCH 503/568] Refactor Psbt logic --- src/psbt.js | 164 ++++++++++++++++++++--------------------------- ts_src/psbt.ts | 169 +++++++++++++++++++++---------------------------- 2 files changed, 143 insertions(+), 190 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 91e0438..d240350 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -764,13 +764,13 @@ function checkTxInputCache(cache, input) { cache.__TX_IN_CACHE[key] = 1; } function scriptCheckerFactory(payment, paymentScriptName) { - return (inputIndex, scriptPubKey, redeemScript) => { + return (inputIndex, scriptPubKey, redeemScript, ioType) => { const redeemScriptOutput = payment({ redeem: { output: redeemScript }, }).output; if (!scriptPubKey.equals(redeemScriptOutput)) { throw new Error( - `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + `${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`, ); } }; @@ -877,7 +877,7 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) { ); } let hash; - let script; + let prevout; if (input.nonWitnessUtxo) { const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( cache, @@ -893,83 +893,51 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) { ); } const prevoutIndex = unsignedTx.ins[inputIndex].index; - const prevout = nonWitnessUtxoTx.outs[prevoutIndex]; - if (input.redeemScript) { - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - checkRedeemScript(inputIndex, prevout.script, input.redeemScript); - script = input.redeemScript; - } else { - script = prevout.script; - } - if (isP2WSHScript(script)) { - if (!input.witnessScript) - throw new Error('Segwit input needs witnessScript if not P2WPKH'); - checkWitnessScript(inputIndex, script, input.witnessScript); - hash = unsignedTx.hashForWitnessV0( - inputIndex, - input.witnessScript, - prevout.value, - sighashType, - ); - script = input.witnessScript; - } else if (isP2WPKH(script)) { - // P2WPKH uses the P2PKH template for prevoutScript when signing - const signingScript = payments.p2pkh({ hash: script.slice(2) }).output; - hash = unsignedTx.hashForWitnessV0( - inputIndex, - signingScript, - prevout.value, - sighashType, - ); - } else { - hash = unsignedTx.hashForSignature(inputIndex, script, sighashType); - } + prevout = nonWitnessUtxoTx.outs[prevoutIndex]; } else if (input.witnessUtxo) { - let _script; // so we don't shadow the `let script` above - if (input.redeemScript) { - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - checkRedeemScript( - inputIndex, - input.witnessUtxo.script, - input.redeemScript, - ); - _script = input.redeemScript; - } else { - _script = input.witnessUtxo.script; - } - if (isP2WPKH(_script)) { - // P2WPKH uses the P2PKH template for prevoutScript when signing - const signingScript = payments.p2pkh({ hash: _script.slice(2) }).output; - hash = unsignedTx.hashForWitnessV0( - inputIndex, - signingScript, - input.witnessUtxo.value, - sighashType, - ); - script = _script; - } else if (isP2WSHScript(_script)) { - if (!input.witnessScript) - throw new Error('Segwit input needs witnessScript if not P2WPKH'); - checkWitnessScript(inputIndex, _script, input.witnessScript); - hash = unsignedTx.hashForWitnessV0( - inputIndex, - input.witnessScript, - input.witnessUtxo.value, - sighashType, - ); - // want to make sure the script we return is the actual meaningful script - script = input.witnessScript; - } else { - throw new Error( - `Input #${inputIndex} has witnessUtxo but non-segwit script: ` + - `${_script.toString('hex')}`, - ); - } + prevout = input.witnessUtxo; } else { throw new Error('Need a Utxo input item for signing'); } + const { meaningfulScript, type } = getMeaningfulScript( + prevout.script, + inputIndex, + 'input', + input.redeemScript, + input.witnessScript, + ); + if (['p2shp2wsh', 'p2wsh'].indexOf(type) >= 0) { + hash = unsignedTx.hashForWitnessV0( + inputIndex, + meaningfulScript, + prevout.value, + sighashType, + ); + } else if (isP2WPKH(meaningfulScript)) { + // P2WPKH uses the P2PKH template for prevoutScript when signing + const signingScript = payments.p2pkh({ hash: meaningfulScript.slice(2) }) + .output; + hash = unsignedTx.hashForWitnessV0( + inputIndex, + signingScript, + prevout.value, + sighashType, + ); + } else { + // non-segwit + if (input.nonWitnessUtxo === undefined) + throw new Error( + `Input #${inputIndex} has witnessUtxo but non-segwit script: ` + + `${meaningfulScript.toString('hex')}`, + ); + hash = unsignedTx.hashForSignature( + inputIndex, + meaningfulScript, + sighashType, + ); + } return { - script, + script: meaningfulScript, sighashType, hash, }; @@ -1235,8 +1203,10 @@ function pubkeyInInput(pubkey, input, inputIndex, cache) { } else { throw new Error("Can't find pubkey in input without Utxo data"); } - const meaningfulScript = getMeaningfulScript( + const { meaningfulScript } = getMeaningfulScript( script, + inputIndex, + 'input', input.redeemScript, input.witnessScript, ); @@ -1244,15 +1214,22 @@ function pubkeyInInput(pubkey, input, inputIndex, cache) { } function pubkeyInOutput(pubkey, output, outputIndex, cache) { const script = cache.__TX.outs[outputIndex].script; - const meaningfulScript = getMeaningfulScript( + const { meaningfulScript } = getMeaningfulScript( script, + outputIndex, + 'output', output.redeemScript, output.witnessScript, ); return pubkeyInScript(pubkey, meaningfulScript); } -function getMeaningfulScript(script, redeemScript, witnessScript) { - const { p2sh, p2wsh } = payments; +function getMeaningfulScript( + script, + index, + ioType, + redeemScript, + witnessScript, +) { const isP2SH = isP2SHScript(script); const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript); const isP2WSH = isP2WSHScript(script); @@ -1262,31 +1239,30 @@ function getMeaningfulScript(script, redeemScript, witnessScript) { throw new Error( 'scriptPubkey or redeemScript is P2WSH but witnessScript missing', ); - let payment; let meaningfulScript; if (isP2SHP2WSH) { meaningfulScript = witnessScript; - payment = p2sh({ redeem: p2wsh({ redeem: { output: meaningfulScript } }) }); - if (!payment.redeem.output.equals(redeemScript)) - throw new Error('P2SHP2WSH witnessScript and redeemScript do not match'); - if (!payment.output.equals(script)) - throw new Error( - 'P2SHP2WSH witnessScript+redeemScript and scriptPubkey do not match', - ); + checkRedeemScript(index, script, redeemScript, ioType); + checkWitnessScript(index, redeemScript, witnessScript, ioType); } else if (isP2WSH) { meaningfulScript = witnessScript; - payment = p2wsh({ redeem: { output: meaningfulScript } }); - if (!payment.output.equals(script)) - throw new Error('P2WSH witnessScript and scriptPubkey do not match'); + checkWitnessScript(index, script, witnessScript, ioType); } else if (isP2SH) { meaningfulScript = redeemScript; - payment = p2sh({ redeem: { output: meaningfulScript } }); - if (!payment.output.equals(script)) - throw new Error('P2SH redeemScript and scriptPubkey do not match'); + checkRedeemScript(index, script, redeemScript, ioType); } else { meaningfulScript = script; } - return meaningfulScript; + return { + meaningfulScript, + type: isP2SHP2WSH + ? 'p2shp2wsh' + : isP2SH + ? 'p2sh' + : isP2WSH + ? 'p2wsh' + : 'raw', + }; } function pubkeyInScript(pubkey, script) { const pubkeyHash = crypto_1.hash160(pubkey); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 7749827..13e1286 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -984,11 +984,12 @@ function checkTxInputCache( function scriptCheckerFactory( payment: any, paymentScriptName: string, -): (idx: number, spk: Buffer, rs: Buffer) => void { +): (idx: number, spk: Buffer, rs: Buffer, ioType: 'input' | 'output') => void { return ( inputIndex: number, scriptPubKey: Buffer, redeemScript: Buffer, + ioType: 'input' | 'output', ): void => { const redeemScriptOutput = payment({ redeem: { output: redeemScript }, @@ -996,7 +997,7 @@ function scriptCheckerFactory( if (!scriptPubKey.equals(redeemScriptOutput)) { throw new Error( - `${paymentScriptName} for input #${inputIndex} doesn't match the scriptPubKey in the prevout`, + `${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`, ); } }; @@ -1158,7 +1159,7 @@ function getHashForSig( ); } let hash: Buffer; - let script: Buffer; + let prevout: Output; if (input.nonWitnessUtxo) { const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( @@ -1178,85 +1179,54 @@ function getHashForSig( } const prevoutIndex = unsignedTx.ins[inputIndex].index; - const prevout = nonWitnessUtxoTx.outs[prevoutIndex] as Output; - - if (input.redeemScript) { - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - checkRedeemScript(inputIndex, prevout.script, input.redeemScript); - script = input.redeemScript; - } else { - script = prevout.script; - } - - if (isP2WSHScript(script)) { - if (!input.witnessScript) - throw new Error('Segwit input needs witnessScript if not P2WPKH'); - checkWitnessScript(inputIndex, script, input.witnessScript); - hash = unsignedTx.hashForWitnessV0( - inputIndex, - input.witnessScript, - prevout.value, - sighashType, - ); - script = input.witnessScript; - } else if (isP2WPKH(script)) { - // P2WPKH uses the P2PKH template for prevoutScript when signing - const signingScript = payments.p2pkh({ hash: script.slice(2) }).output!; - hash = unsignedTx.hashForWitnessV0( - inputIndex, - signingScript, - prevout.value, - sighashType, - ); - } else { - hash = unsignedTx.hashForSignature(inputIndex, script, sighashType); - } + prevout = nonWitnessUtxoTx.outs[prevoutIndex] as Output; } else if (input.witnessUtxo) { - let _script: Buffer; // so we don't shadow the `let script` above - if (input.redeemScript) { - // If a redeemScript is provided, the scriptPubKey must be for that redeemScript - checkRedeemScript( - inputIndex, - input.witnessUtxo.script, - input.redeemScript, - ); - _script = input.redeemScript; - } else { - _script = input.witnessUtxo.script; - } - if (isP2WPKH(_script)) { - // P2WPKH uses the P2PKH template for prevoutScript when signing - const signingScript = payments.p2pkh({ hash: _script.slice(2) }).output!; - hash = unsignedTx.hashForWitnessV0( - inputIndex, - signingScript, - input.witnessUtxo.value, - sighashType, - ); - script = _script; - } else if (isP2WSHScript(_script)) { - if (!input.witnessScript) - throw new Error('Segwit input needs witnessScript if not P2WPKH'); - checkWitnessScript(inputIndex, _script, input.witnessScript); - hash = unsignedTx.hashForWitnessV0( - inputIndex, - input.witnessScript, - input.witnessUtxo.value, - sighashType, - ); - // want to make sure the script we return is the actual meaningful script - script = input.witnessScript; - } else { - throw new Error( - `Input #${inputIndex} has witnessUtxo but non-segwit script: ` + - `${_script.toString('hex')}`, - ); - } + prevout = input.witnessUtxo; } else { throw new Error('Need a Utxo input item for signing'); } + + const { meaningfulScript, type } = getMeaningfulScript( + prevout.script, + inputIndex, + 'input', + input.redeemScript, + input.witnessScript, + ); + + if (['p2shp2wsh', 'p2wsh'].indexOf(type) >= 0) { + hash = unsignedTx.hashForWitnessV0( + inputIndex, + meaningfulScript, + prevout.value, + sighashType, + ); + } else if (isP2WPKH(meaningfulScript)) { + // P2WPKH uses the P2PKH template for prevoutScript when signing + const signingScript = payments.p2pkh({ hash: meaningfulScript.slice(2) }) + .output!; + hash = unsignedTx.hashForWitnessV0( + inputIndex, + signingScript, + prevout.value, + sighashType, + ); + } else { + // non-segwit + if (input.nonWitnessUtxo === undefined) + throw new Error( + `Input #${inputIndex} has witnessUtxo but non-segwit script: ` + + `${meaningfulScript.toString('hex')}`, + ); + hash = unsignedTx.hashForSignature( + inputIndex, + meaningfulScript, + sighashType, + ); + } + return { - script, + script: meaningfulScript, sighashType, hash, }; @@ -1587,8 +1557,10 @@ function pubkeyInInput( } else { throw new Error("Can't find pubkey in input without Utxo data"); } - const meaningfulScript = getMeaningfulScript( + const { meaningfulScript } = getMeaningfulScript( script, + inputIndex, + 'input', input.redeemScript, input.witnessScript, ); @@ -1602,8 +1574,10 @@ function pubkeyInOutput( cache: PsbtCache, ): boolean { const script = cache.__TX.outs[outputIndex].script; - const meaningfulScript = getMeaningfulScript( + const { meaningfulScript } = getMeaningfulScript( script, + outputIndex, + 'output', output.redeemScript, output.witnessScript, ); @@ -1612,10 +1586,14 @@ function pubkeyInOutput( function getMeaningfulScript( script: Buffer, + index: number, + ioType: 'input' | 'output', redeemScript?: Buffer, witnessScript?: Buffer, -): Buffer { - const { p2sh, p2wsh } = payments; +): { + meaningfulScript: Buffer; + type: 'p2sh' | 'p2wsh' | 'p2shp2wsh' | 'raw'; +} { const isP2SH = isP2SHScript(script); const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript); const isP2WSH = isP2WSHScript(script); @@ -1627,32 +1605,31 @@ function getMeaningfulScript( 'scriptPubkey or redeemScript is P2WSH but witnessScript missing', ); - let payment: payments.Payment; let meaningfulScript: Buffer; if (isP2SHP2WSH) { meaningfulScript = witnessScript!; - payment = p2sh({ redeem: p2wsh({ redeem: { output: meaningfulScript } }) }); - if (!payment.redeem!.output!.equals(redeemScript!)) - throw new Error('P2SHP2WSH witnessScript and redeemScript do not match'); - if (!payment.output!.equals(script!)) - throw new Error( - 'P2SHP2WSH witnessScript+redeemScript and scriptPubkey do not match', - ); + checkRedeemScript(index, script, redeemScript!, ioType); + checkWitnessScript(index, redeemScript!, witnessScript!, ioType); } else if (isP2WSH) { meaningfulScript = witnessScript!; - payment = p2wsh({ redeem: { output: meaningfulScript } }); - if (!payment.output!.equals(script!)) - throw new Error('P2WSH witnessScript and scriptPubkey do not match'); + checkWitnessScript(index, script, witnessScript!, ioType); } else if (isP2SH) { meaningfulScript = redeemScript!; - payment = p2sh({ redeem: { output: meaningfulScript } }); - if (!payment.output!.equals(script!)) - throw new Error('P2SH redeemScript and scriptPubkey do not match'); + checkRedeemScript(index, script, redeemScript!, ioType); } else { meaningfulScript = script; } - return meaningfulScript; + return { + meaningfulScript, + type: isP2SHP2WSH + ? 'p2shp2wsh' + : isP2SH + ? 'p2sh' + : isP2WSH + ? 'p2wsh' + : 'raw', + }; } function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean { From 0c52803ba1fe5ae8dd3b84ad50bd84b0878d17de Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Tue, 28 Apr 2020 18:52:43 +0900 Subject: [PATCH 504/568] Add discouraged unsafe nonsegwit signing --- src/psbt.js | 35 +++++++++++++++++++++++++++++++++-- ts_src/psbt.ts | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index d240350..5958b1e 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -69,6 +69,14 @@ class Psbt { __NON_WITNESS_UTXO_BUF_CACHE: [], __TX_IN_CACHE: {}, __TX: this.data.globalMap.unsignedTx.tx, + // Old TransactionBuilder behavior was to not confirm input values + // before signing. Even though we highly encourage people to get + // the full parent transaction to verify values, the ability to + // sign non-segwit inputs without the full transaction was often + // requested. So the only way to activate is to use @ts-ignore. + // We will disable exporting the Psbt when unsafe sign is active. + // because it is not BIP174 compliant. + __UNSAFE_SIGN_NONSEGWIT: false, }; if (this.data.inputs.length === 0) this.setVersion(2); // Make data hidden when enumerating @@ -313,6 +321,7 @@ class Psbt { inputIndex, Object.assign({}, input, { sighashType: sig.hashType }), this.__CACHE, + true, ) : { hash: hashCache, script: scriptCache }; sighashCache = sig.hashType; @@ -513,12 +522,15 @@ class Psbt { }); } toBuffer() { + checkCache(this.__CACHE); return this.data.toBuffer(); } toHex() { + checkCache(this.__CACHE); return this.data.toHex(); } toBase64() { + checkCache(this.__CACHE); return this.data.toBase64(); } updateGlobal(updateData) { @@ -626,6 +638,11 @@ function canFinalize(input, script, scriptType) { return false; } } +function checkCache(cache) { + if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) { + throw new Error('Not BIP174 compliant, can not export'); + } +} function hasSigs(neededSigs, partialSig, pubkeys) { if (!partialSig) return false; let sigs; @@ -857,6 +874,7 @@ function getHashAndSighashType( inputIndex, input, cache, + false, sighashTypes, ); checkScriptForPubkey(pubkey, script, 'sign'); @@ -865,7 +883,7 @@ function getHashAndSighashType( sighashType, }; } -function getHashForSig(inputIndex, input, cache, sighashTypes) { +function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) { const unsignedTx = cache.__TX; const sighashType = input.sighashType || transaction_1.Transaction.SIGHASH_ALL; @@ -925,11 +943,24 @@ function getHashForSig(inputIndex, input, cache, sighashTypes) { ); } else { // non-segwit - if (input.nonWitnessUtxo === undefined) + if ( + input.nonWitnessUtxo === undefined && + cache.__UNSAFE_SIGN_NONSEGWIT === false + ) throw new Error( `Input #${inputIndex} has witnessUtxo but non-segwit script: ` + `${meaningfulScript.toString('hex')}`, ); + if (!forValidate && cache.__UNSAFE_SIGN_NONSEGWIT !== false) + console.warn( + 'Warning: Signing non-segwit inputs without the full parent transaction ' + + 'means there is a chance that a miner could feed you incorrect information ' + + 'to trick you into paying large fees. This behavior is the same as the old ' + + 'TransactionBuilder class when signing non-segwit scripts. You are not ' + + 'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' + + 'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' + + '*********************', + ); hash = unsignedTx.hashForSignature( inputIndex, meaningfulScript, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 13e1286..23bdc1d 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -115,6 +115,14 @@ export class Psbt { __NON_WITNESS_UTXO_BUF_CACHE: [], __TX_IN_CACHE: {}, __TX: (this.data.globalMap.unsignedTx as PsbtTransaction).tx, + // Old TransactionBuilder behavior was to not confirm input values + // before signing. Even though we highly encourage people to get + // the full parent transaction to verify values, the ability to + // sign non-segwit inputs without the full transaction was often + // requested. So the only way to activate is to use @ts-ignore. + // We will disable exporting the Psbt when unsafe sign is active. + // because it is not BIP174 compliant. + __UNSAFE_SIGN_NONSEGWIT: false, }; if (this.data.inputs.length === 0) this.setVersion(2); @@ -386,6 +394,7 @@ export class Psbt { inputIndex, Object.assign({}, input, { sighashType: sig.hashType }), this.__CACHE, + true, ) : { hash: hashCache!, script: scriptCache! }; sighashCache = sig.hashType; @@ -619,14 +628,17 @@ export class Psbt { } toBuffer(): Buffer { + checkCache(this.__CACHE); return this.data.toBuffer(); } toHex(): string { + checkCache(this.__CACHE); return this.data.toHex(); } toBase64(): string { + checkCache(this.__CACHE); return this.data.toBase64(); } @@ -681,6 +693,7 @@ interface PsbtCache { __FEE_RATE?: number; __FEE?: number; __EXTRACTED_TX?: Transaction; + __UNSAFE_SIGN_NONSEGWIT: boolean; } interface PsbtOptsOptional { @@ -825,6 +838,12 @@ function canFinalize( } } +function checkCache(cache: PsbtCache): void { + if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) { + throw new Error('Not BIP174 compliant, can not export'); + } +} + function hasSigs( neededSigs: number, partialSig?: any[], @@ -1130,6 +1149,7 @@ function getHashAndSighashType( inputIndex, input, cache, + false, sighashTypes, ); checkScriptForPubkey(pubkey, script, 'sign'); @@ -1143,6 +1163,7 @@ function getHashForSig( inputIndex: number, input: PsbtInput, cache: PsbtCache, + forValidate: boolean, sighashTypes?: number[], ): { script: Buffer; @@ -1213,11 +1234,24 @@ function getHashForSig( ); } else { // non-segwit - if (input.nonWitnessUtxo === undefined) + if ( + input.nonWitnessUtxo === undefined && + cache.__UNSAFE_SIGN_NONSEGWIT === false + ) throw new Error( `Input #${inputIndex} has witnessUtxo but non-segwit script: ` + `${meaningfulScript.toString('hex')}`, ); + if (!forValidate && cache.__UNSAFE_SIGN_NONSEGWIT !== false) + console.warn( + 'Warning: Signing non-segwit inputs without the full parent transaction ' + + 'means there is a chance that a miner could feed you incorrect information ' + + 'to trick you into paying large fees. This behavior is the same as the old ' + + 'TransactionBuilder class when signing non-segwit scripts. You are not ' + + 'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' + + 'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' + + '*********************', + ); hash = unsignedTx.hashForSignature( inputIndex, meaningfulScript, From d02483473b26f9fd9f24fe11bd1c31d1bb9bc90d Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Tue, 28 Apr 2020 19:58:05 +0700 Subject: [PATCH 505/568] allocUnsafe for faster buffer cloning It's safe to do this because we immediately overwrite the entire buffer. No need to zero out first. --- src/bufferutils.js | 2 +- ts_src/bufferutils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bufferutils.js b/src/bufferutils.js index 2ee3542..a68fd31 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -42,7 +42,7 @@ function reverseBuffer(buffer) { } exports.reverseBuffer = reverseBuffer; function cloneBuffer(buffer) { - const clone = Buffer.alloc(buffer.length); + const clone = Buffer.allocUnsafe(buffer.length); buffer.copy(clone); return clone; } diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 2025f88..9005f2a 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -49,7 +49,7 @@ export function reverseBuffer(buffer: Buffer): Buffer { } export function cloneBuffer(buffer: Buffer): Buffer { - const clone = Buffer.alloc(buffer.length); + const clone = Buffer.allocUnsafe(buffer.length); buffer.copy(clone); return clone; } From c9f399e509c5375580e9c196e3c2c5eeb59aa053 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 29 Apr 2020 11:05:33 +0900 Subject: [PATCH 506/568] Add getInputType --- src/psbt.js | 37 ++++++++++++++--- test/fixtures/psbt.json | 18 +++++++++ test/psbt.spec.ts | 89 +++++++++++++++++++++++++++++++++++++++++ ts_src/psbt.ts | 79 +++++++++++++++++++++++++++++++----- types/psbt.d.ts | 2 + 5 files changed, 208 insertions(+), 17 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 5958b1e..7cab795 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -189,6 +189,7 @@ class Psbt { ); } checkInputsForPartialSig(this.data.inputs, 'addInput'); + if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript); const c = this.__CACHE; this.data.addInput(inputData); const txIn = c.__TX.ins[c.__TX.ins.length - 1]; @@ -285,6 +286,20 @@ class Psbt { this.data.clearFinalizedInput(inputIndex); return this; } + getInputType(inputIndex) { + const input = utils_1.checkForInput(this.data.inputs, inputIndex); + const script = getScriptFromUtxo(inputIndex, input, this.__CACHE); + const result = getMeaningfulScript( + script, + inputIndex, + 'input', + input.redeemScript, + input.witnessScript, + ); + const type = result.type === 'raw' ? '' : result.type + '-'; + const mainType = classifyScript(result.meaningfulScript); + return type + mainType; + } inputHasPubkey(inputIndex, pubkey) { const input = utils_1.checkForInput(this.data.inputs, inputIndex); return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE); @@ -538,6 +553,7 @@ class Psbt { return this; } updateInput(inputIndex, updateData) { + if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript); this.data.updateInput(inputIndex, updateData); if (updateData.nonWitnessUtxo) { addNonWitnessTxCache( @@ -924,7 +940,7 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) { input.redeemScript, input.witnessScript, ); - if (['p2shp2wsh', 'p2wsh'].indexOf(type) >= 0) { + if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) { hash = unsignedTx.hashForWitnessV0( inputIndex, meaningfulScript, @@ -1220,20 +1236,22 @@ function nonWitnessUtxoTxFromCache(cache, input, inputIndex) { } return c[inputIndex]; } -function pubkeyInInput(pubkey, input, inputIndex, cache) { - let script; +function getScriptFromUtxo(inputIndex, input, cache) { if (input.witnessUtxo !== undefined) { - script = input.witnessUtxo.script; + return input.witnessUtxo.script; } else if (input.nonWitnessUtxo !== undefined) { const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( cache, input, inputIndex, ); - script = nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script; + return nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script; } else { throw new Error("Can't find pubkey in input without Utxo data"); } +} +function pubkeyInInput(pubkey, input, inputIndex, cache) { + const script = getScriptFromUtxo(inputIndex, input, cache); const { meaningfulScript } = getMeaningfulScript( script, inputIndex, @@ -1275,9 +1293,11 @@ function getMeaningfulScript( meaningfulScript = witnessScript; checkRedeemScript(index, script, redeemScript, ioType); checkWitnessScript(index, redeemScript, witnessScript, ioType); + checkInvalidP2WSH(meaningfulScript); } else if (isP2WSH) { meaningfulScript = witnessScript; checkWitnessScript(index, script, witnessScript, ioType); + checkInvalidP2WSH(meaningfulScript); } else if (isP2SH) { meaningfulScript = redeemScript; checkRedeemScript(index, script, redeemScript, ioType); @@ -1287,7 +1307,7 @@ function getMeaningfulScript( return { meaningfulScript, type: isP2SHP2WSH - ? 'p2shp2wsh' + ? 'p2sh-p2wsh' : isP2SH ? 'p2sh' : isP2WSH @@ -1295,6 +1315,11 @@ function getMeaningfulScript( : 'raw', }; } +function checkInvalidP2WSH(script) { + if (isP2WPKH(script) || isP2SHScript(script)) { + throw new Error('P2WPKH or P2SH can not be contained within P2WSH'); + } +} function pubkeyInScript(pubkey, script) { const pubkeyHash = crypto_1.hash160(pubkey); const decompiled = bscript.decompile(script); diff --git a/test/fixtures/psbt.json b/test/fixtures/psbt.json index e3062e8..0e51d57 100644 --- a/test/fixtures/psbt.json +++ b/test/fixtures/psbt.json @@ -313,6 +313,24 @@ }, "exception": "Invalid arguments for Psbt\\.addInput\\. Requires single object with at least \\[hash\\] and \\[index\\]" }, + { + "description": "checks for invalid p2wsh witnessScript", + "inputData": { + "hash": "Buffer.from('000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f', 'hex')", + "index": 0, + "witnessScript": "Buffer.from('0014000102030405060708090a0b0c0d0e0f00010203', 'hex')" + }, + "exception": "P2WPKH or P2SH can not be contained within P2WSH" + }, + { + "description": "checks for invalid p2wsh witnessScript", + "inputData": { + "hash": "Buffer.from('000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f', 'hex')", + "index": 0, + "witnessScript": "Buffer.from('a914000102030405060708090a0b0c0d0e0f0001020387', 'hex')" + }, + "exception": "P2WPKH or P2SH can not be contained within P2WSH" + }, { "description": "should be equal", "inputData": { diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index ff2131b..5e88fe0 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -542,6 +542,95 @@ describe(`Psbt`, () => { }); }); + describe('getInputType', () => { + const { publicKey } = ECPair.makeRandom(); + const p2wpkhPub = (pubkey: Buffer): Buffer => + payments.p2wpkh({ + pubkey, + }).output!; + const p2pkhPub = (pubkey: Buffer): Buffer => + payments.p2pkh({ + pubkey, + }).output!; + const p2shOut = (output: Buffer): Buffer => + payments.p2sh({ + redeem: { output }, + }).output!; + const p2wshOut = (output: Buffer): Buffer => + payments.p2wsh({ + redeem: { output }, + }).output!; + const p2shp2wshOut = (output: Buffer): Buffer => p2shOut(p2wshOut(output)); + const noOuter = (output: Buffer): Buffer => output; + + function getInputTypeTest({ + innerScript, + outerScript, + redeemGetter, + witnessGetter, + expectedType, + }: any): void { + const psbt = new Psbt(); + psbt.addInput({ + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, + witnessUtxo: { + script: outerScript(innerScript(publicKey)), + value: 2e3, + }, + ...(redeemGetter ? { redeemScript: redeemGetter(publicKey) } : {}), + ...(witnessGetter ? { witnessScript: witnessGetter(publicKey) } : {}), + }); + const type = psbt.getInputType(0); + assert.strictEqual(type, expectedType, 'incorrect input type'); + } + [ + { + innerScript: p2pkhPub, + outerScript: noOuter, + redeemGetter: null, + witnessGetter: null, + expectedType: 'pubkeyhash', + }, + { + innerScript: p2wpkhPub, + outerScript: noOuter, + redeemGetter: null, + witnessGetter: null, + expectedType: 'witnesspubkeyhash', + }, + { + innerScript: p2pkhPub, + outerScript: p2shOut, + redeemGetter: p2pkhPub, + witnessGetter: null, + expectedType: 'p2sh-pubkeyhash', + }, + { + innerScript: p2wpkhPub, + outerScript: p2shOut, + redeemGetter: p2wpkhPub, + witnessGetter: null, + expectedType: 'p2sh-witnesspubkeyhash', + }, + { + innerScript: p2pkhPub, + outerScript: p2wshOut, + redeemGetter: null, + witnessGetter: p2pkhPub, + expectedType: 'p2wsh-pubkeyhash', + }, + { + innerScript: p2pkhPub, + outerScript: p2shp2wshOut, + redeemGetter: (pk: Buffer): Buffer => p2wshOut(p2pkhPub(pk)), + witnessGetter: p2pkhPub, + expectedType: 'p2sh-p2wsh-pubkeyhash', + }, + ].forEach(getInputTypeTest); + }); + describe('inputHasPubkey', () => { it('should throw', () => { const psbt = new Psbt(); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 23bdc1d..cb14fc5 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -242,6 +242,7 @@ export class Psbt { ); } checkInputsForPartialSig(this.data.inputs, 'addInput'); + if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript); const c = this.__CACHE; this.data.addInput(inputData); const txIn = c.__TX.ins[c.__TX.ins.length - 1]; @@ -355,6 +356,21 @@ export class Psbt { return this; } + getInputType(inputIndex: number): AllScriptType { + const input = checkForInput(this.data.inputs, inputIndex); + const script = getScriptFromUtxo(inputIndex, input, this.__CACHE); + const result = getMeaningfulScript( + script, + inputIndex, + 'input', + input.redeemScript, + input.witnessScript, + ); + const type = result.type === 'raw' ? '' : result.type + '-'; + const mainType = classifyScript(result.meaningfulScript); + return (type + mainType) as AllScriptType; + } + inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean { const input = checkForInput(this.data.inputs, inputIndex); return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE); @@ -648,6 +664,7 @@ export class Psbt { } updateInput(inputIndex: number, updateData: PsbtInputUpdate): this { + if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript); this.data.updateInput(inputIndex, updateData); if (updateData.nonWitnessUtxo) { addNonWitnessTxCache( @@ -1215,7 +1232,7 @@ function getHashForSig( input.witnessScript, ); - if (['p2shp2wsh', 'p2wsh'].indexOf(type) >= 0) { + if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) { hash = unsignedTx.hashForWitnessV0( inputIndex, meaningfulScript, @@ -1572,25 +1589,32 @@ function nonWitnessUtxoTxFromCache( return c[inputIndex]; } -function pubkeyInInput( - pubkey: Buffer, - input: PsbtInput, +function getScriptFromUtxo( inputIndex: number, + input: PsbtInput, cache: PsbtCache, -): boolean { - let script: Buffer; +): Buffer { if (input.witnessUtxo !== undefined) { - script = input.witnessUtxo.script; + return input.witnessUtxo.script; } else if (input.nonWitnessUtxo !== undefined) { const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache( cache, input, inputIndex, ); - script = nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script; + return nonWitnessUtxoTx.outs[cache.__TX.ins[inputIndex].index].script; } else { throw new Error("Can't find pubkey in input without Utxo data"); } +} + +function pubkeyInInput( + pubkey: Buffer, + input: PsbtInput, + inputIndex: number, + cache: PsbtCache, +): boolean { + const script = getScriptFromUtxo(inputIndex, input, cache); const { meaningfulScript } = getMeaningfulScript( script, inputIndex, @@ -1626,7 +1650,7 @@ function getMeaningfulScript( witnessScript?: Buffer, ): { meaningfulScript: Buffer; - type: 'p2sh' | 'p2wsh' | 'p2shp2wsh' | 'raw'; + type: 'p2sh' | 'p2wsh' | 'p2sh-p2wsh' | 'raw'; } { const isP2SH = isP2SHScript(script); const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript); @@ -1645,9 +1669,11 @@ function getMeaningfulScript( meaningfulScript = witnessScript!; checkRedeemScript(index, script, redeemScript!, ioType); checkWitnessScript(index, redeemScript!, witnessScript!, ioType); + checkInvalidP2WSH(meaningfulScript); } else if (isP2WSH) { meaningfulScript = witnessScript!; checkWitnessScript(index, script, witnessScript!, ioType); + checkInvalidP2WSH(meaningfulScript); } else if (isP2SH) { meaningfulScript = redeemScript!; checkRedeemScript(index, script, redeemScript!, ioType); @@ -1657,7 +1683,7 @@ function getMeaningfulScript( return { meaningfulScript, type: isP2SHP2WSH - ? 'p2shp2wsh' + ? 'p2sh-p2wsh' : isP2SH ? 'p2sh' : isP2WSH @@ -1666,6 +1692,12 @@ function getMeaningfulScript( }; } +function checkInvalidP2WSH(script: Buffer): void { + if (isP2WPKH(script) || isP2SHScript(script)) { + throw new Error('P2WPKH or P2SH can not be contained within P2WSH'); + } +} + function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean { const pubkeyHash = hash160(pubkey); @@ -1678,7 +1710,32 @@ function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean { }); } -function classifyScript(script: Buffer): string { +type AllScriptType = + | 'witnesspubkeyhash' + | 'pubkeyhash' + | 'multisig' + | 'pubkey' + | 'nonstandard' + | 'p2sh-witnesspubkeyhash' + | 'p2sh-pubkeyhash' + | 'p2sh-multisig' + | 'p2sh-pubkey' + | 'p2sh-nonstandard' + | 'p2wsh-pubkeyhash' + | 'p2wsh-multisig' + | 'p2wsh-pubkey' + | 'p2wsh-nonstandard' + | 'p2sh-p2wsh-pubkeyhash' + | 'p2sh-p2wsh-multisig' + | 'p2sh-p2wsh-pubkey' + | 'p2sh-p2wsh-nonstandard'; +type ScriptType = + | 'witnesspubkeyhash' + | 'pubkeyhash' + | 'multisig' + | 'pubkey' + | 'nonstandard'; +function classifyScript(script: Buffer): ScriptType { if (isP2WPKH(script)) return 'witnesspubkeyhash'; if (isP2PKH(script)) return 'pubkeyhash'; if (isP2MS(script)) return 'multisig'; diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 127ef0f..4d1c099 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -69,6 +69,7 @@ export declare class Psbt { getFee(): number; finalizeAllInputs(): this; finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this; + getInputType(inputIndex: number): AllScriptType; inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean; outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean; validateSignaturesOfAllInputs(): boolean; @@ -151,4 +152,5 @@ isP2WSH: boolean) => { finalScriptSig: Buffer | undefined; finalScriptWitness: Buffer | undefined; }; +declare type AllScriptType = 'witnesspubkeyhash' | 'pubkeyhash' | 'multisig' | 'pubkey' | 'nonstandard' | 'p2sh-witnesspubkeyhash' | 'p2sh-pubkeyhash' | 'p2sh-multisig' | 'p2sh-pubkey' | 'p2sh-nonstandard' | 'p2wsh-pubkeyhash' | 'p2wsh-multisig' | 'p2wsh-pubkey' | 'p2wsh-nonstandard' | 'p2sh-p2wsh-pubkeyhash' | 'p2sh-p2wsh-multisig' | 'p2sh-p2wsh-pubkey' | 'p2sh-p2wsh-nonstandard'; export {}; From 5d19abfb85641887060804a2a396eacd6fea9117 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 29 Apr 2020 13:32:57 +0900 Subject: [PATCH 507/568] Add ability to get redeemScript|witnessScript from finalized scripts --- src/psbt.js | 35 +++++++++++++++++++++++++++++++++-- test/psbt.spec.ts | 34 ++++++++++++++++++++++------------ ts_src/psbt.ts | 43 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 96 insertions(+), 16 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 7cab795..b80951f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -293,8 +293,9 @@ class Psbt { script, inputIndex, 'input', - input.redeemScript, - input.witnessScript, + input.redeemScript || redeemFromFinalScriptSig(input.finalScriptSig), + input.witnessScript || + redeemFromFinalWitnessScript(input.finalScriptWitness), ); const type = result.type === 'raw' ? '' : result.type + '-'; const mainType = classifyScript(result.meaningfulScript); @@ -1272,6 +1273,36 @@ function pubkeyInOutput(pubkey, output, outputIndex, cache) { ); return pubkeyInScript(pubkey, meaningfulScript); } +function redeemFromFinalScriptSig(finalScript) { + if (!finalScript) return; + const decomp = bscript.decompile(finalScript); + if (!decomp) return; + const lastItem = decomp[decomp.length - 1]; + if ( + !Buffer.isBuffer(lastItem) || + isPubkeyLike(lastItem) || + isSigLike(lastItem) + ) + return; + const sDecomp = bscript.decompile(lastItem); + if (!sDecomp) return; + return lastItem; +} +function redeemFromFinalWitnessScript(finalScript) { + if (!finalScript) return; + const decomp = scriptWitnessToWitnessStack(finalScript); + const lastItem = decomp[decomp.length - 1]; + if (isPubkeyLike(lastItem)) return; + const sDecomp = bscript.decompile(lastItem); + if (!sDecomp) return; + return lastItem; +} +function isPubkeyLike(buf) { + return buf.length === 33 && bscript.isCanonicalPubKey(buf); +} +function isSigLike(buf) { + return bscript.isCanonicalScriptSignature(buf); +} function getMeaningfulScript( script, index, diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 5e88fe0..a5a2214 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -543,7 +543,8 @@ describe(`Psbt`, () => { }); describe('getInputType', () => { - const { publicKey } = ECPair.makeRandom(); + const key = ECPair.makeRandom(); + const { publicKey } = key; const p2wpkhPub = (pubkey: Buffer): Buffer => payments.p2wpkh({ pubkey, @@ -569,19 +570,26 @@ describe(`Psbt`, () => { redeemGetter, witnessGetter, expectedType, + finalize, }: any): void { const psbt = new Psbt(); - psbt.addInput({ - hash: - '0000000000000000000000000000000000000000000000000000000000000000', - index: 0, - witnessUtxo: { - script: outerScript(innerScript(publicKey)), - value: 2e3, - }, - ...(redeemGetter ? { redeemScript: redeemGetter(publicKey) } : {}), - ...(witnessGetter ? { witnessScript: witnessGetter(publicKey) } : {}), - }); + psbt + .addInput({ + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, + witnessUtxo: { + script: outerScript(innerScript(publicKey)), + value: 2e3, + }, + ...(redeemGetter ? { redeemScript: redeemGetter(publicKey) } : {}), + ...(witnessGetter ? { witnessScript: witnessGetter(publicKey) } : {}), + }) + .addOutput({ + script: Buffer.from('0014d85c2b71d0060b09c9886aeb815e50991dda124d'), + value: 1800, + }); + if (finalize) psbt.signInput(0, key).finalizeInput(0); const type = psbt.getInputType(0); assert.strictEqual(type, expectedType, 'incorrect input type'); } @@ -613,6 +621,7 @@ describe(`Psbt`, () => { redeemGetter: p2wpkhPub, witnessGetter: null, expectedType: 'p2sh-witnesspubkeyhash', + finalize: true, }, { innerScript: p2pkhPub, @@ -620,6 +629,7 @@ describe(`Psbt`, () => { redeemGetter: null, witnessGetter: p2pkhPub, expectedType: 'p2wsh-pubkeyhash', + finalize: true, }, { innerScript: p2pkhPub, diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index cb14fc5..39d3a4c 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -363,8 +363,9 @@ export class Psbt { script, inputIndex, 'input', - input.redeemScript, - input.witnessScript, + input.redeemScript || redeemFromFinalScriptSig(input.finalScriptSig), + input.witnessScript || + redeemFromFinalWitnessScript(input.finalScriptWitness), ); const type = result.type === 'raw' ? '' : result.type + '-'; const mainType = classifyScript(result.meaningfulScript); @@ -1642,6 +1643,44 @@ function pubkeyInOutput( return pubkeyInScript(pubkey, meaningfulScript); } +function redeemFromFinalScriptSig( + finalScript: Buffer | undefined, +): Buffer | undefined { + if (!finalScript) return; + const decomp = bscript.decompile(finalScript); + if (!decomp) return; + const lastItem = decomp[decomp.length - 1]; + if ( + !Buffer.isBuffer(lastItem) || + isPubkeyLike(lastItem) || + isSigLike(lastItem) + ) + return; + const sDecomp = bscript.decompile(lastItem); + if (!sDecomp) return; + return lastItem; +} + +function redeemFromFinalWitnessScript( + finalScript: Buffer | undefined, +): Buffer | undefined { + if (!finalScript) return; + const decomp = scriptWitnessToWitnessStack(finalScript); + const lastItem = decomp[decomp.length - 1]; + if (isPubkeyLike(lastItem)) return; + const sDecomp = bscript.decompile(lastItem); + if (!sDecomp) return; + return lastItem; +} + +function isPubkeyLike(buf: Buffer): boolean { + return buf.length === 33 && bscript.isCanonicalPubKey(buf); +} + +function isSigLike(buf: Buffer): boolean { + return bscript.isCanonicalScriptSignature(buf); +} + function getMeaningfulScript( script: Buffer, index: number, From f87a20caa7828f6f8c29049c73efd369ff81c57b Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 29 Apr 2020 14:39:50 +0900 Subject: [PATCH 508/568] Add hasHDKey --- src/psbt.js | 21 ++++++++++++++++++ test/psbt.spec.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++++ ts_src/psbt.ts | 27 +++++++++++++++++++++++ types/psbt.d.ts | 2 ++ 4 files changed, 105 insertions(+) diff --git a/src/psbt.js b/src/psbt.js index b80951f..693bfc3 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -305,10 +305,24 @@ class Psbt { const input = utils_1.checkForInput(this.data.inputs, inputIndex); return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE); } + inputHasHDKey(inputIndex, root) { + const input = utils_1.checkForInput(this.data.inputs, inputIndex); + const derivationIsMine = bip32DerivationIsMine(root); + return ( + !!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine) + ); + } outputHasPubkey(outputIndex, pubkey) { const output = utils_1.checkForOutput(this.data.outputs, outputIndex); return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE); } + outputHasHDKey(outputIndex, root) { + const output = utils_1.checkForOutput(this.data.outputs, outputIndex); + const derivationIsMine = bip32DerivationIsMine(root); + return ( + !!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine) + ); + } validateSignaturesOfAllInputs() { utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one const results = range(this.data.inputs.length).map(idx => @@ -696,6 +710,13 @@ const isP2PKH = isPaymentFactory(payments.p2pkh); const isP2WPKH = isPaymentFactory(payments.p2wpkh); const isP2WSHScript = isPaymentFactory(payments.p2wsh); const isP2SHScript = isPaymentFactory(payments.p2sh); +function bip32DerivationIsMine(root) { + return d => { + if (!d.masterFingerprint.equals(root.fingerprint)) return false; + if (!root.derivePath(d.path).publicKey.equals(d.pubkey)) return false; + return true; + }; +} function check32Bit(num) { if ( typeof num !== 'number' || diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index a5a2214..c33e9cf 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,4 +1,5 @@ import * as assert from 'assert'; +import * as crypto from 'crypto'; import { describe, it } from 'mocha'; import { bip32, ECPair, networks as NETWORKS, payments, Psbt } from '..'; @@ -641,6 +642,29 @@ describe(`Psbt`, () => { ].forEach(getInputTypeTest); }); + describe('inputHasHDKey', () => { + it('should return true if HD key is present', () => { + const root = bip32.fromSeed(crypto.randomBytes(32)); + const root2 = bip32.fromSeed(crypto.randomBytes(32)); + const path = "m/0'/0"; + const psbt = new Psbt(); + psbt.addInput({ + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, + bip32Derivation: [ + { + masterFingerprint: root.fingerprint, + path, + pubkey: root.derivePath(path).publicKey, + }, + ], + }); + assert.strictEqual(psbt.inputHasHDKey(0, root), true); + assert.strictEqual(psbt.inputHasHDKey(0, root2), false); + }); + }); + describe('inputHasPubkey', () => { it('should throw', () => { const psbt = new Psbt(); @@ -712,6 +736,37 @@ describe(`Psbt`, () => { }); }); + describe('outputHasHDKey', () => { + it('should return true if HD key is present', () => { + const root = bip32.fromSeed(crypto.randomBytes(32)); + const root2 = bip32.fromSeed(crypto.randomBytes(32)); + const path = "m/0'/0"; + const psbt = new Psbt(); + psbt + .addInput({ + hash: + '0000000000000000000000000000000000000000000000000000000000000000', + index: 0, + }) + .addOutput({ + script: Buffer.from( + '0014000102030405060708090a0b0c0d0e0f00010203', + 'hex', + ), + value: 2000, + bip32Derivation: [ + { + masterFingerprint: root.fingerprint, + path, + pubkey: root.derivePath(path).publicKey, + }, + ], + }); + assert.strictEqual(psbt.outputHasHDKey(0, root), true); + assert.strictEqual(psbt.outputHasHDKey(0, root2), false); + }); + }); + describe('outputHasPubkey', () => { it('should throw', () => { const psbt = new Psbt(); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 39d3a4c..8f06e15 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,6 +1,7 @@ import { Psbt as PsbtBase } from 'bip174'; import * as varuint from 'bip174/src/lib/converter/varint'; import { + Bip32Derivation, KeyValue, PartialSig, PsbtGlobalUpdate, @@ -377,11 +378,27 @@ export class Psbt { return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE); } + inputHasHDKey(inputIndex: number, root: HDSigner): boolean { + const input = checkForInput(this.data.inputs, inputIndex); + const derivationIsMine = bip32DerivationIsMine(root); + return ( + !!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine) + ); + } + outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean { const output = checkForOutput(this.data.outputs, outputIndex); return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE); } + outputHasHDKey(outputIndex: number, root: HDSigner): boolean { + const output = checkForOutput(this.data.outputs, outputIndex); + const derivationIsMine = bip32DerivationIsMine(root); + return ( + !!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine) + ); + } + validateSignaturesOfAllInputs(): boolean { checkForInput(this.data.inputs, 0); // making sure we have at least one const results = range(this.data.inputs.length).map(idx => @@ -905,6 +922,16 @@ const isP2WPKH = isPaymentFactory(payments.p2wpkh); const isP2WSHScript = isPaymentFactory(payments.p2wsh); const isP2SHScript = isPaymentFactory(payments.p2sh); +function bip32DerivationIsMine( + root: HDSigner, +): (d: Bip32Derivation) => boolean { + return (d: Bip32Derivation): boolean => { + if (!d.masterFingerprint.equals(root.fingerprint)) return false; + if (!root.derivePath(d.path).publicKey.equals(d.pubkey)) return false; + return true; + }; +} + function check32Bit(num: number): void { if ( typeof num !== 'number' || diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 4d1c099..eb239dc 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -71,7 +71,9 @@ export declare class Psbt { finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this; getInputType(inputIndex: number): AllScriptType; inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean; + inputHasHDKey(inputIndex: number, root: HDSigner): boolean; outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean; + outputHasHDKey(outputIndex: number, root: HDSigner): boolean; validateSignaturesOfAllInputs(): boolean; validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean; signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; From 25b5806cf146ef5d5f5770c60f102a7b37bcf660 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 21 May 2020 11:11:12 +0900 Subject: [PATCH 509/568] Throw errors when p2wsh or p2wpkh contain uncompressed pubkeys. This will enforce BIP143 compressed pubkey rules on an address generation level. --- src/payments/p2wpkh.js | 4 ++- src/payments/p2wsh.js | 34 ++++++++++++++++++----- test/fixtures/p2wpkh.json | 17 ++++++++++++ test/fixtures/p2wsh.json | 24 +++++++++++++++++ test/fixtures/transaction_builder.json | 4 +-- ts_src/payments/p2wpkh.ts | 4 ++- ts_src/payments/p2wsh.ts | 37 +++++++++++++++++++++----- 7 files changed, 107 insertions(+), 17 deletions(-) diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index b32e808..0ba4a51 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -107,12 +107,14 @@ function p2wpkh(a, opts) { if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch'); else hash = pkh; + if (!ecc.isPoint(a.pubkey) || a.pubkey.length !== 33) + throw new TypeError('Invalid pubkey for p2wpkh'); } if (a.witness) { if (a.witness.length !== 2) throw new TypeError('Witness is invalid'); if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Witness has invalid signature'); - if (!ecc.isPoint(a.witness[1])) + if (!ecc.isPoint(a.witness[1]) || a.witness[1].length !== 33) throw new TypeError('Witness has invalid pubkey'); if (a.signature && !a.signature.equals(a.witness[0])) throw new TypeError('Signature mismatch'); diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index 6a4aa24..1177f3c 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -6,6 +6,7 @@ const bscript = require('../script'); const lazy = require('./lazy'); const typef = require('typeforce'); const OPS = bscript.OPS; +const ecc = require('tiny-secp256k1'); const bech32 = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); function stacksEqual(a, b) { @@ -14,6 +15,14 @@ function stacksEqual(a, b) { return x.equals(b[i]); }); } +function chunkHasUncompressedPubkey(chunk) { + if (Buffer.isBuffer(chunk) && chunk.length === 65) { + if (ecc.isPoint(chunk)) return true; + else return false; + } else { + return false; + } +} // input: <> // witness: [redeemScriptSig ...] {redeemScript} // output: OP_0 {sha256(redeemScript)} @@ -51,6 +60,9 @@ function p2wsh(a, opts) { const _rchunks = lazy.value(() => { return bscript.decompile(a.redeem.input); }); + const _rochunks = lazy.value(() => { + return bscript.decompile(a.redeem.output); + }); let network = a.network; if (!network) { network = (a.redeem && a.redeem.network) || networks_1.bitcoin; @@ -166,14 +178,24 @@ function p2wsh(a, opts) { !stacksEqual(a.witness, a.redeem.witness) ) throw new TypeError('Witness and redeem.witness mismatch'); - } - if (a.witness) { if ( - a.redeem && - a.redeem.output && - !a.redeem.output.equals(a.witness[a.witness.length - 1]) - ) + (a.redeem.input && _rchunks().some(chunkHasUncompressedPubkey)) || + (a.redeem.output && _rochunks().some(chunkHasUncompressedPubkey)) + ) { + throw new TypeError( + 'redeem.input or redeem.output contains uncompressed pubkey', + ); + } + } + if (a.witness && a.witness.length > 0) { + const wScript = a.witness[a.witness.length - 1]; + if (a.redeem && a.redeem.output && !a.redeem.output.equals(wScript)) throw new TypeError('Witness and redeem.output mismatch'); + if ( + a.witness.some(chunkHasUncompressedPubkey) || + (bscript.decompile(wScript) || []).some(chunkHasUncompressedPubkey) + ) + throw new TypeError('Witness contains uncompressed pubkey'); } } return Object.assign(o, a); diff --git a/test/fixtures/p2wpkh.json b/test/fixtures/p2wpkh.json index 4d62b60..057ba81 100644 --- a/test/fixtures/p2wpkh.json +++ b/test/fixtures/p2wpkh.json @@ -113,6 +113,23 @@ "output": "OP_RESERVED ea6d525c0c955d90d3dbd29a81ef8bfb79003727" } }, + { + "exception": "Invalid pubkey for p2wpkh", + "description": "Uncompressed pubkey in pubkey", + "arguments": { + "pubkey": "049fa211b0fca342589ca381cc96520c3d0e3924832158d9f29891936cac091e80687acdca51868ee1f89a3bb36bb16f186262938e1f94c1e7692924935b9b1457" + } + }, + { + "exception": "Witness has invalid pubkey", + "description": "Uncompressed pubkey in witness", + "arguments": { + "witness": [ + "300602010002010001", + "049fa211b0fca342589ca381cc96520c3d0e3924832158d9f29891936cac091e80687acdca51868ee1f89a3bb36bb16f186262938e1f94c1e7692924935b9b1457" + ] + } + }, { "exception": "Pubkey mismatch", "options": {}, diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json index 0350fe9..0f60112 100644 --- a/test/fixtures/p2wsh.json +++ b/test/fixtures/p2wsh.json @@ -344,6 +344,30 @@ } } }, + { + "exception": "redeem.input or redeem.output contains uncompressed pubkey", + "arguments": { + "redeem": { + "output": "049fa211b0fca342589ca381cc96520c3d0e3924832158d9f29891936cac091e80687acdca51868ee1f89a3bb36bb16f186262938e1f94c1e7692924935b9b1457 OP_CHECKSIG" + } + } + }, + { + "exception": "redeem.input or redeem.output contains uncompressed pubkey", + "arguments": { + "redeem": { + "input": "049fa211b0fca342589ca381cc96520c3d0e3924832158d9f29891936cac091e80687acdca51868ee1f89a3bb36bb16f186262938e1f94c1e7692924935b9b1457" + } + } + }, + { + "exception": "Witness contains uncompressed pubkey", + "arguments": { + "witness": [ + "049fa211b0fca342589ca381cc96520c3d0e3924832158d9f29891936cac091e80687acdca51868ee1f89a3bb36bb16f186262938e1f94c1e7692924935b9b1457" + ] + } + }, { "exception": "Invalid prefix or Network mismatch", "arguments": { diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 07226b0..1564e6a 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -2122,7 +2122,7 @@ }, { "description": "Transaction w/ P2WSH(P2PK), signing with uncompressed public key", - "exception": "BIP143 rejects uncompressed public keys in P2WPKH or P2WSH", + "exception": "redeem.input or redeem.output contains uncompressed pubkey", "inputs": [ { "txId": "2fddebc1a7e67e04fc6b77645ae9ae10eeaa35e168606587d79b031ebca33345", @@ -2148,7 +2148,7 @@ }, { "description": "Transaction w/ P2SH(P2WSH(P2PK)), signing with uncompressed public key", - "exception": "BIP143 rejects uncompressed public keys in P2WPKH or P2WSH", + "exception": "redeem.input or redeem.output contains uncompressed pubkey", "inputs": [ { "txId": "2fddebc1a7e67e04fc6b77645ae9ae10eeaa35e168606587d79b031ebca33345", diff --git a/ts_src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts index fc2a458..27c61c9 100644 --- a/ts_src/payments/p2wpkh.ts +++ b/ts_src/payments/p2wpkh.ts @@ -118,13 +118,15 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch'); else hash = pkh; + if (!ecc.isPoint(a.pubkey) || a.pubkey.length !== 33) + throw new TypeError('Invalid pubkey for p2wpkh'); } if (a.witness) { if (a.witness.length !== 2) throw new TypeError('Witness is invalid'); if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Witness has invalid signature'); - if (!ecc.isPoint(a.witness[1])) + if (!ecc.isPoint(a.witness[1]) || a.witness[1].length !== 33) throw new TypeError('Witness has invalid pubkey'); if (a.signature && !a.signature.equals(a.witness[0])) diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 132dde4..173767d 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -1,10 +1,11 @@ import * as bcrypto from '../crypto'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; -import { Payment, PaymentOpts, StackFunction } from './index'; +import { Payment, PaymentOpts, StackElement, StackFunction } from './index'; import * as lazy from './lazy'; const typef = require('typeforce'); const OPS = bscript.OPS; +const ecc = require('tiny-secp256k1'); const bech32 = require('bech32'); @@ -18,6 +19,15 @@ function stacksEqual(a: Buffer[], b: Buffer[]): boolean { }); } +function chunkHasUncompressedPubkey(chunk: StackElement): boolean { + if (Buffer.isBuffer(chunk) && chunk.length === 65) { + if (ecc.isPoint(chunk)) return true; + else return false; + } else { + return false; + } +} + // input: <> // witness: [redeemScriptSig ...] {redeemScript} // output: OP_0 {sha256(redeemScript)} @@ -59,6 +69,9 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { const _rchunks = lazy.value(() => { return bscript.decompile(a.redeem!.input!); }) as StackFunction; + const _rochunks = lazy.value(() => { + return bscript.decompile(a.redeem!.output!); + }) as StackFunction; let network = a.network; if (!network) { @@ -187,15 +200,25 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { !stacksEqual(a.witness, a.redeem.witness) ) throw new TypeError('Witness and redeem.witness mismatch'); + if ( + (a.redeem.input && _rchunks().some(chunkHasUncompressedPubkey)) || + (a.redeem.output && _rochunks().some(chunkHasUncompressedPubkey)) + ) { + throw new TypeError( + 'redeem.input or redeem.output contains uncompressed pubkey', + ); + } } - if (a.witness) { - if ( - a.redeem && - a.redeem.output && - !a.redeem.output.equals(a.witness[a.witness.length - 1]) - ) + if (a.witness && a.witness.length > 0) { + const wScript = a.witness[a.witness.length - 1]; + if (a.redeem && a.redeem.output && !a.redeem.output.equals(wScript)) throw new TypeError('Witness and redeem.output mismatch'); + if ( + a.witness.some(chunkHasUncompressedPubkey) || + (bscript.decompile(wScript) || []).some(chunkHasUncompressedPubkey) + ) + throw new TypeError('Witness contains uncompressed pubkey'); } } From c2d8d19c616e816c418f1283579010218a523fe9 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 21 May 2020 12:54:49 +0900 Subject: [PATCH 510/568] Simplify chunkHasUncompressedPubkey and remove lazy load of output script --- src/payments/p2wsh.js | 18 +++++++++++------- ts_src/payments/p2wsh.ts | 18 +++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index 1177f3c..f9ae90b 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -16,9 +16,13 @@ function stacksEqual(a, b) { }); } function chunkHasUncompressedPubkey(chunk) { - if (Buffer.isBuffer(chunk) && chunk.length === 65) { - if (ecc.isPoint(chunk)) return true; - else return false; + if ( + Buffer.isBuffer(chunk) && + chunk.length === 65 && + chunk[0] === 0x04 && + ecc.isPoint(chunk) + ) { + return true; } else { return false; } @@ -60,9 +64,6 @@ function p2wsh(a, opts) { const _rchunks = lazy.value(() => { return bscript.decompile(a.redeem.input); }); - const _rochunks = lazy.value(() => { - return bscript.decompile(a.redeem.output); - }); let network = a.network; if (!network) { network = (a.redeem && a.redeem.network) || networks_1.bitcoin; @@ -180,7 +181,10 @@ function p2wsh(a, opts) { throw new TypeError('Witness and redeem.witness mismatch'); if ( (a.redeem.input && _rchunks().some(chunkHasUncompressedPubkey)) || - (a.redeem.output && _rochunks().some(chunkHasUncompressedPubkey)) + (a.redeem.output && + (bscript.decompile(a.redeem.output) || []).some( + chunkHasUncompressedPubkey, + )) ) { throw new TypeError( 'redeem.input or redeem.output contains uncompressed pubkey', diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 173767d..a72be94 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -20,9 +20,13 @@ function stacksEqual(a: Buffer[], b: Buffer[]): boolean { } function chunkHasUncompressedPubkey(chunk: StackElement): boolean { - if (Buffer.isBuffer(chunk) && chunk.length === 65) { - if (ecc.isPoint(chunk)) return true; - else return false; + if ( + Buffer.isBuffer(chunk) && + chunk.length === 65 && + chunk[0] === 0x04 && + ecc.isPoint(chunk) + ) { + return true; } else { return false; } @@ -69,9 +73,6 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { const _rchunks = lazy.value(() => { return bscript.decompile(a.redeem!.input!); }) as StackFunction; - const _rochunks = lazy.value(() => { - return bscript.decompile(a.redeem!.output!); - }) as StackFunction; let network = a.network; if (!network) { @@ -202,7 +203,10 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { throw new TypeError('Witness and redeem.witness mismatch'); if ( (a.redeem.input && _rchunks().some(chunkHasUncompressedPubkey)) || - (a.redeem.output && _rochunks().some(chunkHasUncompressedPubkey)) + (a.redeem.output && + (bscript.decompile(a.redeem.output) || []).some( + chunkHasUncompressedPubkey, + )) ) { throw new TypeError( 'redeem.input or redeem.output contains uncompressed pubkey', From bb89297919882d12e3e35749006d94935d95f20e Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 21 May 2020 13:09:06 +0900 Subject: [PATCH 511/568] 5.1.8 --- CHANGELOG.md | 10 ++++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fecfc19..81884f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 5.1.8 +__fixed__ +- Throw errors when p2wsh or p2wpkh contain uncompressed pubkeys (#1573) + +__added__ +- Add txInputs and txOutputs for Psbt (#1561) + +__changed__ +- (Not exposed) Added BufferWriter to help ease maintenance of certain forks of this library (#1533) + # 5.1.7 __fixed__ - Fixed Transaction class Output interface typing for TypeScript (#1506) diff --git a/package-lock.json b/package-lock.json index ae2d378..b0c46ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.7", + "version": "5.1.8", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e94b78b..52b667c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.7", + "version": "5.1.8", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From c0718a9f7c41f8235e46e2d015ada2c32021e0c7 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 22 May 2020 08:14:30 +0900 Subject: [PATCH 512/568] Actually sort pubkeys in test --- test/integration/transactions.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts index 34feec5..9c98bb4 100644 --- a/test/integration/transactions.spec.ts +++ b/test/integration/transactions.spec.ts @@ -649,7 +649,7 @@ function createPayment(_type: string, myKeys?: any[], network?: any): any { if (type.slice(0, 4) === 'p2ms') { payment = bitcoin.payments.p2ms({ m, - pubkeys: keys.map(key => key.publicKey).sort(), + pubkeys: keys.map(key => key.publicKey).sort((a, b) => a.compare(b)), network, }); } else if (['p2sh', 'p2wsh'].indexOf(type) > -1) { From 48967652cac325c621edf556249a59329277fe7e Mon Sep 17 00:00:00 2001 From: Luke Childs <lukechilds123@gmail.com> Date: Fri, 29 May 2020 13:40:53 +0700 Subject: [PATCH 513/568] Handle non-standard output types in Psbt.txOutputs --- CHANGELOG.md | 4 ++++ src/psbt.js | 16 +++++++++++----- ts_src/psbt.ts | 16 +++++++++++----- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81884f6..f19cc62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 5.1.9 +__fixed__ +- Fixed errors for psbt.txOutputs getter (#1578) + # 5.1.8 __fixed__ - Throw errors when p2wsh or p2wpkh contain uncompressed pubkeys (#1573) diff --git a/src/psbt.js b/src/psbt.js index 134be87..bb41899 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -117,11 +117,17 @@ class Psbt { })); } get txOutputs() { - return this.__CACHE.__TX.outs.map(output => ({ - script: bufferutils_1.cloneBuffer(output.script), - value: output.value, - address: address_1.fromOutputScript(output.script, this.opts.network), - })); + return this.__CACHE.__TX.outs.map(output => { + let address; + try { + address = address_1.fromOutputScript(output.script, this.opts.network); + } catch (_) {} + return { + script: bufferutils_1.cloneBuffer(output.script), + value: output.value, + address, + }; + }); } combine(...those) { this.data.combine(...those.map(o => o.data)); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 89491b9..a09ee0c 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -155,11 +155,17 @@ export class Psbt { } get txOutputs(): TransactionOutput[] { - return this.__CACHE.__TX.outs.map(output => ({ - script: cloneBuffer(output.script), - value: output.value, - address: fromOutputScript(output.script, this.opts.network), - })); + return this.__CACHE.__TX.outs.map(output => { + let address; + try { + address = fromOutputScript(output.script, this.opts.network); + } catch (_) {} + return { + script: cloneBuffer(output.script), + value: output.value, + address, + }; + }); } combine(...those: Psbt[]): this { From afba17ee5d9c9a80b2c48fabedfa7f35f36fd56f Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 29 May 2020 15:59:58 +0900 Subject: [PATCH 514/568] 5.1.9 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b0c46ab..3f0beda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.8", + "version": "5.1.9", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 52b667c..c4e0458 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.8", + "version": "5.1.9", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 65d7603cf77e5b01462154f6f302c85840eeec37 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sun, 31 May 2020 10:13:16 +0900 Subject: [PATCH 515/568] Fix signInputAsync when SignerAsync rejects Co-authored-by: Zhang Zengbo <zengbo.zhang@gmail.com> --- src/psbt.js | 7 ++-- test/psbt.spec.ts | 81 +++++++++++++++++++++++++++++++++++++++-------- ts_src/psbt.ts | 43 ++++++++++++------------- 3 files changed, 91 insertions(+), 40 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index bb41899..13bbef1 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -488,9 +488,9 @@ class Psbt { keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], ) { - return new Promise((resolve, reject) => { + return Promise.resolve().then(() => { if (!keyPair || !keyPair.publicKey) - return reject(new Error('Need Signer to sign input')); + throw new Error('Need Signer to sign input'); const { hash, sighashType } = getHashAndSighashType( this.data.inputs, inputIndex, @@ -498,7 +498,7 @@ class Psbt { this.__CACHE, sighashTypes, ); - Promise.resolve(keyPair.sign(hash)).then(signature => { + return Promise.resolve(keyPair.sign(hash)).then(signature => { const partialSig = [ { pubkey: keyPair.publicKey, @@ -506,7 +506,6 @@ class Psbt { }, ]; this.data.updateInput(inputIndex, { partialSig }); - resolve(); }); }); } diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index e0eba81..da35dbf 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,7 +1,14 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; -import { bip32, ECPair, networks as NETWORKS, Psbt } from '..'; +import { + bip32, + ECPair, + networks as NETWORKS, + Psbt, + Signer, + SignerAsync, +} from '..'; import * as preFixtures from './fixtures/psbt.json'; @@ -22,6 +29,40 @@ const fixtures = initBuffers(preFixtures); const upperCaseFirstLetter = (str: string): string => str.replace(/^./, s => s.toUpperCase()); +const toAsyncSigner = (signer: Signer): SignerAsync => { + const ret: SignerAsync = { + publicKey: signer.publicKey, + sign: (hash: Buffer, lowerR: boolean | undefined): Promise<Buffer> => { + return new Promise( + (resolve, rejects): void => { + setTimeout(() => { + try { + const r = signer.sign(hash, lowerR); + resolve(r); + } catch (e) { + rejects(e); + } + }, 10); + }, + ); + }, + }; + return ret; +}; +const failedAsyncSigner = (publicKey: Buffer): SignerAsync => { + return { + publicKey, + sign: (__: Buffer): Promise<Buffer> => { + return new Promise( + (_, reject): void => { + setTimeout(() => { + reject(new Error('sign failed')); + }, 10); + }, + ); + }, + }; +}; // const b = (hex: string) => Buffer.from(hex, 'hex'); describe(`Psbt`, () => { @@ -164,25 +205,39 @@ describe(`Psbt`, () => { it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { await psbtThatShouldsign.signInputAsync( f.shouldSign.inputToCheck, ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, ); }); + await assert.rejects(async () => { + await psbtThatShouldsign.signInputAsync( + f.shouldSign.inputToCheck, + failedAsyncSigner(ECPair.fromWIF(f.shouldSign.WIF).publicKey), + f.shouldSign.sighashTypes || undefined, + ); + }, /sign failed/); } if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); - assert.rejects(async () => { + await assert.rejects(async () => { await psbtThatShouldThrow.signInputAsync( f.shouldThrow.inputToCheck, ECPair.fromWIF(f.shouldThrow.WIF), (f.shouldThrow as any).sighashTypes || undefined, ); }, new RegExp(f.shouldThrow.errorMessage)); - assert.rejects(async () => { + await assert.rejects(async () => { + await psbtThatShouldThrow.signInputAsync( + f.shouldThrow.inputToCheck, + toAsyncSigner(ECPair.fromWIF(f.shouldThrow.WIF)), + (f.shouldThrow as any).sighashTypes || undefined, + ); + }, new RegExp(f.shouldThrow.errorMessage)); + await assert.rejects(async () => { await (psbtThatShouldThrow.signInputAsync as any)( f.shouldThrow.inputToCheck, ); @@ -229,7 +284,7 @@ describe(`Psbt`, () => { it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { await psbtThatShouldsign.signAllInputsAsync( ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, @@ -239,13 +294,13 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); - assert.rejects(async () => { + await assert.rejects(async () => { await psbtThatShouldThrow.signAllInputsAsync( ECPair.fromWIF(f.shouldThrow.WIF), (f.shouldThrow as any).sighashTypes || undefined, ); }, new RegExp('No inputs were signed')); - assert.rejects(async () => { + await assert.rejects(async () => { await (psbtThatShouldThrow.signAllInputsAsync as any)(); }, new RegExp('Need Signer to sign input')); } @@ -288,7 +343,7 @@ describe(`Psbt`, () => { it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { await psbtThatShouldsign.signInputHDAsync( f.shouldSign.inputToCheck, bip32.fromBase58(f.shouldSign.xprv), @@ -299,14 +354,14 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); - assert.rejects(async () => { + await assert.rejects(async () => { await psbtThatShouldThrow.signInputHDAsync( f.shouldThrow.inputToCheck, bip32.fromBase58(f.shouldThrow.xprv), (f.shouldThrow as any).sighashTypes || undefined, ); }, new RegExp(f.shouldThrow.errorMessage)); - assert.rejects(async () => { + await assert.rejects(async () => { await (psbtThatShouldThrow.signInputHDAsync as any)( f.shouldThrow.inputToCheck, ); @@ -354,7 +409,7 @@ describe(`Psbt`, () => { it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { await psbtThatShouldsign.signAllInputsHDAsync( bip32.fromBase58(f.shouldSign.xprv), (f.shouldSign as any).sighashTypes || undefined, @@ -364,13 +419,13 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); - assert.rejects(async () => { + await assert.rejects(async () => { await psbtThatShouldThrow.signAllInputsHDAsync( bip32.fromBase58(f.shouldThrow.xprv), (f.shouldThrow as any).sighashTypes || undefined, ); }, new RegExp('No inputs were signed')); - assert.rejects(async () => { + await assert.rejects(async () => { await (psbtThatShouldThrow.signAllInputsHDAsync as any)(); }, new RegExp('Need HDSigner to sign input')); } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index a09ee0c..b1f83cd 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -580,31 +580,28 @@ export class Psbt { keyPair: Signer | SignerAsync, sighashTypes: number[] = [Transaction.SIGHASH_ALL], ): Promise<void> { - return new Promise( - (resolve, reject): void => { - if (!keyPair || !keyPair.publicKey) - return reject(new Error('Need Signer to sign input')); - const { hash, sighashType } = getHashAndSighashType( - this.data.inputs, - inputIndex, - keyPair.publicKey, - this.__CACHE, - sighashTypes, - ); + return Promise.resolve().then(() => { + if (!keyPair || !keyPair.publicKey) + throw new Error('Need Signer to sign input'); + const { hash, sighashType } = getHashAndSighashType( + this.data.inputs, + inputIndex, + keyPair.publicKey, + this.__CACHE, + sighashTypes, + ); - Promise.resolve(keyPair.sign(hash)).then(signature => { - const partialSig = [ - { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(signature, sighashType), - }, - ]; + return Promise.resolve(keyPair.sign(hash)).then(signature => { + const partialSig = [ + { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(signature, sighashType), + }, + ]; - this.data.updateInput(inputIndex, { partialSig }); - resolve(); - }); - }, - ); + this.data.updateInput(inputIndex, { partialSig }); + }); + }); } toBuffer(): Buffer { From 0fb04aefa1e1989f6bfc35268702b734e1a461d5 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 1 Jun 2020 15:59:01 +0900 Subject: [PATCH 516/568] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f19cc62..2e9eae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 5.1.10 +__fixed__ +- Fixed psbt.signInputAsync (and consequentially all Async signing methods) not handling rejection of keypair.sign properly (#1582) + # 5.1.9 __fixed__ - Fixed errors for psbt.txOutputs getter (#1578) From 91bb1e91aed97c4a31476c576d1810baeabdc055 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 1 Jun 2020 15:59:06 +0900 Subject: [PATCH 517/568] 5.1.10 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f0beda..9edaa53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.9", + "version": "5.1.10", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c4e0458..5b765c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.9", + "version": "5.1.10", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From e7ef9715d105ea6e9cb8312f04ce574e8866004c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 30 Jul 2020 18:33:48 +0900 Subject: [PATCH 518/568] Fix vulns from npm audit --- package-lock.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9edaa53..d9a67fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -750,9 +750,9 @@ "dev": true }, "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -1342,10 +1342,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" }, "lodash.flattendeep": { "version": "4.4.0", From 62bb17b04562f06333ceba301d124ac877ba4c2e Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Mon, 31 Aug 2020 15:57:09 +0300 Subject: [PATCH 519/568] Separate readUInt64LE fixtures from writeUInt64LE; add two more tests: "n < 0" & "0 < n < 1" --- test/bufferutils.spec.ts | 2 +- test/fixtures/bufferutils.json | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/test/bufferutils.spec.ts b/test/bufferutils.spec.ts index b8f86ab..213b156 100644 --- a/test/bufferutils.spec.ts +++ b/test/bufferutils.spec.ts @@ -42,7 +42,7 @@ describe('bufferutils', () => { }); }); - fixtures.invalid.readUInt64LE.forEach(f => { + fixtures.invalid.writeUInt64LE.forEach(f => { it('throws on ' + f.description, () => { const buffer = Buffer.alloc(8, 0); diff --git a/test/fixtures/bufferutils.json b/test/fixtures/bufferutils.json index 42c145d..0f046bc 100644 --- a/test/fixtures/bufferutils.json +++ b/test/fixtures/bufferutils.json @@ -71,6 +71,32 @@ "hex": "0100000000002000", "dec": 9007199254740993 } + ], + "writeUInt64LE": [ + { + "description": "n === 2^53", + "exception": "RangeError: value out of range", + "hex": "0000000000002000", + "dec": 9007199254740992 + }, + { + "description": "n > 2^53", + "exception": "RangeError: value out of range", + "hex": "0100000000002000", + "dec": 9007199254740993 + }, + { + "description": "n < 0", + "exception": "specified a negative value for writing an unsigned value", + "hex": "", + "dec": -1 + }, + { + "description": "0 < n < 1", + "exception": "value has a fractional component", + "hex": "", + "dec": 0.1 + } ] } } From bc9b5abb7c1e089cca3265e38c31d46846d96f25 Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Mon, 31 Aug 2020 17:09:08 +0300 Subject: [PATCH 520/568] Throw Error if no private key found when calling toWIF() --- test/ecpair.spec.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/ecpair.spec.ts b/test/ecpair.spec.ts index b9b8538..0b54ecc 100644 --- a/test/ecpair.spec.ts +++ b/test/ecpair.spec.ts @@ -146,6 +146,13 @@ describe('ECPair', () => { assert.strictEqual(result, f.WIF); }); }); + it('throws if no private key is found', () => { + assert.throws(() => { + const keyPair = ECPair.makeRandom(); + delete (keyPair as any).__D; + keyPair.toWIF(); + }, /Missing private key/); + }); }); describe('makeRandom', () => { From 883d021117136867974b3fcb2bcfad10c15d9ff5 Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Tue, 1 Sep 2020 14:58:43 +0300 Subject: [PATCH 521/568] cover the case when some chunks are "minimalOP" --- test/script.spec.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/script.spec.ts b/test/script.spec.ts index 95a339d..fe5fa63 100644 --- a/test/script.spec.ts +++ b/test/script.spec.ts @@ -41,6 +41,21 @@ describe('script', () => { }); }); + describe('toASM', () => { + const OP_RETURN = bscript.OPS.OP_RETURN; + it('encodes empty buffer as OP_0', () => { + const chunks = [OP_RETURN, Buffer.from([])]; + assert.strictEqual(bscript.toASM(chunks), 'OP_RETURN OP_0'); + }); + + for (let i = 1; i <= 16; i++) { + it(`encodes one byte buffer [${i}] as OP_${i}`, () => { + const chunks = [OP_RETURN, Buffer.from([i])]; + assert.strictEqual(bscript.toASM(chunks), 'OP_RETURN OP_' + i); + }); + } + }); + describe('fromASM/toASM (templates)', () => { fixtures2.valid.forEach(f => { if (f.inputHex) { From ec17b06bde6095d3856d51d8a7b7317dd36690c7 Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Tue, 1 Sep 2020 15:45:34 +0300 Subject: [PATCH 522/568] cover 'fromBech32(address)' throwing exception & bad 'decodeBech32.data.length' --- test/fixtures/address.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/fixtures/address.json b/test/fixtures/address.json index 2668a08..1b428d9 100644 --- a/test/fixtures/address.json +++ b/test/fixtures/address.json @@ -189,6 +189,14 @@ { "exception": "has no matching Script", "address": "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2" + }, + { + "exception": "has no matching Script", + "address": "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5" + }, + { + "exception": "has no matching Script", + "address": "bc1qqqqqqqqqqv9qus" } ] } From 0148d40f880674fe2171808a31bbed48dbf6337a Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Wed, 2 Sep 2020 10:08:31 +0300 Subject: [PATCH 523/568] add unit tests for UInt31 and BIP32Path --- test/types.spec.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/types.spec.ts b/test/types.spec.ts index 363b83c..1be3170 100644 --- a/test/types.spec.ts +++ b/test/types.spec.ts @@ -54,4 +54,41 @@ describe('types', () => { }); }); }); + + describe('UInt31', () => { + const UINT31_MAX = Math.pow(2, 31) - 1; + it('return true for valid values', () => { + assert.strictEqual(types.UInt31(0), true); + assert.strictEqual(types.UInt31(1000), true); + assert.strictEqual(types.UInt31(UINT31_MAX), true); + }); + + it('return false for negative values', () => { + assert.strictEqual(types.UInt31(-1), false); + assert.strictEqual(types.UInt31(-UINT31_MAX), false); + }); + + it('return false for values > UINT31_MAX', () => { + assert.strictEqual(types.UInt31(UINT31_MAX + 1), false); + }); + }); + + describe('BIP32Path', () => { + it('return true for valid paths', () => { + assert.strictEqual(types.BIP32Path("m/0'/0'"), true); + assert.strictEqual(types.BIP32Path("m/0'/0"), true); + assert.strictEqual(types.BIP32Path("m/0'/1'/2'/3/4'"), true); + }); + + it('return false for invalid paths', () => { + assert.strictEqual(types.BIP32Path('m'), false); + assert.strictEqual(types.BIP32Path("n/0'/0'"), false); + assert.strictEqual(types.BIP32Path("m/0'/x"), false); + }); + + it('return "BIP32 derivation path" for JSON.strigify()', () => { + const toJsonValue = JSON.stringify(types.BIP32Path); + assert.equal(toJsonValue, '"BIP32 derivation path"'); + }); + }); }); From 970b554896231d93652aca95384fa05c300461fd Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Wed, 2 Sep 2020 11:15:11 +0300 Subject: [PATCH 524/568] improve test name - show actual value for UINT31_MAX --- test/types.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/types.spec.ts b/test/types.spec.ts index 1be3170..478fd99 100644 --- a/test/types.spec.ts +++ b/test/types.spec.ts @@ -68,7 +68,7 @@ describe('types', () => { assert.strictEqual(types.UInt31(-UINT31_MAX), false); }); - it('return false for values > UINT31_MAX', () => { + it(`return false for value > ${UINT31_MAX}`, () => { assert.strictEqual(types.UInt31(UINT31_MAX + 1), false); }); }); From b77b1a06534ae92edcab8c6eb5c9bc3e29c2e96f Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Wed, 2 Sep 2020 11:16:43 +0300 Subject: [PATCH 525/568] add unit tests for three uncovered cases --- test/fixtures/embed.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/fixtures/embed.json b/test/fixtures/embed.json index 40e43cd..3ab2ddf 100644 --- a/test/fixtures/embed.json +++ b/test/fixtures/embed.json @@ -44,6 +44,36 @@ "arguments": { "output": "OP_1 OP_2 OP_ADD" } + }, + { + "description": "Return value and data do not match", + "exception": "Data mismatch", + "options": {}, + "arguments": { + "output": "OP_RETURN a3b147dbe4a85579fc4b5a1811e76620560e07267e62b9a0d6858f9127735cadd82f67e06c24dbc4", + "data": [ + "a3b147dbe4a85579fc4b5a1855555555555555555555555555555555555555555555555555555555" + ] + } + }, + { + "description": "Script length incorrect", + "exception": "Data mismatch", + "options": {}, + "arguments": { + "output": "OP_RETURN a3b1 47db", + "data": [ + "a3b1" + ] + } + }, + { + "description": "Return data is not buffer", + "exception": "Output is invalid", + "options": {}, + "arguments": { + "output": "OP_RETURN OP_1" + } } ], "dynamic": { From c63721ca8f1cbfb5f102ded30d15189c5e3cf1b3 Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Mon, 7 Sep 2020 10:56:06 +0300 Subject: [PATCH 526/568] Add tests for the exceptions: 'Input and witness provided' and 'Non push-only scriptSig' --- test/fixtures/p2sh.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/fixtures/p2sh.json b/test/fixtures/p2sh.json index 21b63cc..b2a7607 100644 --- a/test/fixtures/p2sh.json +++ b/test/fixtures/p2sh.json @@ -323,6 +323,26 @@ } } }, + { + "exception": "Input and witness provided", + "arguments": { + "redeem": { + "input": "OP_0", + "witness": [ + "030000000000000000000000000000000000000000000000000000000000000001" + ] + } + } + }, + { + "exception": "Non push-only scriptSig", + "arguments": { + "redeem": { + "input": "OP_RETURN", + "output": "OP_1" + } + } + }, { "exception": "Redeem.output too short", "arguments": { From bae1d362445507918cb628ca04a1c550d0da396f Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Mon, 7 Sep 2020 13:49:10 +0300 Subject: [PATCH 527/568] add test for "Signature mismatch" --- test/fixtures/p2wpkh.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/fixtures/p2wpkh.json b/test/fixtures/p2wpkh.json index 057ba81..ad0d595 100644 --- a/test/fixtures/p2wpkh.json +++ b/test/fixtures/p2wpkh.json @@ -141,6 +141,16 @@ ] } }, + { + "exception": "Signature mismatch", + "arguments": { + "signature": "300602010002010002", + "witness": [ + "300602010002010001", + "030000000000000000000000000000000000000000000000000000000000000001" + ] + } + }, { "exception": "Invalid prefix or Network mismatch", "arguments": { From 3a54c738176a04ad8cc5e08944cb07bb171a87de Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 12 Sep 2020 00:19:21 +0900 Subject: [PATCH 528/568] Update bip174 dep --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index d9a67fe..5b53bb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -367,9 +367,9 @@ } }, "bip174": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-1.0.1.tgz", - "integrity": "sha512-Mq2aFs1TdMfxBpYPg7uzjhsiXbAtoVq44TNjEWtvuZBiBgc3m7+n55orYMtTAxdg7jWbL4DtH0MKocJER4xERQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.0.1.tgz", + "integrity": "sha512-i3X26uKJOkDTAalYAp0Er+qGMDhrbbh2o93/xiPyAN2s25KrClSpe3VXo/7mNJoqA5qfko8rLS2l3RWZgYmjKQ==" }, "bip32": { "version": "2.0.4", @@ -1344,7 +1344,8 @@ "lodash": { "version": "4.17.19", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "dev": true }, "lodash.flattendeep": { "version": "4.4.0", diff --git a/package.json b/package.json index 5b765c6..b45735b 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ ], "dependencies": { "bech32": "^1.1.2", - "bip174": "^1.0.1", + "bip174": "^2.0.1", "bip32": "^2.0.4", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", From 5e3442b74be523f511bcee362c184d91d5f44331 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 12 Sep 2020 00:35:57 +0900 Subject: [PATCH 529/568] Fix txOutputs --- ts_src/psbt.ts | 6 +++--- types/psbt.d.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d4d886f..c55e6bc 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -32,8 +32,8 @@ export interface PsbtTxInput extends TransactionInput { hash: Buffer; } -export interface PsbtTxOutput extends Output { - address: string; +export interface PsbtTxOutput extends TransactionOutput { + address: string | undefined; } /** @@ -171,7 +171,7 @@ export class Psbt { })); } - get txOutputs(): TransactionOutput[] { + get txOutputs(): PsbtTxOutput[] { return this.__CACHE.__TX.outs.map(output => { let address; try { diff --git a/types/psbt.d.ts b/types/psbt.d.ts index eb239dc..022a95d 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,13 +1,13 @@ import { Psbt as PsbtBase } from 'bip174'; -import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; +import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; -import { Output, Transaction } from './transaction'; +import { Transaction } from './transaction'; export interface PsbtTxInput extends TransactionInput { hash: Buffer; } -export interface PsbtTxOutput extends Output { - address: string; +export interface PsbtTxOutput extends TransactionOutput { + address: string | undefined; } /** * Psbt class can parse and generate a PSBT binary based off of the BIP174. From 7aaef308e0b1c9dddd7ba8049780e59ecb4f6f8c Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 12 Sep 2020 00:49:05 +0900 Subject: [PATCH 530/568] 5.2.0 --- CHANGELOG.md | 7 +++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e9eae7..a4e1eb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 5.2.0 +__changed__ +- Updated PSBT to allow for witnessUtxo and nonWitnessUtxo simultaneously (Re: segwit psbt bug) (#1563) + +__added__ +- PSBT methods `getInputType`, `inputHasPubkey`, `inputHasHDKey`, `outputHasPubkey`, `outputHasHDKey` (#1563) + # 5.1.10 __fixed__ - Fixed psbt.signInputAsync (and consequentially all Async signing methods) not handling rejection of keypair.sign properly (#1582) diff --git a/package-lock.json b/package-lock.json index 5b53bb7..7278818 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.10", + "version": "5.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b45735b..0f92b75 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.1.10", + "version": "5.2.0", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./types/index.d.ts", From 09a0eb97793e78626f484c09aac547314d3fed4d Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Thu, 1 Oct 2020 15:10:49 +0300 Subject: [PATCH 531/568] #4 do not add empty redeem script name --- src/payments/p2sh.js | 3 ++- src/payments/p2wsh.js | 3 ++- ts_src/payments/p2sh.ts | 3 ++- ts_src/payments/p2wsh.ts | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index 42b1e23..0f46403 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -95,7 +95,8 @@ function p2sh(a, opts) { }); lazy.prop(o, 'name', () => { const nameParts = ['p2sh']; - if (o.redeem !== undefined) nameParts.push(o.redeem.name); + if (o.redeem !== undefined && o.redeem.name !== undefined) + nameParts.push(o.redeem.name); return nameParts.join('-'); }); if (opts.validate) { diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index f9ae90b..e100c6d 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -118,7 +118,8 @@ function p2wsh(a, opts) { }); lazy.prop(o, 'name', () => { const nameParts = ['p2wsh']; - if (o.redeem !== undefined) nameParts.push(o.redeem.name); + if (o.redeem !== undefined && o.redeem.name !== undefined) + nameParts.push(o.redeem.name); return nameParts.join('-'); }); // extended validation diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index 8a097bd..3b53fdc 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -118,7 +118,8 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { }); lazy.prop(o, 'name', () => { const nameParts = ['p2sh']; - if (o.redeem !== undefined) nameParts.push(o.redeem.name!); + if (o.redeem !== undefined && o.redeem.name !== undefined) + nameParts.push(o.redeem.name!); return nameParts.join('-'); }); diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index a72be94..6653448 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -132,7 +132,8 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { }); lazy.prop(o, 'name', () => { const nameParts = ['p2wsh']; - if (o.redeem !== undefined) nameParts.push(o.redeem.name!); + if (o.redeem !== undefined && o.redeem.name !== undefined) + nameParts.push(o.redeem.name!); return nameParts.join('-'); }); From e80dc8d347c8eed46d9b0fdce00dab98e3be65dd Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Thu, 1 Oct 2020 15:12:06 +0300 Subject: [PATCH 532/568] #4 check `name` field for equate() --- test/payments.utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/payments.utils.ts b/test/payments.utils.ts index 3f0ce23..c0635f3 100644 --- a/test/payments.utils.ts +++ b/test/payments.utils.ts @@ -81,6 +81,7 @@ export function equate(a: any, b: any, args?: any): void { if (b.signature === null) b.signature = undefined; if (b.signatures === null) b.signatures = undefined; if ('address' in b) t.strictEqual(a.address, b.address, 'Inequal *.address'); + if ('name' in b) t.strictEqual(a.name, b.name, 'Inequal *.name'); if ('hash' in b) t.strictEqual(tryHex(a.hash), tryHex(b.hash), 'Inequal *.hash'); if ('pubkey' in b) From 7d3bc8ffbcf32401fb954eeb269195cac4a5ffcd Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Thu, 1 Oct 2020 15:12:54 +0300 Subject: [PATCH 533/568] #5 remove redeem script name --- test/fixtures/p2sh.json | 16 ++++++++-------- test/fixtures/p2wsh.json | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/fixtures/p2sh.json b/test/fixtures/p2sh.json index b2a7607..8a1f9f6 100644 --- a/test/fixtures/p2sh.json +++ b/test/fixtures/p2sh.json @@ -52,7 +52,7 @@ } }, "expected": { - "name": "p2sh-p2pkh", + "name": "p2sh", "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", @@ -69,7 +69,7 @@ } }, "expected": { - "name": "p2sh-p2wpkh", + "name": "p2sh", "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", @@ -86,7 +86,7 @@ } }, "expected": { - "name": "p2sh-p2pk", + "name": "p2sh", "address": "36TibC8RrPB9WrBdPoGXhHqDHJosyFVtVQ", "hash": "3454c084887afe854e80221c69d6282926f809c4", "output": "OP_HASH160 3454c084887afe854e80221c69d6282926f809c4 OP_EQUAL", @@ -103,7 +103,7 @@ } }, "expected": { - "name": "p2sh-p2pkh", + "name": "p2sh", "address": "3GETYP4cuSesh2zsPEEYVZqnRedwe4FwUT", "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", @@ -124,7 +124,7 @@ } }, "expected": { - "name": "p2sh-p2wpkh", + "name": "p2sh", "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", @@ -141,7 +141,7 @@ "input": "3045022100e4fce9ec72b609a2df1dc050c20dcf101d27faefb3e686b7a4cb067becdd5e8e022071287fced53806b08cf39b5ad58bbe614775b3776e98a9f8760af0d4d1d47a9501 2103e15819590382a9dd878f01e2f0cbce541564eb415e43b440472d883ecd283058ac" }, "expected": { - "name": "p2sh-p2pk", + "name": "p2sh", "address": "36TibC8RrPB9WrBdPoGXhHqDHJosyFVtVQ", "hash": "3454c084887afe854e80221c69d6282926f809c4", "output": "OP_HASH160 3454c084887afe854e80221c69d6282926f809c4 OP_EQUAL", @@ -163,7 +163,7 @@ ] }, "expected": { - "name": "p2sh-p2wpkh", + "name": "p2sh", "address": "325CuTNSYmvurXaBmhNFer5zDkKnDXZggu", "hash": "0432515d8fe8de31be8207987fc6d67b29d5e7cc", "output": "OP_HASH160 0432515d8fe8de31be8207987fc6d67b29d5e7cc OP_EQUAL", @@ -187,7 +187,7 @@ } }, "expected": { - "name": "p2sh-p2pkh", + "name": "p2sh", "address": "2N7nfc7zeWuADtpdR4MrR7Wq3dzr7LxTCgS", "hash": "9f840a5fc02407ef0ad499c2ec0eb0b942fb0086", "output": "OP_HASH160 9f840a5fc02407ef0ad499c2ec0eb0b942fb0086 OP_EQUAL", diff --git a/test/fixtures/p2wsh.json b/test/fixtures/p2wsh.json index 0f60112..03fb01d 100644 --- a/test/fixtures/p2wsh.json +++ b/test/fixtures/p2wsh.json @@ -52,7 +52,7 @@ } }, "expected": { - "name": "p2wsh-p2pkh", + "name": "p2wsh", "address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", @@ -69,7 +69,7 @@ } }, "expected": { - "name": "p2wsh-p2wpkh", + "name": "p2wsh", "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", @@ -86,7 +86,7 @@ } }, "expected": { - "name": "p2wsh-p2pk", + "name": "p2wsh", "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", @@ -103,7 +103,7 @@ } }, "expected": { - "name": "p2wsh-p2pkh", + "name": "p2wsh", "address": "bc1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ss2cq5ar", "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", @@ -131,7 +131,7 @@ } }, "expected": { - "name": "p2wsh-p2wpkh", + "name": "p2wsh", "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", @@ -152,7 +152,7 @@ ] }, "expected": { - "name": "p2wsh-p2pk", + "name": "p2wsh", "address": "bc1q6rgl33d3s9dugudw7n68yrryajkr3ha9q8q24j20zs62se4q9tsqdy0t2q", "hash": "d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", "output": "OP_0 d0d1f8c5b1815bc471aef4f4720c64ecac38dfa501c0aac94f1434a866a02ae0", @@ -176,7 +176,7 @@ ] }, "expected": { - "name": "p2wsh-p2wpkh", + "name": "p2wsh", "address": "bc1qpsl7el8wcx22f3fpdt3lm2wmzug7yyx2q3n8wzgtf37kps9tqy7skc7m3e", "hash": "0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", "output": "OP_0 0c3fecfceec194a4c5216ae3fda9db1711e210ca046677090b4c7d60c0ab013d", @@ -200,7 +200,7 @@ } }, "expected": { - "name": "p2wsh-p2pkh", + "name": "p2wsh", "address": "tb1qusxlgq9quu27ucxs7a2fg8nv0pycdzvxsjk9npyupupxw3y892ssaskm8v", "hash": "e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", "output": "OP_0 e40df400a0e715ee60d0f754941e6c784986898684ac59849c0f026744872aa1", From 6661e3d5c410575ed9205e30cc3a9f077ae44590 Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Thu, 26 Nov 2020 19:23:01 +0200 Subject: [PATCH 534/568] #1470 Bring over TransactionInput & TransactionOutputfrom BIP174 --- ts_src/psbt.ts | 13 +++++++++++-- types/psbt.d.ts | 11 ++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index c55e6bc..8838b37 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -11,8 +11,6 @@ import { PsbtOutputUpdate, Transaction as ITransaction, TransactionFromBuffer, - TransactionInput, - TransactionOutput, } from 'bip174/src/lib/interfaces'; import { checkForInput, checkForOutput } from 'bip174/src/lib/utils'; import { fromOutputScript, toOutputScript } from './address'; @@ -28,10 +26,21 @@ import * as payments from './payments'; import * as bscript from './script'; import { Output, Transaction } from './transaction'; +export interface TransactionInput { + hash: string | Buffer; + index: number; + sequence?: number; +} + export interface PsbtTxInput extends TransactionInput { hash: Buffer; } +export interface TransactionOutput { + script: Buffer; + value: number; +} + export interface PsbtTxOutput extends TransactionOutput { address: string | undefined; } diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 022a95d..e7a79eb 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,11 +1,20 @@ import { Psbt as PsbtBase } from 'bip174'; -import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; +import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; +export interface TransactionInput { + hash: string | Buffer; + index: number; + sequence?: number; +} export interface PsbtTxInput extends TransactionInput { hash: Buffer; } +export interface TransactionOutput { + script: Buffer; + value: number; +} export interface PsbtTxOutput extends TransactionOutput { address: string | undefined; } From c0c9760ed26b7cb40225d9cf5764923c5f6f5229 Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Fri, 27 Nov 2020 10:00:51 +0200 Subject: [PATCH 535/568] #1470 update tiny-secp256k1 from 1.1.1 to 1.1.5; --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7278818..829bfd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1495,9 +1495,9 @@ "dev": true }, "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" }, "node-environment-flags": { "version": "1.0.6", @@ -2246,9 +2246,9 @@ } }, "tiny-secp256k1": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.1.tgz", - "integrity": "sha512-jA9WalQuhKun1svJrAVi9Vu8aUWKMfR7nMV903kHjrHTTY/IFa0petSq+Jk/Mv447dGD9LC8fGsmGRubBbcNng==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.5.tgz", + "integrity": "sha512-duE2hSLSQIpHGzmK48OgRrGTi+4OTkXLC6aa86uOYQ6LLCYZSarVKIAvEtY7MoXjoL6bOXMSerEGMzrvW4SkDw==", "requires": { "bindings": "^1.3.0", "bn.js": "^4.11.8", diff --git a/package.json b/package.json index 0f92b75..c10870f 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "merkle-lib": "^2.0.10", "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", - "tiny-secp256k1": "^1.1.1", + "tiny-secp256k1": "^1.1.5", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" From 5c6243f4e4480d3543cd02f3be480c338ef66589 Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Fri, 27 Nov 2020 11:39:10 +0200 Subject: [PATCH 536/568] #1477 remove TransactionBuilder and all references to it --- src/index.js | 2 - src/psbt.js | 6 +- src/transaction_builder.js | 1065 ---------- test/fixtures/transaction_builder.json | 2712 ------------------------ test/transaction_builder.spec.ts | 1001 --------- ts_src/index.ts | 1 - ts_src/psbt.ts | 6 +- ts_src/transaction_builder.ts | 1318 ------------ types/index.d.ts | 1 - types/transaction_builder.d.ts | 37 - 10 files changed, 4 insertions(+), 6145 deletions(-) delete mode 100644 src/transaction_builder.js delete mode 100644 test/fixtures/transaction_builder.json delete mode 100644 test/transaction_builder.spec.ts delete mode 100644 ts_src/transaction_builder.ts delete mode 100644 types/transaction_builder.d.ts diff --git a/src/index.js b/src/index.js index bc71f02..e27b98a 100644 --- a/src/index.js +++ b/src/index.js @@ -22,5 +22,3 @@ var script_1 = require('./script'); exports.opcodes = script_1.OPS; var transaction_1 = require('./transaction'); exports.Transaction = transaction_1.Transaction; -var transaction_builder_1 = require('./transaction_builder'); -exports.TransactionBuilder = transaction_builder_1.TransactionBuilder; diff --git a/src/psbt.js b/src/psbt.js index 5193dd3..6381669 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -69,8 +69,7 @@ class Psbt { __NON_WITNESS_UTXO_BUF_CACHE: [], __TX_IN_CACHE: {}, __TX: this.data.globalMap.unsignedTx.tx, - // Old TransactionBuilder behavior was to not confirm input values - // before signing. Even though we highly encourage people to get + // Even though we highly encourage people to get // the full parent transaction to verify values, the ability to // sign non-segwit inputs without the full transaction was often // requested. So the only way to activate is to use @ts-ignore. @@ -998,8 +997,7 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) { console.warn( 'Warning: Signing non-segwit inputs without the full parent transaction ' + 'means there is a chance that a miner could feed you incorrect information ' + - 'to trick you into paying large fees. This behavior is the same as the old ' + - 'TransactionBuilder class when signing non-segwit scripts. You are not ' + + 'to trick you into paying large fees. You are not ' + 'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' + 'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' + '*********************', diff --git a/src/transaction_builder.js b/src/transaction_builder.js deleted file mode 100644 index a13c481..0000000 --- a/src/transaction_builder.js +++ /dev/null @@ -1,1065 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const baddress = require('./address'); -const bufferutils_1 = require('./bufferutils'); -const classify = require('./classify'); -const bcrypto = require('./crypto'); -const ECPair = require('./ecpair'); -const networks = require('./networks'); -const payments = require('./payments'); -const bscript = require('./script'); -const script_1 = require('./script'); -const transaction_1 = require('./transaction'); -const types = require('./types'); -const typeforce = require('typeforce'); -const SCRIPT_TYPES = classify.types; -const PREVOUT_TYPES = new Set([ - // Raw - 'p2pkh', - 'p2pk', - 'p2wpkh', - 'p2ms', - // P2SH wrapped - 'p2sh-p2pkh', - 'p2sh-p2pk', - 'p2sh-p2wpkh', - 'p2sh-p2ms', - // P2WSH wrapped - 'p2wsh-p2pkh', - 'p2wsh-p2pk', - 'p2wsh-p2ms', - // P2SH-P2WSH wrapper - 'p2sh-p2wsh-p2pkh', - 'p2sh-p2wsh-p2pk', - 'p2sh-p2wsh-p2ms', -]); -function tfMessage(type, value, message) { - try { - typeforce(type, value); - } catch (err) { - throw new Error(message); - } -} -function txIsString(tx) { - return typeof tx === 'string' || tx instanceof String; -} -function txIsTransaction(tx) { - return tx instanceof transaction_1.Transaction; -} -class TransactionBuilder { - // WARNING: maximumFeeRate is __NOT__ to be relied on, - // it's just another potential safety mechanism (safety in-depth) - constructor(network = networks.bitcoin, maximumFeeRate = 2500) { - this.network = network; - this.maximumFeeRate = maximumFeeRate; - this.__PREV_TX_SET = {}; - this.__INPUTS = []; - this.__TX = new transaction_1.Transaction(); - this.__TX.version = 2; - this.__USE_LOW_R = false; - console.warn( - 'Deprecation Warning: TransactionBuilder will be removed in the future. ' + - '(v6.x.x or later) Please use the Psbt class instead. Examples of usage ' + - 'are available in the transactions-psbt.js integration test file on our ' + - 'Github. A high level explanation is available in the psbt.ts and psbt.js ' + - 'files as well.', - ); - } - static fromTransaction(transaction, network) { - const txb = new TransactionBuilder(network); - // Copy transaction fields - txb.setVersion(transaction.version); - txb.setLockTime(transaction.locktime); - // Copy outputs (done first to avoid signature invalidation) - transaction.outs.forEach(txOut => { - txb.addOutput(txOut.script, txOut.value); - }); - // Copy inputs - transaction.ins.forEach(txIn => { - txb.__addInputUnsafe(txIn.hash, txIn.index, { - sequence: txIn.sequence, - script: txIn.script, - witness: txIn.witness, - }); - }); - // fix some things not possible through the public API - txb.__INPUTS.forEach((input, i) => { - fixMultisigOrder(input, transaction, i); - }); - return txb; - } - setLowR(setting) { - typeforce(typeforce.maybe(typeforce.Boolean), setting); - if (setting === undefined) { - setting = true; - } - this.__USE_LOW_R = setting; - return setting; - } - setLockTime(locktime) { - typeforce(types.UInt32, locktime); - // if any signatures exist, throw - if ( - this.__INPUTS.some(input => { - if (!input.signatures) return false; - return input.signatures.some(s => s !== undefined); - }) - ) { - throw new Error('No, this would invalidate signatures'); - } - this.__TX.locktime = locktime; - } - setVersion(version) { - typeforce(types.UInt32, version); - // XXX: this might eventually become more complex depending on what the versions represent - this.__TX.version = version; - } - addInput(txHash, vout, sequence, prevOutScript) { - if (!this.__canModifyInputs()) { - throw new Error('No, this would invalidate signatures'); - } - let value; - // is it a hex string? - if (txIsString(txHash)) { - // transaction hashs's are displayed in reverse order, un-reverse it - txHash = bufferutils_1.reverseBuffer(Buffer.from(txHash, 'hex')); - // is it a Transaction object? - } else if (txIsTransaction(txHash)) { - const txOut = txHash.outs[vout]; - prevOutScript = txOut.script; - value = txOut.value; - txHash = txHash.getHash(false); - } - return this.__addInputUnsafe(txHash, vout, { - sequence, - prevOutScript, - value, - }); - } - addOutput(scriptPubKey, value) { - if (!this.__canModifyOutputs()) { - throw new Error('No, this would invalidate signatures'); - } - // Attempt to get a script if it's a base58 or bech32 address string - if (typeof scriptPubKey === 'string') { - scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); - } - return this.__TX.addOutput(scriptPubKey, value); - } - build() { - return this.__build(false); - } - buildIncomplete() { - return this.__build(true); - } - sign( - signParams, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - ) { - trySign( - getSigningData( - this.network, - this.__INPUTS, - this.__needsOutputs.bind(this), - this.__TX, - signParams, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - this.__USE_LOW_R, - ), - ); - } - __addInputUnsafe(txHash, vout, options) { - if (transaction_1.Transaction.isCoinbaseHash(txHash)) { - throw new Error('coinbase inputs not supported'); - } - const prevTxOut = txHash.toString('hex') + ':' + vout; - if (this.__PREV_TX_SET[prevTxOut] !== undefined) - throw new Error('Duplicate TxOut: ' + prevTxOut); - let input = {}; - // derive what we can from the scriptSig - if (options.script !== undefined) { - input = expandInput(options.script, options.witness || []); - } - // if an input value was given, retain it - if (options.value !== undefined) { - input.value = options.value; - } - // derive what we can from the previous transactions output script - if (!input.prevOutScript && options.prevOutScript) { - let prevOutType; - if (!input.pubkeys && !input.signatures) { - const expanded = expandOutput(options.prevOutScript); - if (expanded.pubkeys) { - input.pubkeys = expanded.pubkeys; - input.signatures = expanded.signatures; - } - prevOutType = expanded.type; - } - input.prevOutScript = options.prevOutScript; - input.prevOutType = prevOutType || classify.output(options.prevOutScript); - } - const vin = this.__TX.addInput( - txHash, - vout, - options.sequence, - options.scriptSig, - ); - this.__INPUTS[vin] = input; - this.__PREV_TX_SET[prevTxOut] = true; - return vin; - } - __build(allowIncomplete) { - if (!allowIncomplete) { - if (!this.__TX.ins.length) throw new Error('Transaction has no inputs'); - if (!this.__TX.outs.length) throw new Error('Transaction has no outputs'); - } - const tx = this.__TX.clone(); - // create script signatures from inputs - this.__INPUTS.forEach((input, i) => { - if (!input.prevOutType && !allowIncomplete) - throw new Error('Transaction is not complete'); - const result = build(input.prevOutType, input, allowIncomplete); - if (!result) { - if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) - throw new Error('Unknown input type'); - if (!allowIncomplete) throw new Error('Not enough information'); - return; - } - tx.setInputScript(i, result.input); - tx.setWitness(i, result.witness); - }); - if (!allowIncomplete) { - // do not rely on this, its merely a last resort - if (this.__overMaximumFees(tx.virtualSize())) { - throw new Error('Transaction has absurd fees'); - } - } - return tx; - } - __canModifyInputs() { - return this.__INPUTS.every(input => { - if (!input.signatures) return true; - return input.signatures.every(signature => { - if (!signature) return true; - const hashType = signatureHashType(signature); - // if SIGHASH_ANYONECANPAY is set, signatures would not - // be invalidated by more inputs - return ( - (hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY) !== 0 - ); - }); - }); - } - __needsOutputs(signingHashType) { - if (signingHashType === transaction_1.Transaction.SIGHASH_ALL) { - return this.__TX.outs.length === 0; - } - // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs - // .build() will fail, but .buildIncomplete() is OK - return ( - this.__TX.outs.length === 0 && - this.__INPUTS.some(input => { - if (!input.signatures) return false; - return input.signatures.some(signature => { - if (!signature) return false; // no signature, no issue - const hashType = signatureHashType(signature); - if (hashType & transaction_1.Transaction.SIGHASH_NONE) return false; // SIGHASH_NONE doesn't care about outputs - return true; // SIGHASH_* does care - }); - }) - ); - } - __canModifyOutputs() { - const nInputs = this.__TX.ins.length; - const nOutputs = this.__TX.outs.length; - return this.__INPUTS.every(input => { - if (input.signatures === undefined) return true; - return input.signatures.every(signature => { - if (!signature) return true; - const hashType = signatureHashType(signature); - const hashTypeMod = hashType & 0x1f; - if (hashTypeMod === transaction_1.Transaction.SIGHASH_NONE) return true; - if (hashTypeMod === transaction_1.Transaction.SIGHASH_SINGLE) { - // if SIGHASH_SINGLE is set, and nInputs > nOutputs - // some signatures would be invalidated by the addition - // of more outputs - return nInputs <= nOutputs; - } - return false; - }); - }); - } - __overMaximumFees(bytes) { - // not all inputs will have .value defined - const incoming = this.__INPUTS.reduce((a, x) => a + (x.value >>> 0), 0); - // but all outputs do, and if we have any input value - // we can immediately determine if the outputs are too small - const outgoing = this.__TX.outs.reduce((a, x) => a + x.value, 0); - const fee = incoming - outgoing; - const feeRate = fee / bytes; - return feeRate > this.maximumFeeRate; - } -} -exports.TransactionBuilder = TransactionBuilder; -function expandInput(scriptSig, witnessStack, type, scriptPubKey) { - if (scriptSig.length === 0 && witnessStack.length === 0) return {}; - if (!type) { - let ssType = classify.input(scriptSig, true); - let wsType = classify.witness(witnessStack, true); - if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined; - if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined; - type = ssType || wsType; - } - switch (type) { - case SCRIPT_TYPES.P2WPKH: { - const { output, pubkey, signature } = payments.p2wpkh({ - witness: witnessStack, - }); - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2WPKH, - pubkeys: [pubkey], - signatures: [signature], - }; - } - case SCRIPT_TYPES.P2PKH: { - const { output, pubkey, signature } = payments.p2pkh({ - input: scriptSig, - }); - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2PKH, - pubkeys: [pubkey], - signatures: [signature], - }; - } - case SCRIPT_TYPES.P2PK: { - const { signature } = payments.p2pk({ input: scriptSig }); - return { - prevOutType: SCRIPT_TYPES.P2PK, - pubkeys: [undefined], - signatures: [signature], - }; - } - case SCRIPT_TYPES.P2MS: { - const { m, pubkeys, signatures } = payments.p2ms( - { - input: scriptSig, - output: scriptPubKey, - }, - { allowIncomplete: true }, - ); - return { - prevOutType: SCRIPT_TYPES.P2MS, - pubkeys, - signatures, - maxSignatures: m, - }; - } - } - if (type === SCRIPT_TYPES.P2SH) { - const { output, redeem } = payments.p2sh({ - input: scriptSig, - witness: witnessStack, - }); - const outputType = classify.output(redeem.output); - const expanded = expandInput( - redeem.input, - redeem.witness, - outputType, - redeem.output, - ); - if (!expanded.prevOutType) return {}; - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2SH, - redeemScript: redeem.output, - redeemScriptType: expanded.prevOutType, - witnessScript: expanded.witnessScript, - witnessScriptType: expanded.witnessScriptType, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - }; - } - if (type === SCRIPT_TYPES.P2WSH) { - const { output, redeem } = payments.p2wsh({ - input: scriptSig, - witness: witnessStack, - }); - const outputType = classify.output(redeem.output); - let expanded; - if (outputType === SCRIPT_TYPES.P2WPKH) { - expanded = expandInput(redeem.input, redeem.witness, outputType); - } else { - expanded = expandInput( - bscript.compile(redeem.witness), - [], - outputType, - redeem.output, - ); - } - if (!expanded.prevOutType) return {}; - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2WSH, - witnessScript: redeem.output, - witnessScriptType: expanded.prevOutType, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - }; - } - return { - prevOutType: SCRIPT_TYPES.NONSTANDARD, - prevOutScript: scriptSig, - }; -} -// could be done in expandInput, but requires the original Transaction for hashForSignature -function fixMultisigOrder(input, transaction, vin) { - if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) - return; - if (input.pubkeys.length === input.signatures.length) return; - const unmatched = input.signatures.concat(); - input.signatures = input.pubkeys.map(pubKey => { - const keyPair = ECPair.fromPublicKey(pubKey); - let match; - // check for a signature - unmatched.some((signature, i) => { - // skip if undefined || OP_0 - if (!signature) return false; - // TODO: avoid O(n) hashForSignature - const parsed = bscript.signature.decode(signature); - const hash = transaction.hashForSignature( - vin, - input.redeemScript, - parsed.hashType, - ); - // skip if signature does not match pubKey - if (!keyPair.verify(hash, parsed.signature)) return false; - // remove matched signature from unmatched - unmatched[i] = undefined; - match = signature; - return true; - }); - return match; - }); -} -function expandOutput(script, ourPubKey) { - typeforce(types.Buffer, script); - const type = classify.output(script); - switch (type) { - case SCRIPT_TYPES.P2PKH: { - if (!ourPubKey) return { type }; - // does our hash160(pubKey) match the output scripts? - const pkh1 = payments.p2pkh({ output: script }).hash; - const pkh2 = bcrypto.hash160(ourPubKey); - if (!pkh1.equals(pkh2)) return { type }; - return { - type, - pubkeys: [ourPubKey], - signatures: [undefined], - }; - } - case SCRIPT_TYPES.P2WPKH: { - if (!ourPubKey) return { type }; - // does our hash160(pubKey) match the output scripts? - const wpkh1 = payments.p2wpkh({ output: script }).hash; - const wpkh2 = bcrypto.hash160(ourPubKey); - if (!wpkh1.equals(wpkh2)) return { type }; - return { - type, - pubkeys: [ourPubKey], - signatures: [undefined], - }; - } - case SCRIPT_TYPES.P2PK: { - const p2pk = payments.p2pk({ output: script }); - return { - type, - pubkeys: [p2pk.pubkey], - signatures: [undefined], - }; - } - case SCRIPT_TYPES.P2MS: { - const p2ms = payments.p2ms({ output: script }); - return { - type, - pubkeys: p2ms.pubkeys, - signatures: p2ms.pubkeys.map(() => undefined), - maxSignatures: p2ms.m, - }; - } - } - return { type }; -} -function prepareInput(input, ourPubKey, redeemScript, witnessScript) { - if (redeemScript && witnessScript) { - const p2wsh = payments.p2wsh({ - redeem: { output: witnessScript }, - }); - const p2wshAlt = payments.p2wsh({ output: redeemScript }); - const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); - const p2shAlt = payments.p2sh({ redeem: p2wsh }); - // enforces P2SH(P2WSH(...)) - if (!p2wsh.hash.equals(p2wshAlt.hash)) - throw new Error('Witness script inconsistent with prevOutScript'); - if (!p2sh.hash.equals(p2shAlt.hash)) - throw new Error('Redeem script inconsistent with prevOutScript'); - const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); - if (!expanded.pubkeys) - throw new Error( - expanded.type + - ' not supported as witnessScript (' + - bscript.toASM(witnessScript) + - ')', - ); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - const signScript = witnessScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) - throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure'); - return { - redeemScript, - redeemScriptType: SCRIPT_TYPES.P2WSH, - witnessScript, - witnessScriptType: expanded.type, - prevOutType: SCRIPT_TYPES.P2SH, - prevOutScript: p2sh.output, - hasWitness: true, - signScript, - signType: expanded.type, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - if (redeemScript) { - const p2sh = payments.p2sh({ redeem: { output: redeemScript } }); - if (input.prevOutScript) { - let p2shAlt; - try { - p2shAlt = payments.p2sh({ output: input.prevOutScript }); - } catch (e) { - throw new Error('PrevOutScript must be P2SH'); - } - if (!p2sh.hash.equals(p2shAlt.hash)) - throw new Error('Redeem script inconsistent with prevOutScript'); - } - const expanded = expandOutput(p2sh.redeem.output, ourPubKey); - if (!expanded.pubkeys) - throw new Error( - expanded.type + - ' not supported as redeemScript (' + - bscript.toASM(redeemScript) + - ')', - ); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - let signScript = redeemScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output; - } - return { - redeemScript, - redeemScriptType: expanded.type, - prevOutType: SCRIPT_TYPES.P2SH, - prevOutScript: p2sh.output, - hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, - signScript, - signType: expanded.type, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - if (witnessScript) { - const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }); - if (input.prevOutScript) { - const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }); - if (!p2wsh.hash.equals(p2wshAlt.hash)) - throw new Error('Witness script inconsistent with prevOutScript'); - } - const expanded = expandOutput(p2wsh.redeem.output, ourPubKey); - if (!expanded.pubkeys) - throw new Error( - expanded.type + - ' not supported as witnessScript (' + - bscript.toASM(witnessScript) + - ')', - ); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - const signScript = witnessScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) - throw new Error('P2WSH(P2WPKH) is a consensus failure'); - return { - witnessScript, - witnessScriptType: expanded.type, - prevOutType: SCRIPT_TYPES.P2WSH, - prevOutScript: p2wsh.output, - hasWitness: true, - signScript, - signType: expanded.type, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - if (input.prevOutType && input.prevOutScript) { - // embedded scripts are not possible without extra information - if (input.prevOutType === SCRIPT_TYPES.P2SH) - throw new Error( - 'PrevOutScript is ' + input.prevOutType + ', requires redeemScript', - ); - if (input.prevOutType === SCRIPT_TYPES.P2WSH) - throw new Error( - 'PrevOutScript is ' + input.prevOutType + ', requires witnessScript', - ); - if (!input.prevOutScript) throw new Error('PrevOutScript is missing'); - const expanded = expandOutput(input.prevOutScript, ourPubKey); - if (!expanded.pubkeys) - throw new Error( - expanded.type + - ' not supported (' + - bscript.toASM(input.prevOutScript) + - ')', - ); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - let signScript = input.prevOutScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output; - } - return { - prevOutType: expanded.type, - prevOutScript: input.prevOutScript, - hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, - signScript, - signType: expanded.type, - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output; - return { - prevOutType: SCRIPT_TYPES.P2PKH, - prevOutScript, - hasWitness: false, - signScript: prevOutScript, - signType: SCRIPT_TYPES.P2PKH, - pubkeys: [ourPubKey], - signatures: [undefined], - }; -} -function build(type, input, allowIncomplete) { - const pubkeys = input.pubkeys || []; - let signatures = input.signatures || []; - switch (type) { - case SCRIPT_TYPES.P2PKH: { - if (pubkeys.length === 0) break; - if (signatures.length === 0) break; - return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] }); - } - case SCRIPT_TYPES.P2WPKH: { - if (pubkeys.length === 0) break; - if (signatures.length === 0) break; - return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] }); - } - case SCRIPT_TYPES.P2PK: { - if (pubkeys.length === 0) break; - if (signatures.length === 0) break; - return payments.p2pk({ signature: signatures[0] }); - } - case SCRIPT_TYPES.P2MS: { - const m = input.maxSignatures; - if (allowIncomplete) { - signatures = signatures.map(x => x || script_1.OPS.OP_0); - } else { - signatures = signatures.filter(x => x); - } - // if the transaction is not not complete (complete), or if signatures.length === m, validate - // otherwise, the number of OP_0's may be >= m, so don't validate (boo) - const validate = !allowIncomplete || m === signatures.length; - return payments.p2ms( - { m, pubkeys, signatures }, - { allowIncomplete, validate }, - ); - } - case SCRIPT_TYPES.P2SH: { - const redeem = build(input.redeemScriptType, input, allowIncomplete); - if (!redeem) return; - return payments.p2sh({ - redeem: { - output: redeem.output || input.redeemScript, - input: redeem.input, - witness: redeem.witness, - }, - }); - } - case SCRIPT_TYPES.P2WSH: { - const redeem = build(input.witnessScriptType, input, allowIncomplete); - if (!redeem) return; - return payments.p2wsh({ - redeem: { - output: input.witnessScript, - input: redeem.input, - witness: redeem.witness, - }, - }); - } - } -} -function canSign(input) { - return ( - input.signScript !== undefined && - input.signType !== undefined && - input.pubkeys !== undefined && - input.signatures !== undefined && - input.signatures.length === input.pubkeys.length && - input.pubkeys.length > 0 && - (input.hasWitness === false || input.value !== undefined) - ); -} -function signatureHashType(buffer) { - return buffer.readUInt8(buffer.length - 1); -} -function checkSignArgs(inputs, signParams) { - if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { - throw new TypeError( - `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, - ); - } - tfMessage( - typeforce.Number, - signParams.vin, - `sign must include vin parameter as Number (input index)`, - ); - tfMessage( - types.Signer, - signParams.keyPair, - `sign must include keyPair parameter as Signer interface`, - ); - tfMessage( - typeforce.maybe(typeforce.Number), - signParams.hashType, - `sign hashType parameter must be a number`, - ); - const prevOutType = (inputs[signParams.vin] || []).prevOutType; - const posType = signParams.prevOutScriptType; - switch (posType) { - case 'p2pkh': - if (prevOutType && prevOutType !== 'pubkeyhash') { - throw new TypeError( - `input #${signParams.vin} is not of type p2pkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2pk': - if (prevOutType && prevOutType !== 'pubkey') { - throw new TypeError( - `input #${signParams.vin} is not of type p2pk: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2wpkh': - if (prevOutType && prevOutType !== 'witnesspubkeyhash') { - throw new TypeError( - `input #${signParams.vin} is not of type p2wpkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - break; - case 'p2ms': - if (prevOutType && prevOutType !== 'multisig') { - throw new TypeError( - `input #${signParams.vin} is not of type p2ms: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2sh-p2wpkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${signParams.vin} is not of type p2sh-p2wpkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - break; - case 'p2sh-p2ms': - case 'p2sh-p2pk': - case 'p2sh-p2pkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2wsh-p2ms': - case 'p2wsh-p2pk': - case 'p2wsh-p2pkh': - if (prevOutType && prevOutType !== 'witnessscripthash') { - throw new TypeError( - `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.Buffer, - signParams.witnessScript, - `${posType} requires witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - break; - case 'p2sh-p2wsh-p2ms': - case 'p2sh-p2wsh-p2pk': - case 'p2sh-p2wsh-p2pkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.Buffer, - signParams.witnessScript, - `${posType} requires witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires witnessScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessScript`, - ); - break; - } -} -function trySign({ - input, - ourPubKey, - keyPair, - signatureHash, - hashType, - useLowR, -}) { - // enforce in order signing of public keys - let signed = false; - for (const [i, pubKey] of input.pubkeys.entries()) { - if (!ourPubKey.equals(pubKey)) continue; - if (input.signatures[i]) throw new Error('Signature already exists'); - // TODO: add tests - if (ourPubKey.length !== 33 && input.hasWitness) { - throw new Error( - 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', - ); - } - const signature = keyPair.sign(signatureHash, useLowR); - input.signatures[i] = bscript.signature.encode(signature, hashType); - signed = true; - } - if (!signed) throw new Error('Key pair cannot sign for this input'); -} -function getSigningData( - network, - inputs, - needsOutputs, - tx, - signParams, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - useLowR, -) { - let vin; - if (typeof signParams === 'number') { - console.warn( - 'DEPRECATED: TransactionBuilder sign method arguments ' + - 'will change in v6, please use the TxbSignArg interface', - ); - vin = signParams; - } else if (typeof signParams === 'object') { - checkSignArgs(inputs, signParams); - ({ - vin, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - } = signParams); - } else { - throw new TypeError( - 'TransactionBuilder sign first arg must be TxbSignArg or number', - ); - } - if (keyPair === undefined) { - throw new Error('sign requires keypair'); - } - // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== network) - throw new TypeError('Inconsistent network'); - if (!inputs[vin]) throw new Error('No input at index: ' + vin); - hashType = hashType || transaction_1.Transaction.SIGHASH_ALL; - if (needsOutputs(hashType)) throw new Error('Transaction needs outputs'); - const input = inputs[vin]; - // if redeemScript was previously provided, enforce consistency - if ( - input.redeemScript !== undefined && - redeemScript && - !input.redeemScript.equals(redeemScript) - ) { - throw new Error('Inconsistent redeemScript'); - } - const ourPubKey = - keyPair.publicKey || (keyPair.getPublicKey && keyPair.getPublicKey()); - if (!canSign(input)) { - if (witnessValue !== undefined) { - if (input.value !== undefined && input.value !== witnessValue) - throw new Error('Input did not match witnessValue'); - typeforce(types.Satoshi, witnessValue); - input.value = witnessValue; - } - if (!canSign(input)) { - const prepared = prepareInput( - input, - ourPubKey, - redeemScript, - witnessScript, - ); - // updates inline - Object.assign(input, prepared); - } - if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); - } - // ready to sign - let signatureHash; - if (input.hasWitness) { - signatureHash = tx.hashForWitnessV0( - vin, - input.signScript, - input.value, - hashType, - ); - } else { - signatureHash = tx.hashForSignature(vin, input.signScript, hashType); - } - return { - input, - ourPubKey, - keyPair, - signatureHash, - hashType, - useLowR: !!useLowR, - }; -} diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json deleted file mode 100644 index 1564e6a..0000000 --- a/test/fixtures/transaction_builder.json +++ /dev/null @@ -1,2712 +0,0 @@ -{ - "valid": { - "build": [ - { - "description": "Transaction w/ P2PKH -> P2PKH", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006b483045022100a3b254e1c10b5d039f36c05f323995d6e5a367d98dd78a13d5bbc3991b35720e022022fccea3897d594de0689601fbd486588d5bfa6915be2386db0397ee9a6e80b601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ P2PK -> P2PKH", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000494830450221009833abb3ab49d7004c06bcc79eafd6905ada3eee91f3376ad388548034acd9a702202e84dda6ef2678c82256afcfc459aaa68e179b2bb0e6b2dc3f1410e132c5e6c301ffffffff0100f90295000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 OP_CHECKSIG", - "signs": [ - { - "prevOutScriptType": "p2pk", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 2500000000 - } - ] - }, - { - "description": "Transaction w/ P2SH(P2PKH) -> P2PKH", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000085483045022100a3b254e1c10b5d039f36c05f323995d6e5a367d98dd78a13d5bbc3991b35720e022022fccea3897d594de0689601fbd486588d5bfa6915be2386db0397ee9a6e80b601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817981976a914751e76e8199196d454941c45d1b3a323f1433bd688acffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "redeemScript": "OP_DUP OP_HASH160 751e76e8199196d454941c45d1b3a323f1433bd6 OP_EQUALVERIFY OP_CHECKSIG" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ P2SH(P2MS 2/2) -> P2PKH", - "network": "testnet", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd1b0100483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a4014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 faf1d99bf040ea9c7f8cc9f14ac6733ad75ce246 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ P2MS 2/2 -> P2PKH", - "network": "testnet", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009200483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a401ffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" - }, - { - "prevOutScriptType": "p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 faf1d99bf040ea9c7f8cc9f14ac6733ad75ce246 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ P2MS 2/3 -> P2PKH", - "network": "testnet", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000910047304402206b2fc7d3182e2853cab5bcffb85c3ef5470d2d05c496295538c9947af3bfd0ec0220300aa705a985c74f76c26c6d68da9b61b5c4cd5432e8c6a54623f376c8bf8cde01473044022031059c4dd6a97d84e3a4eb1ca21a9870bd1762fbd5db7c1932d75e56da78794502200f22d85be3c5f7035e89a147ee2619a066df19aff14a62e6bb3f649b6da19edf01ffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" - }, - { - "prevOutScriptType": "p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 faf1d99bf040ea9c7f8cc9f14ac6733ad75ce246 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ P2MS 2/2 (reverse order) -> P2PKH", - "network": "testnet", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009200483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a401ffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" - }, - { - "prevOutScriptType": "p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 faf1d99bf040ea9c7f8cc9f14ac6733ad75ce246 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ P2SH(P2MS 2/3)", - "network": "testnet", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd5e0100483045022100eec19e061cad41610f9b42d2b06638b6b0fec3da0de9c6858e7f8c06053979900220622936dd47e202b2ad17639cda680e52334d407149252959936bb1f38e4acc52014830450221009aac215157a74a18234fd06be27448dccee809986bbf93be457a9262f0c69a9402203ff41d7c757f0e8951e4471f205087ecff499f986400ab18210eaad9a628e33c014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Transaction w/ P2SH(P2MS 2/3), different hash types", - "network": "testnet", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd5e0100483045022100eec19e061cad41610f9b42d2b06638b6b0fec3da0de9c6858e7f8c06053979900220622936dd47e202b2ad17639cda680e52334d407149252959936bb1f38e4acc5201483045022100d404fb6de6cf42efb9d7948d2e8fb6618f8eba55ecd25907d18d576d9aa6f39d02205ec2e7fa7c5f8a9793732ca9d2f9aba3b2bb04ca6d467ba36940e0f695e48de5024cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", - "hashType": 1 - }, - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", - "hashType": 2 - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Transaction w/ P2SH(P2PK) -> P2PKH", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006c47304402201115644b134932c8a7a8e925769d130a801288d477130e2bf6fadda20b33754d02202ecefbf63844d7cb2d5868539c39f973fe019f72e5c31a707836c0d61ef317db012321033e29aea1168a835d5e386c292082db7b7807172a10ec634ad34226f36d79e70facffffffff0100f90295000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_HASH160 e89677d91455e541630d62c63718bef738b478b1 OP_EQUAL", - "signs": [ - { - "prevOutScriptType": "p2sh-p2pk", - "keyPair": "KxLDMPtVM7sLSu2v5n1LybDibw6P9FFbL4pUwJ51UDm7rp5AmXWW", - "redeemScript": "033e29aea1168a835d5e386c292082db7b7807172a10ec634ad34226f36d79e70f OP_CHECKSIG" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 2500000000 - } - ] - }, - { - "description": "Transaction w/ non-zero vin inputs", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000006a47304402205c80bbb5125b35d5e5a8324b1336832d29a6fc004859c8a9ff6bef47ba7fc348022018612216e57a521b2c4543f1f4fd738a76814c37c074e88adfe12464fff31cf901210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ non-default input sequence numbers, version and locktime", - "txHex": "0400000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000006a47304402200e7c0330f39c04e3c1b9e3daf71d106c7129c095f6ebb494d06f0ef8013b74ea022003fc0fe05e71a2a4f8434feb0632cdb6883676d0ccb9b19d7a8a9e5fc02a616401210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b9c220000110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac09990400", - "version": 4, - "locktime": 301321, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 2, - "sequence": 2147001, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ nLockTime, P2PKH -> P2PKH", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006b483045022100e31ef1bcc6f060cb6a53675a11606b9180f4f8b1ec823113fb4c0bf1c5b99b8e02204234690c19cd89e544002d26dbcbd49bf9d1b4cfc5a617fd8ab2607acfd869b001210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588acffff0000", - "version": 1, - "locktime": 65535, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ 1 P2PKH transaction input (Issue #644)", - "network": "testnet", - "txHex": "010000000132595835c74fccf097db4ccae9dc2de621e58e0d3f697a27b469b61c7a223b39000000006a47304402202fd41f18f1d7915bc811610236e1d708f9cd3515734abd5db7ac607f030728af02206bee7d163f04c470ce55561523824eb8b3abce80c214aabf7dfb78a073ea4a70012103f29374a4c2c218a4077db9ba0b9d674cde3719560460af4eb3190d512dd5de92ffffffff01808d5b00000000001976a914ff99e06c1a4ac394b4e1cb3d3a4b2b47749e339a88ac00000000", - "version": 1, - "inputs": [ - { - "txHex": "0100000001f7e6430096cd2790bac115aaab22c0a50fb0a1794305302e1a399e81d8d354f4020000006a47304402205793a862d193264afc32713e2e14541e1ff9ebb647dd7e7e6a0051d0faa87de302205216653741ecbbed573ea2fc053209dd6980616701c27be5b958a159fc97f45a012103e877e7deb32d19250dcfe534ea82c99ad739800295cd5429a7f69e2896c36fcdfeffffff0340420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac8bda2702000000001976a9145a0ef60784137d03e7868d063b05424f2f43799f88ac40420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac2fcc0e00", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "cQ6483mDWwoG8o4tn6nU9Jg52RKMjPUWXSY1vycAyPRXQJ1Pn2Rq" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 ff99e06c1a4ac394b4e1cb3d3a4b2b47749e339a OP_EQUALVERIFY OP_CHECKSIG", - "value": 6000000 - } - ] - }, - { - "description": "Transaction w/ P2PKH -> P2WPKH", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006b4830450221009a524a3257b8bd0c77ba4cba65fc00954f2030243f2eb16571838a3b951c8c07022008f5af9de672b365fd257377db1cf6da4da1b49b9637ceb651ac0eb4181dc3ca01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff011027000000000000160014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c500000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - } - ] - } - ], - "outputs": [ - { - "script": "OP_0 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ P2PKH -> P2WSH", - "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006a473044022056c99ba23eb15b3e666b188f87b04d5ef23eeda5298939cdaec35a3bddf3835602205887a5a460f299819b0c93948fafab8b2d64d8c051934431e3bb9acebef5d1b001210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff01102700000000000022002032447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b8000000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - } - ] - } - ], - "outputs": [ - { - "script": "OP_0 32447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b80", - "value": 10000 - } - ] - }, - { - "description": "P2WPKH -> P2WPKH", - "txHex": "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff011027000000000000160014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c502483045022100a8fc5e4c6d7073474eff2af5d756966e75be0cdfbba299518526080ce8b584be02200f26d41082764df89e3c815b8eaf51034a3b68a25f1be51208f54222c1bb6c1601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", - "version": 1, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_0 751e76e8199196d454941c45d1b3a323f1433bd6", - "signs": [ - { - "prevOutScriptType": "p2wpkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "value": 10000 - } - ] - } - ], - "outputs": [ - { - "script": "OP_0 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ P2WSH(P2PK) -> P2PKH", - "txHex": "010000000001014533a3bc1e039bd787656068e135aaee10aee95a64776bfc047ee6a7c1ebdd2f0000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac02473044022039725bb7291a14dd182dafdeaf3ea0d5c05c34f4617ccbaa46522ca913995c4e02203b170d072ed2e489e7424ad96d8fa888deb530be2d4c5d9aaddf111a7efdb2d3012321038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bac00000000", - "version": 1, - "inputs": [ - { - "txId": "2fddebc1a7e67e04fc6b77645ae9ae10eeaa35e168606587d79b031ebca33345", - "vout": 0, - "prevTxScript": "OP_0 0f9ea7bae7166c980169059e39443ed13324495b0d6678ce716262e879591210", - "signs": [ - { - "prevOutScriptType": "p2wsh-p2pk", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "witnessScript": "038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_CHECKSIG", - "value": 80000 - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG", - "value": 60000 - } - ] - }, - { - "description": "SIGHASH SINGLE (random)", - "txHex": "01000000012ffb29d53528ad30c37c267fbbeda3c6fce08f5f6f5d3b1eab22193599a3612a010000006b483045022100f963f1d9564075a934d7c3cfa333bd1378859b84cba947e149926fc9ec89b5ae02202b5b912e507bae65002aff972f9752e2aeb2e22c5fdbaaad672090378184df37032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff0260a62f01000000001976a9140de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b888ac80969800000000001976a91454d0e925d5ee0ee26768a237067dee793d01a70688ac00000000", - "version": 1, - "inputs": [ - { - "txId": "2a61a399351922ab1e3b5d6f5f8fe0fcc6a3edbb7f267cc330ad2835d529fb2f", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 3 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "outputs": [ - { - "value": 19900000, - "script": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 10000000, - "script": "OP_DUP OP_HASH160 54d0e925d5ee0ee26768a237067dee793d01a706 OP_EQUALVERIFY OP_CHECKSIG" - } - ] - }, - { - "description": "SIGHASH ALL", - "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006a47304402206abb0622b8b6ca83f1f4de84830cf38bf4615dc9e47a7dcdcc489905f26aa9cb02201d2d8a7815242b88e4cd66390ca46da802238f9b1395e0d118213d30dad38184012102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006b483045022100de13b42804f87a09bb46def12ab4608108d8c2db41db4bc09064f9c46fcf493102205e5c759ab7b2895c9b0447e56029f6895ff7bb20e0847c564a88a3cfcf080c4f012102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b4830450221009100a3f5b30182d1cb0172792af6947b6d8d42badb0539f2c209aece5a0628f002200ae91702ca63347e344c85fcb536f30ee97b75cdf4900de534ed5e040e71a548012102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", - "version": 1, - "inputs": [ - { - "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 1 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "057ef664f0e1108f270729e53a62e4cb1a7480d9a87f543c6a1a785a1e492c65", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 1 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "35db95c66634c3497a277c1f08ed71a0cb53195ffecbf9798cdde4a30f27fab9", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 1 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "outputs": [ - { - "value": 20000, - "script": "OP_DUP OP_HASH160 9ed1f577c60e4be1dbf35318ec12f51d25e85773 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 30000, - "script": "OP_DUP OP_HASH160 fb407e88c48921d5547d899e18a7c0a36919f54d OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 50000, - "script": "OP_DUP OP_HASH160 04ccb4eed8cfa9f6e394e945178960f5ccddb387 OP_EQUALVERIFY OP_CHECKSIG" - } - ] - }, - { - "description": "SIGHASH ALL | ANYONECANPAY", - "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100bd2829550e9b3a081747281029b5f5a96bbd83bb6a92fa2f8310f1bd0d53abc90220071b469417c55cdb3b04171fd7900d2768981b7ab011553d84d24ea85d277079812102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a47304402206295e17c45c6356ffb20365b696bcbb869db7e8697f4b8a684098ee2bff85feb02202905c441abe39ec9c480749236b84fdd3ebd91ecd25b559136370aacfcf2815c812102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b483045022100f58e7c98ac8412944d575bcdece0e5966d4018f05988b5b60b6f46b8cb7a543102201c5854d3361e29b58123f34218cec2c722f5ec7a08235ebd007ec637b07c193a812102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", - "version": 1, - "inputs": [ - { - "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 129 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "057ef664f0e1108f270729e53a62e4cb1a7480d9a87f543c6a1a785a1e492c65", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 129 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "35db95c66634c3497a277c1f08ed71a0cb53195ffecbf9798cdde4a30f27fab9", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 129 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "outputs": [ - { - "value": 20000, - "script": "OP_DUP OP_HASH160 9ed1f577c60e4be1dbf35318ec12f51d25e85773 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 30000, - "script": "OP_DUP OP_HASH160 fb407e88c48921d5547d899e18a7c0a36919f54d OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 50000, - "script": "OP_DUP OP_HASH160 04ccb4eed8cfa9f6e394e945178960f5ccddb387 OP_EQUALVERIFY OP_CHECKSIG" - } - ] - }, - { - "description": "SIGHASH SINGLE", - "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100e822f152bb15a1d623b91913cd0fb915e9f85a8dc6c26d51948208bbc0218e800220255f78549d9614c88eac9551429bc00224f22cdcb41a3af70d52138f7e98d333032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a47304402206f37f79adeb86e0e2da679f79ff5c3ba206c6d35cd9a21433f0de34ee83ddbc00220118cabbac5d83b3aa4c2dc01b061e4b2fe83750d85a72ae6a1752300ee5d9aff032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006a473044022042ac843d220a56b3de05f24c85a63e71efa7e5fc7c2ec766a2ffae82a88572b0022051a816b317313ea8d90010a77c3e02d41da4a500e67e6a5347674f836f528d82032102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", - "version": 1, - "inputs": [ - { - "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 3 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "057ef664f0e1108f270729e53a62e4cb1a7480d9a87f543c6a1a785a1e492c65", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 3 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "35db95c66634c3497a277c1f08ed71a0cb53195ffecbf9798cdde4a30f27fab9", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 3 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "outputs": [ - { - "value": 20000, - "script": "OP_DUP OP_HASH160 9ed1f577c60e4be1dbf35318ec12f51d25e85773 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 30000, - "script": "OP_DUP OP_HASH160 fb407e88c48921d5547d899e18a7c0a36919f54d OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 50000, - "script": "OP_DUP OP_HASH160 04ccb4eed8cfa9f6e394e945178960f5ccddb387 OP_EQUALVERIFY OP_CHECKSIG" - } - ] - }, - { - "description": "SIGHASH SINGLE|ANYONECANPAY", - "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100d05a3b6cf2f0301000b0e45c09054f2c61570ce8798ebf571eef72da3b1c94a1022016d7ef3c133fa703bae2c75158ea08d335ac698506f99b3c369c37a9e8fc4beb832102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006b483045022100ee6bf07b051001dcbfa062692a40adddd070303286b714825b3fb4693dd8fcdb022056610885e5053e5d47f2be3433051305abe7978ead8f7cf2d0368947aff6b307832102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b483045022100cfc930d5b5272d0220d9da98fabec97b9e66306f735efa837f43f6adc675cad902202f9dff76b8b9ec8f613d46094f17f64d875804292d8804aa59fd295b6fc1416b832102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", - "version": 1, - "inputs": [ - { - "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 131 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "057ef664f0e1108f270729e53a62e4cb1a7480d9a87f543c6a1a785a1e492c65", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 131 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "35db95c66634c3497a277c1f08ed71a0cb53195ffecbf9798cdde4a30f27fab9", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 131 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "outputs": [ - { - "value": 20000, - "script": "OP_DUP OP_HASH160 9ed1f577c60e4be1dbf35318ec12f51d25e85773 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 30000, - "script": "OP_DUP OP_HASH160 fb407e88c48921d5547d899e18a7c0a36919f54d OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 50000, - "script": "OP_DUP OP_HASH160 04ccb4eed8cfa9f6e394e945178960f5ccddb387 OP_EQUALVERIFY OP_CHECKSIG" - } - ] - }, - { - "description": "SIGHASH NONE", - "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006b483045022100e7f0a1ddd2c0b81e093e029b8a503afa27fe43549b0668d2141abf35eb3a63be022037f12d12cd50fc94a135f933406a8937557de9b9566a8841ff1548c1b6984531022102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a473044022008451123ec2535dab545ade9d697519e63b28df5e311ea05e0ce28d39877a7c8022061ce5dbfb7ab478dd9e05b0acfd959ac3eb2641f61958f5d352f37621073d7c0022102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006a47304402205c001bcdfb35c70d8aa3bdbc75399afb72eb7cf1926ca7c1dfcddcb4d4d3e0f8022028992fffdcd4e9f34ab726f97c24157917641c2ef99361f588e3d4147d46eea5022102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", - "version": 1, - "inputs": [ - { - "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 2 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "057ef664f0e1108f270729e53a62e4cb1a7480d9a87f543c6a1a785a1e492c65", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 2 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "35db95c66634c3497a277c1f08ed71a0cb53195ffecbf9798cdde4a30f27fab9", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 2 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "outputs": [ - { - "value": 20000, - "script": "OP_DUP OP_HASH160 9ed1f577c60e4be1dbf35318ec12f51d25e85773 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 30000, - "script": "OP_DUP OP_HASH160 fb407e88c48921d5547d899e18a7c0a36919f54d OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 50000, - "script": "OP_DUP OP_HASH160 04ccb4eed8cfa9f6e394e945178960f5ccddb387 OP_EQUALVERIFY OP_CHECKSIG" - } - ] - }, - { - "description": "SIGHASH NONE | ANYONECANPAY", - "txHex": "01000000037db7f0b2a345ded6ddf28da3211a7d7a95a2943e9a879493d6481b7d69613f04010000006a47304402204ed272952177aaa5a1b171c2ca5a7a3d300ffcd7e04b040c0baaa4e3561862a502207e65a5b8f99c8a632b186c8a60496a12bf3116f51909b7497413aefdc3be7bf6822102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff652c491e5a781a6a3c547fa8d980741acbe4623ae52907278f10e1f064f67e05000000006a47304402203ec365300cc67602f4cc5be027959d3667b48db34c6c87d267c94a7e210d5c1f02204843350311c0a9711cad1960b17ce9e323a1ce6f37deefc3ffe63082d480be92822102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffffb9fa270fa3e4dd8c79f9cbfe5f1953cba071ed081f7c277a49c33466c695db35000000006b48304502210084f86f905c36372eff9c54ccd509a519a3325bcace8abfeed7ed3f0d579979e902201ff330dd2402e5ca9989a8a294fa36d6cf3a093edb18d29c9d9644186a3efeb4822102f1c7eac9200f8dee7e34e59318ff2076c8b3e3ac7f43121e57569a1aec1803d4ffffffff03204e0000000000001976a9149ed1f577c60e4be1dbf35318ec12f51d25e8577388ac30750000000000001976a914fb407e88c48921d5547d899e18a7c0a36919f54d88ac50c30000000000001976a91404ccb4eed8cfa9f6e394e945178960f5ccddb38788ac00000000", - "version": 1, - "inputs": [ - { - "txId": "043f61697d1b48d69394879a3e94a2957a7d1a21a38df2ddd6de45a3b2f0b77d", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 130 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "057ef664f0e1108f270729e53a62e4cb1a7480d9a87f543c6a1a785a1e492c65", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 130 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "txId": "35db95c66634c3497a277c1f08ed71a0cb53195ffecbf9798cdde4a30f27fab9", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzRGFiqhXB7SyX6idHQkt77B8mX7adnujdg3VG47jdVK2x4wbUYg", - "hashType": 130 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 0de1f9b92d2ab6d8ead83f9a0ff5cf518dcb03b8 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "outputs": [ - { - "value": 20000, - "script": "OP_DUP OP_HASH160 9ed1f577c60e4be1dbf35318ec12f51d25e85773 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 30000, - "script": "OP_DUP OP_HASH160 fb407e88c48921d5547d899e18a7c0a36919f54d OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 50000, - "script": "OP_DUP OP_HASH160 04ccb4eed8cfa9f6e394e945178960f5ccddb387 OP_EQUALVERIFY OP_CHECKSIG" - } - ] - }, - { - "description": "SIGHASH V0+V1, (P2PKH, P2WPKH) -> 2x P2PKH", - "txHex": "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000484730440220691a19d365c8d75f921346c70271506bde136f13a4b566dd796902c262e2ec6d02202b00c4aa030eedf294552bdfc163936d2f4e91c59e7798c4471250cf07cb859501eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff0230f45e13000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac00e9a435000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac0002483045022100fddd014889f18d489b5400bfa8cb0a32301a768d934b1a0e2b55398119f26cab02207676c64c16ffa7ffaaf8e16b3b74e916687eebdfdb36b9b7997e838384d464640121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000", - "version": 1, - "inputs": [ - { - "txId": "9f96ade4b41d5433f4eda31e1738ec2b36f6e7d1420d94a6af99801a88f7f7ff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pk", - "keyPair": "L3Wh2WPg21MWqzMFYsVC7PeBXcq1ow32KRccRihnTUnAhJaZUvg1", - "hashType": 1 - } - ], - "sequence": 4294967278, - "prevTxScript": "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432 OP_CHECKSIG" - }, - { - "txId": "8ac60eb9575db5b2d987e29f301b5b819ea83a5c6579d282d189cc04b8e151ef", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2wpkh", - "keyPair": "KzVTBhbMaKrAYagJ11VdTaBrb6yzLykLGyuMBkf9sCFPDxdT8shL", - "hashType": 1, - "value": 600000000 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_0 1d0f172a0ecb48aee1be1f2687d2963ae33f71a1" - } - ], - "outputs": [ - { - "value": 324990000, - "script": "OP_DUP OP_HASH160 8280b37df378db99f66f85c95a783a76ac7a6d59 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 900000000, - "script": "OP_DUP OP_HASH160 3bde42dbee7e4dbe6a21b2d50ce2f0167faa8159 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 17 - }, - { - "description": "SIGHASH V0+V1, P2SH(P2WPKH) -> P2PKH", - "txHex": "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000", - "version": 1, - "inputs": [ - { - "txId": "77541aeb3c4dac9260b68f74f44c973081a9d4cb2ebe8038b2d70faa201b6bdb", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2sh-p2wpkh", - "keyPair": "L57KYn5isHFThD4cohjJgLTZA2vaxnMMKWngnzbttF159yH9dARf", - "hashType": 1, - "redeemScript": "OP_0 79091972186c449eb1ded22b78e40d009bdf0089", - "value": 1000000000 - } - ], - "sequence": 4294967294, - "prevTxScript": "OP_HASH160 4733f37cf4db86fbc2efed2500b4f4e49f312023 OP_EQUAL" - } - ], - "outputs": [ - { - "value": 199996600, - "script": "OP_DUP OP_HASH160 a457b684d7f0d539a46a45bbc043f35b59d0d963 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 800000000, - "script": "OP_DUP OP_HASH160 fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 1170 - }, - { - "description": "Sighash V1: P2SH(P2WSH(P2MS 6/6))", - "txHex": "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff02e6312761010000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688ac583e0f00000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac0800483045022100f902f491c4df15199e584790ae8c7202569a977accac0a09fa3f4f3b6ec3517602205961a951c4a12fa966da67b6fd75975b9de156b9895f8ab5f289ecaee12b9b3501473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502483045022100bd5294e145d729e9593f49079b74e6e4b8aeba63440408595ce0949d5c6450a702207f9c9fb45907fe0180d3f4bee499006007bb90894b5f824a26dfa5d3afec543303483045022100febf9409d7f3c091ddc4d296a483aae7b3d2a91d38f6ea2a153f7ff085fe7766022078d11972c74cd78f816152463a5e1a5d986dfb94b55cf5f7242e4f6d5df000ff81483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a088247304402201a0e125aed6a700e45d6c86017d5a9d2264c8079319d868f3f163f5d63cb5bfe02200887608f2322ca0d82df67316275371028b0b21750417d594117963fe23b67ec83cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000", - "version": 1, - "inputs": [ - { - "txId": "6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "L15NqbRvcqso8ZCqD8aFaZV3CTypw6svjk8oCWsAfMmNViahS2Mw", - "hashType": 1, - "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", - "redeemScript": "OP_0 a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54", - "value": 987654321 - }, - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "Kwpf3fycToLH1ymZUkezFrYwTjhKaucHD861Ft5A4Tih855LBxVx", - "hashType": 2, - "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", - "redeemScript": "OP_0 a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54", - "value": 987654321 - }, - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "L1EV111k2WzNTapY2etd1TaB2aWbjUgouko9YyipS2S8H8WdGkQi", - "hashType": 3, - "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", - "redeemScript": "OP_0 a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54", - "value": 987654321 - }, - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "KwuvEmpBtJaw8SQLnpi3CoEHZJvv33EnYBHn13VcDuwprJqmkfSH", - "hashType": 129, - "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", - "redeemScript": "OP_0 a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54", - "value": 987654321 - }, - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "L5kdM8eWyfj8pdRDWA8j5SmBwAQt2yyhqjb2ZZQxtRGJfCquC6TB", - "hashType": 130, - "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", - "redeemScript": "OP_0 a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54", - "value": 987654321 - }, - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "KyT4JbJVRy5FZ6ZEZhkaocP2JSBXiF7X3Cx6DBAGLrydR9fiXQUK", - "hashType": 131, - "witnessScript": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", - "redeemScript": "OP_0 a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54", - "value": 987654321 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_HASH160 9993a429037b5d912407a71c252019287b8d27a5 OP_EQUAL" - } - ], - "outputs": [ - { - "value": 5924925926, - "script": "OP_DUP OP_HASH160 389ffce9cd9ae88dcc0631e88a821ffdbe9bfe26 OP_EQUALVERIFY OP_CHECKSIG" - }, - { - "value": 999000, - "script": "OP_DUP OP_HASH160 7480a33f950689af511e6e84c138dbbd3c3ee415 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2PK", - "txHex": "010000000193aef40ae141694895e99e18e49d0181b086dd7c011c0241175c6eaf320099970000000049483045022100e57eba5380dcc8a7bdb5370b423dadd43070e1ca268f94bc97b2ded55ca45e9502206a43151c8af03a00f0ac86526d07981e303fc0daea8c6ed435abe8961533046d01ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", - "version": 1, - "inputs": [ - { - "txId": "97990032af6e5c1741021c017cdd86b081019de4189ee995486941e10af4ae93", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pk", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1 - } - ], - "sequence": 4294967295, - "prevTxScript": "038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_CHECKSIG" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2SH(P2PK)", - "txHex": "0100000001a30e865fa60f6c25a8b218bb5a6b9acc7cf3f1db2f2e3a7114b51af5d6ae811f000000006c473044022026d2b56b6cb0269bf4e80dd655b9e917019e2ccef57f4b858d03bb45a2da59d9022010519a7f327f03e7c9613e0694f929544af29d3682e7ec8f19147e7a86651ecd012321038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bacffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", - "version": 1, - "inputs": [ - { - "txId": "1f81aed6f51ab514713a2e2fdbf1f37ccc9a6b5abb18b2a8256c0fa65f860ea3", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2pk", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "redeemScript": "038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_CHECKSIG" - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_HASH160 c99d9ebb5a4828e4e1b606dd6a51a2babebbdc09 OP_EQUAL" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2WSH(P2PK)", - "txHex": "010000000001014533a3bc1e039bd787656068e135aaee10aee95a64776bfc047ee6a7c1ebdd2f0000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac02473044022039725bb7291a14dd182dafdeaf3ea0d5c05c34f4617ccbaa46522ca913995c4e02203b170d072ed2e489e7424ad96d8fa888deb530be2d4c5d9aaddf111a7efdb2d3012321038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bac00000000", - "version": 1, - "inputs": [ - { - "txId": "2fddebc1a7e67e04fc6b77645ae9ae10eeaa35e168606587d79b031ebca33345", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2wsh-p2pk", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "witnessScript": "038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_CHECKSIG", - "value": 80000 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_0 0f9ea7bae7166c980169059e39443ed13324495b0d6678ce716262e879591210" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2SH(P2WSH(P2PK))", - "txHex": "01000000000101e0779d448aaa203a96b3de14d0482e26dd75a4278ae5bb6d7cc18e6874f3866000000000232200200f9ea7bae7166c980169059e39443ed13324495b0d6678ce716262e879591210ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac024730440220014207a5f0601ed7b3c3f9d82309b32e8f76dd6776a55cb5f8684b9ff029e0850220693afd7b69471b51d9354cc1a956b68b8d48e32f6b0ad7a19bb5dd3e4499179a012321038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bac00000000", - "version": 1, - "inputs": [ - { - "txId": "6086f374688ec17c6dbbe58a27a475dd262e48d014deb3963a20aa8a449d77e0", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2wsh-p2pk", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "redeemScript": "OP_0 0f9ea7bae7166c980169059e39443ed13324495b0d6678ce716262e879591210", - "witnessScript": "038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_CHECKSIG", - "value": 80000 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_HASH160 6d185c7042d01ea8276dc6be6603101dc441d8a4 OP_EQUAL" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2PKH", - "txHex": "010000000176d7b05b96e69d9760bacf14e496ea01085eff32be8f4e08b299eb92057826e5000000006b4830450221009bd6ff2561437155913c289923175d3f114cca1c0e2bc5989315d5261502c2c902201b71ad90dce076a5eb872330ed729e7c2c4bc2d0513efff099dbefb3b62eab4f0121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2bffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", - "version": 1, - "inputs": [ - { - "txId": "e526780592eb99b2084e8fbe32ff5e0801ea96e414cfba60979de6965bb0d776", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2SH(P2PKH)", - "txHex": "01000000014b9ffc17c3cce03ee66980bf32d36aaa13462980c3af9d9d29ec6b97ab1c91650000000084473044022003d738d855d0c54a419ac62ebe1a1c0bf2dc6993c9585adb9a8666736658107002204d57ff62ee7efae6df73430bba62494faeba8c125a4abcf2488757a4f8877dd50121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b1976a914851a33a5ef0d4279bd5854949174e2c65b1d450088acffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", - "version": 1, - "inputs": [ - { - "txId": "65911cab976bec299d9dafc380294613aa6ad332bf8069e63ee0ccc317fc9f4b", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2pkh", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "redeemScript": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_HASH160 2162ff7c23d47a0c331f95c67d7c3e22abb12a02 OP_EQUAL" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2WSH(P2PKH)", - "txHex": "0100000000010123539877e39a273819006de1c433e09f9e9af201fc178dd0f2cf2eaa5ad53b480000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac03483045022100f02a82b0a94a5d5dc4d2127ac34be62cb066713d71d56bdf5ef7810ab57a157302205f24abdde1dab554a02edcf378e98828024e57272e5e474a5b04accdca080a030121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b1976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", - "version": 1, - "inputs": [ - { - "txId": "483bd55aaa2ecff2d08d17fc01f29a9e9fe033c4e16d001938279ae377985323", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2wsh-p2pkh", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "witnessScript": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG", - "value": 80000 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_0 578db4b54a6961060b71385c17d3280379a557224c52b11b19a3a1c1eef606a0" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2SH(P2WSH(P2PKH))", - "txHex": "01000000000101363dfbfe2566db77e3b1195bedf1d0daeb9ce526cd7611ba81759b2654ce415c0000000023220020578db4b54a6961060b71385c17d3280379a557224c52b11b19a3a1c1eef606a0ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac03483045022100c8bd5ebb26ba6719158650c3e7c5e80be4c886ba025c44cc41f5149b3114705a02203ac6e1f38f6c081d506f28f1b5e38ebec9e0f0fa911d0e3f68d48d8b0e77b34b0121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b1976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", - "version": 1, - "inputs": [ - { - "txId": "5c41ce54269b7581ba1176cd26e59cebdad0f1ed5b19b1e377db6625fefb3d36", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2wsh-p2pkh", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "witnessScript": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG", - "redeemScript": "OP_0 578db4b54a6961060b71385c17d3280379a557224c52b11b19a3a1c1eef606a0", - "value": 80000 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_HASH160 44a641c4e06eb6118c99e5ed29954b705b50fb6a OP_EQUAL" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2MS 1/1", - "txHex": "010000000179310ec46e734b3490ee839c5ae4a09d28561ee9fff2d051f733d201f958d6d2000000004a00483045022100d269531f120f377ed2f94f42bef893ff2fe6544ac97fb477fa291bc6cfb7647e02200983f6a5bbd4ce6cf97f571995634805a7324cc5d8353ed954fa62477b0fcd0901ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", - "version": 1, - "inputs": [ - { - "txId": "d2d658f901d233f751d0f2ffe91e56289da0e45a9c83ee90344b736ec40e3179", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2ms", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_1 038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_1 OP_CHECKMULTISIG" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2SH(P2MS)", - "txHex": "010000000152882c661c49dd2f53bd9ced7e9f44b184888ad2fe7d86737f0efaa7aecdced1000000006f00473044022025f2e161f0a97888df948f4dcc7c04fe502510b8d8260ca9920f38d55e4d17720220271b6843224b3a34542a4df31781d048da56ee46b8c5fb99043e30abd527b2d801255121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b51aeffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac00000000", - "version": 1, - "inputs": [ - { - "txId": "d1cecdaea7fa0e7f73867dfed28a8884b1449f7eed9cbd532fdd491c662c8852", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "redeemScript": "OP_1 038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_1 OP_CHECKMULTISIG" - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_HASH160 38c064c6387d1071eeb5c3d90350054aea0b3fc1 OP_EQUAL" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2WSH(P2MS)", - "txHex": "01000000000101c1eced6216de0889d4629ff64a8af8e8ec6d0b414de0c57b46c02cc303d321fe0000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac0300483045022100d4c0cbdb45915b8a3162362fa5f74556de919aeda5337fc44a7fb000e833460d022017742c37d7a061e2ae3a086c7c585c9c85e5d31af468d3e00045c0f35b8f8eb601255121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b51ae00000000", - "version": 1, - "inputs": [ - { - "txId": "fe21d303c32cc0467bc5e04d410b6dece8f88a4af69f62d48908de1662edecc1", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2wsh-p2ms", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "witnessScript": "OP_1 038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_1 OP_CHECKMULTISIG", - "value": 80000 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_0 1b8c0c2878c5634c3ce738cdc568c592e99783dbd28ff4c6cb5b7b4675d9ee99" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2SH(P2WSH(P2MS))", - "txHex": "010000000001013a5a2ab0223d3b504b52af76d650329750666fbf1be13d4cb08d0d9fc550a47d00000000232200201b8c0c2878c5634c3ce738cdc568c592e99783dbd28ff4c6cb5b7b4675d9ee99ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac0300483045022100c97a5e205ce0023d3d44f846abf1f0e21b6f2646bd2496bbe92e4333fe4401be02201247e047d669f713582713e35d2eba430abc3d75a924bb500362bf47d6234ed501255121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b51ae00000000", - "version": 1, - "inputs": [ - { - "txId": "7da450c59f0d8db04c3de11bbf6f6650973250d676af524b503b3d22b02a5a3a", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "witnessScript": "OP_1 038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b OP_1 OP_CHECKMULTISIG", - "redeemScript": "OP_0 1b8c0c2878c5634c3ce738cdc568c592e99783dbd28ff4c6cb5b7b4675d9ee99", - "value": 80000 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_HASH160 cc6ea17c33de7996471e40892acdd6e5f61b9b6f OP_EQUAL" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2WPKH -> P2PKH", - "txHex": "0100000000010133defbe3e28860007ff3e21222774c220cb35d554fa3e3796d25bf8ee983e1080000000000ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac0248304502210097c3006f0b390982eb47f762b2853773c6cedf83668a22d710f4c13c4fd6b15502205e26ef16a81fc818a37f3a34fc6d0700e61100ea6c6773907c9c046042c440340121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b00000000", - "version": 1, - "inputs": [ - { - "txId": "08e183e98ebf256d79e3a34f555db30c224c772212e2f37f006088e2e3fbde33", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2wpkh", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "value": 80000 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_0 851a33a5ef0d4279bd5854949174e2c65b1d4500" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "P2SH(P2WPKH) -> P2PKH", - "txHex": "010000000001015df9a0b9ade2d835881704e0f53b51a4b19ecfc794ea1f3555783dd7f68659ce0000000017160014851a33a5ef0d4279bd5854949174e2c65b1d4500ffffffff0160ea0000000000001976a914851a33a5ef0d4279bd5854949174e2c65b1d450088ac02483045022100cb3929c128fec5108071b662e5af58e39ac8708882753a421455ca80462956f6022030c0f4738dd1a13fc7a34393002d25c6e8a6399f29c7db4b98f53a9475d94ca20121038de63cf582d058a399a176825c045672d5ff8ea25b64d28d4375dcdb14c02b2b00000000", - "version": 1, - "inputs": [ - { - "txId": "ce5986f6d73d7855351fea94c7cf9eb1a4513bf5e004178835d8e2adb9a0f95d", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2wpkh", - "keyPair": "L2FroWqrUgsPpTMhpXcAFnVDLPTToDbveh3bhDaU4jhe7Cw6YujN", - "hashType": 1, - "redeemScript": "OP_0 851a33a5ef0d4279bd5854949174e2c65b1d4500", - "value": 80000 - } - ], - "sequence": 4294967295, - "prevTxScript": "OP_HASH160 0d061ae2c8ad224a81142a2e02181f5173b576b3 OP_EQUAL" - } - ], - "outputs": [ - { - "value": 60000, - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "locktime": 0 - }, - { - "description": "Defaults to version 2", - "txHex": "0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006b483045022100d090736fce4387d25f9d7ba85270a3859947dbd0a19f347467c3a596e06c132402204b14c29d128f824bee953bdaf2ed4e16c7cff0b24bddb4add94b4b6c42773d8801210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": null, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 10000 - } - ], - "locktime": 0 - }, - { - "description": "P2SH(P2WSH(P2MS 2/2)), incomplete", - "network": "testnet", - "txHex": "010000000001012915794541ffa77ca795ec7c23ee989a63ccd1a71fab73e1c27ed20c4b6c69a4010000002322002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5afffffffff01b8820100000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac040047304402203b334650f1f13574a1c2edc76421867f7252950968bf0293c8b3ed086ab89e3d0220565cafab0a5044617e94756b948241525b2483a52504e1064d29f641fb18129e010047522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae00000000", - "version": 1, - "incomplete": true, - "inputs": [ - { - "txId": "a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS", - "witnessScript": "OP_2 02bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e2 02d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea19 OP_2 OP_CHECKMULTISIG", - "redeemScript": "OP_0 24376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af", - "value": 100000 - } - ], - "prevTxScript": "OP_HASH160 b64f1a3eacc1c8515592a6f10457e8ff90e4db6a OP_EQUAL" - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 99000 - } - ] - }, - { - "description": "P2SH(P2WSH(P2MS 2/2))", - "network": "testnet", - "txHex": "010000000001012915794541ffa77ca795ec7c23ee989a63ccd1a71fab73e1c27ed20c4b6c69a4010000002322002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5afffffffff01b8820100000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac040047304402203b334650f1f13574a1c2edc76421867f7252950968bf0293c8b3ed086ab89e3d0220565cafab0a5044617e94756b948241525b2483a52504e1064d29f641fb18129e0148304502210096e859827fb629b6547658c613f7c8298de151513d74b224560aa8608d521d600220736fb5564322237716ec940de44c67c428198adf5dedfda183c17aa77cd28d640147522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea1952ae00000000", - "version": 1, - "incomplete": true, - "inputs": [ - { - "txId": "a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS", - "witnessScript": "OP_2 02bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e2 02d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea19 OP_2 OP_CHECKMULTISIG", - "redeemScript": "OP_0 24376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af", - "value": 100000 - }, - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "cTUFsNeVd8TKU4yREN8nMdViNnHyNvCCYVRmRUmkMLgomiMWTiii", - "witnessScript": "OP_2 02bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e352e2 02d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532b9ea19 OP_2 OP_CHECKMULTISIG", - "redeemScript": "OP_0 24376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af", - "value": 100000 - } - ], - "prevTxScript": "OP_HASH160 b64f1a3eacc1c8515592a6f10457e8ff90e4db6a OP_EQUAL" - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 99000 - } - ] - }, - { - "description": "P2SH(P2WSH(P2MS 2/3)) -> P2PKH", - "network": "testnet", - "txHex": "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01000000232200201b48bf145648b9492ecd6d76754ea3def4b90e22e4ef7aee9ca291b2de455701ffffffff01f07e0e00000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac0400473044022036c9ecb03cb04c09be1f52766725dcfe9a815973bd2f34ce19a345f2d925a45502207b90737852d2508db104ad17612de473687e67928c045555a1ed8d495c0570d901483045022100aec0e58e4e597b35ca5a727702a0da3d4f2ef4759914da7fc80aecb3c479a6d902201ec27ea8dcca4b73ee81e4b627f52f9e627c3497f61e4beeb98f86e02979640a0169522103c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc5787721020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b02102d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc914953ae00000000", - "version": 1, - "stages": [ - "01000000000101ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01000000232200201b48bf145648b9492ecd6d76754ea3def4b90e22e4ef7aee9ca291b2de455701ffffffff01f07e0e00000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac0500473044022036c9ecb03cb04c09be1f52766725dcfe9a815973bd2f34ce19a345f2d925a45502207b90737852d2508db104ad17612de473687e67928c045555a1ed8d495c0570d901000069522103c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc5787721020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b02102d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc914953ae00000000" - ], - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "cUxccFVBdJRq6HnyxiFMd8Z15GLThXaNLcnPBgoXLEv9iX6wuV2b", - "witnessScript": "OP_2 03c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc57877 020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b0 02d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc9149 OP_3 OP_CHECKMULTISIG", - "redeemScript": "OP_0 1b48bf145648b9492ecd6d76754ea3def4b90e22e4ef7aee9ca291b2de455701", - "value": 1000000, - "stage": true - }, - { - "prevOutScriptType": "p2sh-p2wsh-p2ms", - "keyPair": "cVSNe9ZdZRsRvEBL8YRR7YiZmH4cLsf5FthgERWkZezJVrGseaXy", - "witnessScript": "OP_2 03c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc57877 020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b0 02d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc9149 OP_3 OP_CHECKMULTISIG", - "redeemScript": "OP_0 1b48bf145648b9492ecd6d76754ea3def4b90e22e4ef7aee9ca291b2de455701", - "value": 1000000 - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 950000 - } - ] - } - ], - "fromTransaction": [ - { - "description": "Transaction w/ P2SH(P2MS 2/2) -> OP_RETURN | 1 OP_0 fixes to 2 OP_0, no signatures", - "network": "testnet", - "incomplete": true, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "scriptSig": "OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae", - "scriptSigAfter": "OP_0 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Transaction w/ P2SH(P2MS 2/2) -> OP_RETURN | missing OP_0, 1 signature", - "network": "testnet", - "incomplete": true, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "scriptSig": "OP_0 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae", - "scriptSigAfter": "OP_0 OP_0 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Transaction w/ P2SH(P2MS 2/2) -> OP_RETURN | no OP_0, 2 signatures", - "network": "testnet", - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae", - "scriptSigAfter": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - } - ], - "fromTransactionSequential": [ - { - "description": "Transaction w/ P2SH(P2MS 2/3) -> ?", - "network": "testnet", - "txHex": "0100000001b033b2214568b49fda417371aba0634b0303a2b6a19884c25d03d0b91bdbe231000000006f000000004c6952210258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a9121038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f2103b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d953aeffffffff0200e1f5050000000017a914a9974100aeee974a20cda9a2f545704a0ab54fdc87c72831010000000017a9149f57a6712ef023f85ffac631ed4263b977b2d0678700000000", - "txHexAfter": "0100000001b033b2214568b49fda417371aba0634b0303a2b6a19884c25d03d0b91bdbe23100000000b60000004730440220793d87f2a8afeb856816efa38984418c692c15170e99ca371f547454079c0dd3022074ae95e438fee1f37619fabe0ce1083c3be0d65c3defb5337833d50fdc694b13014c6952210258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a9121038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f2103b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d953aeffffffff0200e1f5050000000017a914a9974100aeee974a20cda9a2f545704a0ab54fdc87c72831010000000017a9149f57a6712ef023f85ffac631ed4263b977b2d0678700000000", - "incomplete": true, - "inputs": [ - { - "vout": 0, - "scriptSig": "OP_0 OP_0 OP_0 OP_0 52210258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a9121038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f2103b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d953ae", - "scriptSigAfter": "OP_0 OP_0 OP_0 30440220793d87f2a8afeb856816efa38984418c692c15170e99ca371f547454079c0dd3022074ae95e438fee1f37619fabe0ce1083c3be0d65c3defb5337833d50fdc694b1301 52210258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a9121038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f2103b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d953ae", - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "cTkcnMZoFYH1UgumzCFHv2veLMNN1PaJyHHUxFT127zhNGBqqEZ2", - "redeemScript": "OP_2 0258db1bb3801f1ecde47602143beaeb9cac93251724b8e589fae5c08c1a399a91 038e803e3d84cfc821cc8bf46233a9c2bb359d529db0bcdd3f1a4f38678dd02d7f 03b83e59d848407d7f62a82c99905f5ca3e8e8f5d6400eb78a0b4b067aea0720d9 OP_3 OP_CHECKMULTISIG" - } - ] - } - ] - } - ], - "classification": { - "hex": "01000000059c06fb641a8cd69be81ca91e68d8a115cb698396876ecd77120ec1e4ab9002279f000000b500483045022100d58f828ab39cfac592f89fe372fb520992975218698c683a893f29e39cf0080302207cc0485dab5ce621089bdd15e1f15db0ecbde8dd4bb661bcf0e3af6ecab075e6014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffff0821dc00213d2b7993f8f2a1553800c6f2f31106da176505d0ade467b68401d795000000b400473044022028e937a7bba888fe3428f442f6e22d92ce2ddba01548c38780d40890fa6cc305022043204d0bcfb1150b045d54cf9b13462e44e2ef47fee03d3cea08e84a8060fc30014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffffaa997ac385dc666af1f5947ef615431024eb314cac2308d5e1b903e28ca466f499000000b50048304502210093efc26facedc5f51e304aa270a7b4f1a911b2d912c3674e5c6e2ad4ac7a410402201cf0b62c240461902f9f16d8a0bc3a210b7bfcd2c06523dd4b4b63be22e85252014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffffd9f61bf98a021ee144f33ba5a6b04274de8fcb5c05f1ff7c12367fb7a608b2dd9e000000b4004730440220456e1201c1fa727288cba7fa0054dc02d8dd6c7418cae1e97006ef0652891c9202201192d0fbf3a9c00afb99a415f2bf515509e1150805acd8de95c496c27cb6570f014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffff1f8119e3bc7c2f451feaa79f42ec5a63502afb425c253c935e43d217d5c29bdea1000000b500483045022100f669004f770490093eba4ac4903cb7581f7d18ea9245c538585ef5367e520e4702205485fafe0be178563a599d41e0cc172bb01314ed65d0e48df19a5258f17bdbc4014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753aeffffffff0380f0fa02000000001976a91439692085cf9d27e8c1cf63e76bd32d9bd15cab8b88ac50c300000000000017a9147204bb26950ce1595255897f63d205779f033f3e875b5409000000000017a9142538893d984a4b5695e4bfde1a90a9f02fabf8e38700000000" - }, - "multisig": [ - { - "description": "P2SH(P2MS 2/2), signed in correct order", - "network": "testnet", - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 0, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 1, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", - "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "P2SH(P2MS 2/2), signed in shuffled order", - "network": "testnet", - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 1, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", - "scriptSig": "OP_0 OP_0 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 0, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "P2SH(P2MS 2/2), manually messed up order of signatures", - "network": "testnet", - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 0, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 1, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", - "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "P2SH(P2MS 2/3), signed by key 1 and 2", - "network": "testnet", - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100ae06d8269b79b5cfa0297d1d88261b0061e52fc2814948c3aa05fa78ee76894302206e0c79a5c90569d8c72a542ef9a06471cbbcd2c651b312339983dfba4f8ff463014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 0, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 1, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", - "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100ae06d8269b79b5cfa0297d1d88261b0061e52fc2814948c3aa05fa78ee76894302206e0c79a5c90569d8c72a542ef9a06471cbbcd2c651b312339983dfba4f8ff46301 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "P2SH(P2MS 2/3), signed by key 1 and 3", - "network": "testnet", - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 0, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 2, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", - "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "P2SH(P2MS 2/3), signed by key 3 and 1", - "network": "testnet", - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 2, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", - "scriptSig": "OP_0 OP_0 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 0, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "P2SH(P2MS 2/3), signed by key 1 and 3, manually messed up order of signatures", - "network": "testnet", - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 0, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 2, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", - "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "P2SH(P2MS 2/3), signed by key 3 and 1, manually removing OP_0s", - "network": "testnet", - "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", - "version": 1, - "inputs": [ - { - "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", - "vout": 0, - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 2, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", - "scriptSig": "OP_0 OP_0 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "pubKeyIndex": 0, - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "scriptSigBefore": "OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae", - "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - } - ] - }, - "invalid": { - "build": [ - { - "exception": "Transaction has no inputs", - "inputs": [], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "exception": "Transaction has no outputs", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0 - } - ], - "outputs": [] - }, - { - "exception": "Transaction has absurd fees", - "inputs": [ - { - "txRaw": { - "inputs": [], - "outputs": [ - { - "address": "1C5XhB1UkFuyCV1CG9dmXaXGu3xDL4nAjv", - "value": 1000000000 - } - ], - "incomplete": true - }, - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzBQVXYUGDAvqG7VeU3C7ZMRYiwtsxSVVFcYGzKU9E4aUVDUquZU" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 ff99e06c1a4ac394b4e1cb3d3a4b2b47749e339a OP_EQUALVERIFY OP_CHECKSIG", - "value": 200000 - } - ] - }, - { - "description": "Incomplete transaction, nothing assumed", - "exception": "Transaction is not complete", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0 - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Incomplete transaction, known prevTxScript, thereby throws for missing signatures", - "exception": "Not enough information", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - } - ] - }, - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "prevTxScript": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG" - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Incomplete Transaction P2SH(P2MS 2/3), missing signature", - "exception": "Not enough signatures provided", - "network": "testnet", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG" - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Duplicate transaction outs", - "exception": "Duplicate TxOut: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:0", - "incomplete": true, - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0 - }, - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0 - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Transaction w/ invalid scripts", - "exception": "Invalid script", - "incomplete": true, - "txHex": "010000000100000000171a0000e028f2000000000050178500000000000d0000000e000000000000002009f691b2263260e71f363d1db51ff3100d285956a40cc0e4f8c8c2c4a80559b1ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" - }, - { - "description": "Complete transaction w/ non-standard inputs", - "exception": "Unknown input type", - "txHex": "010000000100000000171a0000e028f2000000000050178500000000000d0000000e00000000000000201ff691b2263260e71f363d1db51ff3100d285956a40cc0e4f8c8c2c4a80559b1ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" - } - ], - "sign": [ - { - "description": "Transaction w/ witness value mismatch", - "exception": "Input did not match witnessValue", - "network": "testnet", - "inputs": [ - { - "txHex": "01000000000101f7e6430096cd2790bac115aaab22c0a50fb0a1794305302e1a399e81d8d354f40200000000feffffff0340420f00000000001600145c7b8d623fba952d2387703d051d8e931a6aa0a18bda2702000000001976a9145a0ef60784137d03e7868d063b05424f2f43799f88ac40420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac0247304402205793a862d193264afc32713e2e14541e1ff9ebb647dd7e7e6a0051d0faa87de302205216653741ecbbed573ea2fc053209dd6980616701c27be5b958a159fc97f45a012103e877e7deb32d19250dcfe534ea82c99ad739800295cd5429a7f69e2896c36fcd2fcc0e00", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2wpkh", - "keyPair": "cQ6483mDWwoG8o4tn6nU9Jg52RKMjPUWXSY1vycAyPRXQJ1Pn2Rq", - "throws": true, - "value": 22500000000 - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 ff99e06c1a4ac394b4e1cb3d3a4b2b47749e339a OP_EQUALVERIFY OP_CHECKSIG", - "value": 6000000 - } - ] - }, - { - "description": "Too many signatures - P2PKH", - "exception": "Signature already exists", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" - }, - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Transaction w/ P2WPKH, signing with uncompressed public key", - "exception": "BIP143 rejects uncompressed public keys in P2WPKH or P2WSH", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_0 15a71ffa7b5bb70cddefcf364494071022efe390", - "signs": [ - { - "prevOutScriptType": "p2wpkh", - "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", - "value": 10000, - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_0 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", - "value": 10000 - } - ] - }, - { - "description": "Transaction w/ P2WSH(P2PK), signing with uncompressed public key", - "exception": "redeem.input or redeem.output contains uncompressed pubkey", - "inputs": [ - { - "txId": "2fddebc1a7e67e04fc6b77645ae9ae10eeaa35e168606587d79b031ebca33345", - "vout": 0, - "prevTxScript": "OP_0 5339df4de3854c4208376443ed075014ad996aa349ad6b5abf6c4d20f604d348", - "signs": [ - { - "prevOutScriptType": "p2wsh-p2pk", - "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", - "witnessScript": "04f56d09b32cefc818735150bf8560eefdaf30d2edb3fe557bf27682aedaed81bf9aaff7eeb496e088058ec548826c12b521dbb566a862d9b67677910c2b421e06 OP_CHECKSIG", - "value": 80000, - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG", - "value": 60000 - } - ] - }, - { - "description": "Transaction w/ P2SH(P2WSH(P2PK)), signing with uncompressed public key", - "exception": "redeem.input or redeem.output contains uncompressed pubkey", - "inputs": [ - { - "txId": "2fddebc1a7e67e04fc6b77645ae9ae10eeaa35e168606587d79b031ebca33345", - "vout": 0, - "prevTxScript": "OP_HASH160 5afe12b2827e3eac05fe3f17c59406ef262aa177 OP_EQUAL", - "signs": [ - { - "prevOutScriptType": "p2sh-p2wsh-p2pk", - "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", - "redeemScript": "OP_0 5339df4de3854c4208376443ed075014ad996aa349ad6b5abf6c4d20f604d348", - "witnessScript": "04f56d09b32cefc818735150bf8560eefdaf30d2edb3fe557bf27682aedaed81bf9aaff7eeb496e088058ec548826c12b521dbb566a862d9b67677910c2b421e06 OP_CHECKSIG", - "value": 80000, - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 851a33a5ef0d4279bd5854949174e2c65b1d4500 OP_EQUALVERIFY OP_CHECKSIG", - "value": 60000 - } - ] - }, - { - "exception": "nulldata not supported as redeemScript \\(OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474\\)", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2sh-p2pk", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "redeemScript": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "exception": "p2sh-p2pkh requires redeemScript", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "prevTxScript": "OP_HASH160 7f67f0521934a57d3039f77f9f32cf313f3ac74b OP_EQUAL", - "signs": [ - { - "prevOutScriptType": "p2sh-p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "exception": "p2wsh-p2pk requires witnessScript", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "prevTxScript": "OP_0 0f9ea7bae7166c980169059e39443ed13324495b0d6678ce716262e879591210", - "signs": [ - { - "prevOutScriptType": "p2wsh-p2pk", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "exception": "Inconsistent redeemScript", - "network": "testnet", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Inconsistent RedeemScript hash", - "exception": "Redeem script inconsistent with prevOutScript", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_HASH160 ffffffffffffffffffffffffffffffffffffffff OP_EQUAL", - "signs": [ - { - "prevOutScriptType": "p2sh-p2pkh", - "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", - "redeemScript": "OP_1", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_0 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", - "value": 10000 - } - ] - }, - { - "description": "Inconsistent WitnessScript hash", - "exception": "Witness script inconsistent with prevOutScript", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_0 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "signs": [ - { - "prevOutScriptType": "p2wsh-p2pkh", - "keyPair": "5JiHJJjdufSiMxbvnyNcKtQNLYH6SvUpQnRv9yZENFDWTQKQkzC", - "witnessScript": "OP_1", - "value": 10000, - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_0 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", - "value": 10000 - } - ] - }, - { - "exception": "scripthash not supported as redeemScript \\(OP_HASH160 7f67f0521934a57d3039f77f9f32cf313f3ac74b OP_EQUAL\\)", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2sh-p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "redeemScript": "OP_HASH160 7f67f0521934a57d3039f77f9f32cf313f3ac74b OP_EQUAL", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "exception": "input #0 is not of type p2sh-p2pkh: pubkeyhash", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "prevTxScript": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "signs": [ - { - "prevOutScriptType": "p2sh-p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Too many signatures - P2SH(P2MS 1/1)", - "exception": "Signature already exists", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf", - "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG" - }, - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf", - "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Wrong network for keyPair", - "exception": "Inconsistent network", - "network": "bitcoin", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", - "network": "testnet", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "description": "Wrong key pair for P2MS redeemScript", - "exception": "Key pair cannot sign for this input", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2sh-p2ms", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "exception": "input #0 is not of type p2pkh: nulldata", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "value": 1000 - } - ] - }, - { - "exception": "input #0 is not of type p2pk: nulldata", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_RETURN deadbeef", - "signs": [ - { - "prevOutScriptType": "p2pk", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_RETURN deadbeef", - "value": 1000 - } - ] - }, - { - "exception": "input #0 is not of type p2wpkh: nulldata", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_RETURN deadbeef", - "signs": [ - { - "prevOutScriptType": "p2wpkh", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_RETURN deadbeef", - "value": 1000 - } - ] - }, - { - "exception": "input #0 is not of type p2ms: nulldata", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_RETURN deadbeef", - "signs": [ - { - "prevOutScriptType": "p2ms", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_RETURN deadbeef", - "value": 1000 - } - ] - }, - { - "exception": "input #0 is not of type p2sh-p2wpkh: nulldata", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_RETURN deadbeef", - "signs": [ - { - "prevOutScriptType": "p2sh-p2wpkh", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_RETURN deadbeef", - "value": 1000 - } - ] - }, - { - "exception": "input #0 is not of type p2sh-p2pk: nulldata", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_RETURN deadbeef", - "signs": [ - { - "prevOutScriptType": "p2sh-p2pk", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_RETURN deadbeef", - "value": 1000 - } - ] - }, - { - "exception": "input #0 is not of type p2wsh-p2pk: nulldata", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_RETURN deadbeef", - "signs": [ - { - "prevOutScriptType": "p2wsh-p2pk", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_RETURN deadbeef", - "value": 1000 - } - ] - }, - { - "exception": "input #0 is not of type p2sh-p2wsh-p2pk: nulldata", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_RETURN deadbeef", - "signs": [ - { - "prevOutScriptType": "p2sh-p2wsh-p2pk", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_RETURN deadbeef", - "value": 1000 - } - ] - }, - { - "exception": "Unknown prevOutScriptType \"notvalidtype\"", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "prevTxScript": "OP_RETURN deadbeef", - "signs": [ - { - "prevOutScriptType": "notvalidtype", - "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "throws": true - } - ] - } - ], - "outputs": [ - { - "script": "OP_RETURN deadbeef", - "value": 1000 - } - ] - }, - { - "description": "Transaction w/ no outputs (but 1 SIGHASH_NONE)", - "exception": "Transaction needs outputs", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "hashType": 2 - } - ] - }, - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 1, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "throws": true - } - ] - } - ], - "outputs": [] - }, - { - "description": "Transaction w/ no outputs", - "exception": "Transaction needs outputs", - "inputs": [ - { - "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "vout": 0, - "signs": [ - { - "prevOutScriptType": "p2pkh", - "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "throws": true - } - ] - } - ], - "outputs": [] - } - ], - "fromTransaction": [ - { - "exception": "coinbase inputs not supported", - "txHex": "01000000010000000000000000000000000000000000000000000000000000000000000000000000006b483045022100a3b254e1c10b5d039f36c05f323995d6e5a367d98dd78a13d5bbc3991b35720e022022fccea3897d594de0689601fbd486588d5bfa6915be2386db0397ee9a6e80b601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" - } - ] - } -} diff --git a/test/transaction_builder.spec.ts b/test/transaction_builder.spec.ts deleted file mode 100644 index b07462e..0000000 --- a/test/transaction_builder.spec.ts +++ /dev/null @@ -1,1001 +0,0 @@ -import * as assert from 'assert'; -import { beforeEach, describe, it } from 'mocha'; -import { - ECPair, - networks as NETWORKS, - Transaction, - TransactionBuilder, -} from '..'; -import * as baddress from '../src/address'; -import * as payments from '../src/payments'; -import * as bscript from '../src/script'; - -console.warn = (): void => { - return; -}; // Silence the Deprecation Warning - -import * as fixtures from './fixtures/transaction_builder.json'; - -function constructSign( - f: any, - txb: any, - useOldSignArgs: any, -): TransactionBuilder { - const network = (NETWORKS as any)[f.network]; - const stages = f.stages && f.stages.concat(); - - f.inputs.forEach((input: any, index: number) => { - if (!input.signs) return; - input.signs.forEach((sign: any) => { - const keyPair = ECPair.fromWIF(sign.keyPair, network); - let redeemScript; - let witnessScript; - let witnessValue; - - if (sign.redeemScript) { - redeemScript = bscript.fromASM(sign.redeemScript); - } - - if (sign.value) { - witnessValue = sign.value; - } - - if (sign.witnessScript) { - witnessScript = bscript.fromASM(sign.witnessScript); - } - - if (useOldSignArgs) { - // DEPRECATED: v6 will remove this interface - txb.sign( - index, - keyPair, - redeemScript, - sign.hashType, - witnessValue, - witnessScript, - ); - } else { - // prevOutScriptType is required, see /ts_src/transaction_builder.ts - // The PREVOUT_TYPES constant is a Set with all possible values. - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: index, - keyPair, - redeemScript, - hashType: sign.hashType, - witnessValue, - witnessScript, - }); - } - - if (sign.stage) { - const tx = txb.buildIncomplete(); - assert.strictEqual(tx.toHex(), stages.shift()); - txb = TransactionBuilder.fromTransaction(tx, network); - } - }); - }); - - return txb; -} - -function construct( - f: any, - dontSign?: any, - useOldSignArgs?: any, -): TransactionBuilder { - const network = (NETWORKS as any)[f.network]; - const txb = new TransactionBuilder(network); - - if (Number.isFinite(f.version)) txb.setVersion(f.version); - if (f.locktime !== undefined) txb.setLockTime(f.locktime); - - f.inputs.forEach((input: any) => { - let prevTx; - if (input.txRaw) { - const constructed = construct(input.txRaw); - if (input.txRaw.incomplete) prevTx = constructed.buildIncomplete(); - else prevTx = constructed.build(); - } else if (input.txHex) { - prevTx = Transaction.fromHex(input.txHex); - } else { - prevTx = input.txId; - } - - let prevTxScript; - if (input.prevTxScript) { - prevTxScript = bscript.fromASM(input.prevTxScript); - } - - txb.addInput(prevTx, input.vout, input.sequence, prevTxScript); - }); - - f.outputs.forEach((output: any) => { - if (output.address) { - txb.addOutput(output.address, output.value); - } else { - txb.addOutput(bscript.fromASM(output.script), output.value); - } - }); - - if (dontSign) return txb; - return constructSign(f, txb, useOldSignArgs); -} - -// TODO: Remove loop in v6 -for (const useOldSignArgs of [false, true]) { - // Search for "useOldSignArgs" - // to find the second part of this console.warn replace - let consoleWarn: any; - if (useOldSignArgs) { - consoleWarn = console.warn; - // Silence console.warn during these tests - console.warn = (): undefined => undefined; - } - describe(`TransactionBuilder: useOldSignArgs === ${useOldSignArgs}`, () => { - // constants - const keyPair = ECPair.fromPrivateKey( - Buffer.from( - '0000000000000000000000000000000000000000000000000000000000000001', - 'hex', - ), - ); - const scripts = [ - '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', - '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', - ].map(x => { - return baddress.toOutputScript(x); - }); - const txHash = Buffer.from( - '0e7cea811c0be9f73c0aca591034396e7264473fc25c1ca45195d7417b36cbe2', - 'hex', - ); - - describe('fromTransaction', () => { - fixtures.valid.build.forEach(f => { - it('returns TransactionBuilder, with ' + f.description, () => { - const network = (NETWORKS as any)[f.network || 'bitcoin']; - - const tx = Transaction.fromHex(f.txHex); - const txb = TransactionBuilder.fromTransaction(tx, network); - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build(); - - assert.strictEqual(txAfter.toHex(), f.txHex); - assert.strictEqual(txb.network, network); - }); - }); - - fixtures.valid.fromTransaction.forEach(f => { - it('returns TransactionBuilder, with ' + f.description, () => { - const tx = new Transaction(); - - f.inputs.forEach(input => { - const txHash2 = Buffer.from(input.txId, 'hex').reverse() as Buffer; - - tx.addInput( - txHash2, - input.vout, - undefined, - bscript.fromASM(input.scriptSig), - ); - }); - - f.outputs.forEach(output => { - tx.addOutput(bscript.fromASM(output.script), output.value); - }); - - const txb = TransactionBuilder.fromTransaction(tx); - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build(); - - txAfter.ins.forEach((input, i) => { - assert.strictEqual( - bscript.toASM(input.script), - f.inputs[i].scriptSigAfter, - ); - }); - - txAfter.outs.forEach((output, i) => { - assert.strictEqual( - bscript.toASM(output.script), - f.outputs[i].script, - ); - }); - }); - }); - - fixtures.valid.fromTransactionSequential.forEach(f => { - it('with ' + f.description, () => { - const network = (NETWORKS as any)[f.network]; - const tx = Transaction.fromHex(f.txHex); - const txb = TransactionBuilder.fromTransaction(tx, network); - - tx.ins.forEach((input, i) => { - assert.strictEqual( - bscript.toASM(input.script), - f.inputs[i].scriptSig, - ); - }); - - constructSign(f, txb, useOldSignArgs); - const txAfter = f.incomplete ? txb.buildIncomplete() : txb.build(); - - txAfter.ins.forEach((input, i) => { - assert.strictEqual( - bscript.toASM(input.script), - f.inputs[i].scriptSigAfter, - ); - }); - - assert.strictEqual(txAfter.toHex(), f.txHexAfter); - }); - }); - - it('classifies transaction inputs', () => { - const tx = Transaction.fromHex(fixtures.valid.classification.hex); - const txb = TransactionBuilder.fromTransaction(tx); - - (txb as any).__INPUTS.forEach((i: any) => { - assert.strictEqual(i.prevOutType, 'scripthash'); - assert.strictEqual(i.redeemScriptType, 'multisig'); - }); - }); - - fixtures.invalid.fromTransaction.forEach(f => { - it('throws ' + f.exception, () => { - const tx = Transaction.fromHex(f.txHex); - - assert.throws(() => { - TransactionBuilder.fromTransaction(tx); - }, new RegExp(f.exception)); - }); - }); - }); - - describe('addInput', () => { - let txb: TransactionBuilder; - beforeEach(() => { - txb = new TransactionBuilder(); - }); - - it('accepts a txHash, index [and sequence number]', () => { - const vin = txb.addInput(txHash, 1, 54); - assert.strictEqual(vin, 0); - - const txIn = (txb as any).__TX.ins[0]; - assert.strictEqual(txIn.hash, txHash); - assert.strictEqual(txIn.index, 1); - assert.strictEqual(txIn.sequence, 54); - assert.strictEqual((txb as any).__INPUTS[0].prevOutScript, undefined); - }); - - it('accepts a txHash, index [, sequence number and scriptPubKey]', () => { - const vin = txb.addInput(txHash, 1, 54, scripts[1]); - assert.strictEqual(vin, 0); - - const txIn = (txb as any).__TX.ins[0]; - assert.strictEqual(txIn.hash, txHash); - assert.strictEqual(txIn.index, 1); - assert.strictEqual(txIn.sequence, 54); - assert.strictEqual((txb as any).__INPUTS[0].prevOutScript, scripts[1]); - }); - - it('accepts a prevTx, index [and sequence number]', () => { - const prevTx = new Transaction(); - prevTx.addOutput(scripts[0], 0); - prevTx.addOutput(scripts[1], 1); - - const vin = txb.addInput(prevTx, 1, 54); - assert.strictEqual(vin, 0); - - const txIn = (txb as any).__TX.ins[0]; - assert.deepStrictEqual(txIn.hash, prevTx.getHash()); - assert.strictEqual(txIn.index, 1); - assert.strictEqual(txIn.sequence, 54); - assert.strictEqual((txb as any).__INPUTS[0].prevOutScript, scripts[1]); - }); - - it('returns the input index', () => { - assert.strictEqual(txb.addInput(txHash, 0), 0); - assert.strictEqual(txb.addInput(txHash, 1), 1); - }); - - it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { - txb.addInput(txHash, 0); - txb.addOutput(scripts[0], 1000); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - }); - - assert.throws(() => { - txb.addInput(txHash, 0); - }, /No, this would invalidate signatures/); - }); - }); - - describe('addOutput', () => { - let txb: TransactionBuilder; - beforeEach(() => { - txb = new TransactionBuilder(); - }); - - it('accepts an address string and value', () => { - const { address } = payments.p2pkh({ pubkey: keyPair.publicKey }); - const vout = txb.addOutput(address!, 1000); - assert.strictEqual(vout, 0); - - const txout = (txb as any).__TX.outs[0]; - assert.deepStrictEqual(txout.script, scripts[0]); - assert.strictEqual(txout.value, 1000); - }); - - it('accepts a ScriptPubKey and value', () => { - const vout = txb.addOutput(scripts[0], 1000); - assert.strictEqual(vout, 0); - - const txout = (txb as any).__TX.outs[0]; - assert.deepStrictEqual(txout.script, scripts[0]); - assert.strictEqual(txout.value, 1000); - }); - - it('throws if address is of the wrong network', () => { - assert.throws(() => { - txb.addOutput('2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9', 1000); - }, /2NGHjvjw83pcVFgMcA7QvSMh2c246rxLVz9 has no matching Script/); - }); - - it('add second output after signed first input with SIGHASH_NONE', () => { - txb.addInput(txHash, 0); - txb.addOutput(scripts[0], 2000); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - hashType: Transaction.SIGHASH_NONE, - }); - assert.strictEqual(txb.addOutput(scripts[1], 9000), 1); - }); - - it('add first output after signed first input with SIGHASH_NONE', () => { - txb.addInput(txHash, 0); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - hashType: Transaction.SIGHASH_NONE, - }); - assert.strictEqual(txb.addOutput(scripts[0], 2000), 0); - }); - - it('add second output after signed first input with SIGHASH_SINGLE', () => { - txb.addInput(txHash, 0); - txb.addOutput(scripts[0], 2000); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - hashType: Transaction.SIGHASH_SINGLE, - }); - assert.strictEqual(txb.addOutput(scripts[1], 9000), 1); - }); - - it('add first output after signed first input with SIGHASH_SINGLE', () => { - txb.addInput(txHash, 0); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - hashType: Transaction.SIGHASH_SINGLE, - }); - assert.throws(() => { - txb.addOutput(scripts[0], 2000); - }, /No, this would invalidate signatures/); - }); - - it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', () => { - txb.addInput(txHash, 0); - txb.addOutput(scripts[0], 2000); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - }); - - assert.throws(() => { - txb.addOutput(scripts[1], 9000); - }, /No, this would invalidate signatures/); - }); - }); - - describe('setLockTime', () => { - it('throws if if there exist any scriptSigs', () => { - const txb = new TransactionBuilder(); - txb.addInput(txHash, 0); - txb.addOutput(scripts[0], 100); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - }); - - assert.throws(() => { - txb.setLockTime(65535); - }, /No, this would invalidate signatures/); - }); - }); - - describe('sign', () => { - it('supports the alternative abstract interface { publicKey, sign }', () => { - const innerKeyPair = { - publicKey: ECPair.makeRandom({ - rng: (): Buffer => { - return Buffer.alloc(32, 1); - }, - }).publicKey, - sign: (): Buffer => { - return Buffer.alloc(64, 0x5f); - }, - }; - - const txb = new TransactionBuilder(); - txb.setVersion(1); - txb.addInput( - 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - 1, - ); - txb.addOutput('1111111111111111111114oLvT2', 100000); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair: innerKeyPair, - }); - assert.strictEqual( - txb.build().toHex(), - '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + - 'ffffffff010000006a47304402205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f' + - '5f5f5f5f5f5f5f5f5f5f5f5f5f02205f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f' + - '5f5f5f5f5f5f5f5f5f5f5f5f5f5f0121031b84c5567b126440995d3ed5aaba0565' + - 'd71e1834604819ff9c17f5e9d5dd078fffffffff01a0860100000000001976a914' + - '000000000000000000000000000000000000000088ac00000000', - ); - }); - - it('supports low R signature signing', () => { - let txb = new TransactionBuilder(); - txb.setVersion(1); - txb.addInput( - 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - 1, - ); - txb.addOutput('1111111111111111111114oLvT2', 100000); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - }); - // high R - assert.strictEqual( - txb.build().toHex(), - '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + - 'ffffffff010000006b483045022100b872677f35c9c14ad9c41d83649fb049250f' + - '32574e0b2547d67e209ed14ff05d022059b36ad058be54e887a1a311d5c393cb49' + - '41f6b93a0b090845ec67094de8972b01210279be667ef9dcbbac55a06295ce870b' + - '07029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a9' + - '14000000000000000000000000000000000000000088ac00000000', - ); - - txb = new TransactionBuilder(); - txb.setVersion(1); - txb.addInput( - 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - 1, - ); - txb.addOutput('1111111111111111111114oLvT2', 100000); - txb.setLowR(); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - }); - // low R - assert.strictEqual( - txb.build().toHex(), - '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + - 'ffffffff010000006a473044022012a601efa8756ebe83e9ac7a7db061c3147e3b' + - '49d8be67685799fe51a4c8c62f02204d568d301d5ce14af390d566d4fd50e7b8ee' + - '48e71ec67786c029e721194dae3601210279be667ef9dcbbac55a06295ce870b07' + - '029bfcdb2dce28d959f2815b16f81798ffffffff01a0860100000000001976a914' + - '000000000000000000000000000000000000000088ac00000000', - ); - }); - - it('fails when missing required arguments', () => { - const txb = new TransactionBuilder(); - txb.setVersion(1); - txb.addInput( - 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - 1, - ); - txb.addOutput('1111111111111111111114oLvT2', 100000); - assert.throws(() => { - (txb as any).sign(); - }, /TransactionBuilder sign first arg must be TxbSignArg or number/); - assert.throws(() => { - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 1, - keyPair, - }); - }, /No input at index: 1/); - assert.throws(() => { - (txb as any).sign({ - prevOutScriptType: 'p2pkh', - keyPair, - }); - }, /sign must include vin parameter as Number \(input index\)/); - assert.throws(() => { - (txb as any).sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair: {}, - }); - }, /sign must include keyPair parameter as Signer interface/); - assert.throws(() => { - (txb as any).sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair, - hashType: 'string', - }); - }, /sign hashType parameter must be a number/); - if (useOldSignArgs) { - assert.throws(() => { - txb.sign(0); - }, /sign requires keypair/); - } - }); - - fixtures.invalid.sign.forEach(f => { - it( - 'throws ' + - f.exception + - (f.description ? ' (' + f.description + ')' : ''), - () => { - const txb = construct(f, true); - - let threw = false; - (f.inputs as any).forEach( - (input: any, index: number): void => { - input.signs.forEach((sign: any) => { - const keyPairNetwork = (NETWORKS as any)[ - sign.network || f.network - ]; - const keyPair2 = ECPair.fromWIF(sign.keyPair, keyPairNetwork); - let redeemScript: Buffer | undefined; - let witnessScript: Buffer | undefined; - - if (sign.redeemScript) { - redeemScript = bscript.fromASM(sign.redeemScript); - } - - if (sign.witnessScript) { - witnessScript = bscript.fromASM(sign.witnessScript); - } - - if (sign.throws) { - assert.throws(() => { - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: index, - keyPair: keyPair2, - redeemScript, - hashType: sign.hashType, - witnessValue: sign.value, - witnessScript, - }); - }, new RegExp(f.exception)); - threw = true; - } else { - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: index, - keyPair: keyPair2, - redeemScript, - hashType: sign.hashType, - witnessValue: sign.value, - witnessScript, - }); - } - }); - }, - ); - - assert.strictEqual(threw, true); - }, - ); - }); - }); - - describe('build', () => { - fixtures.valid.build.forEach(f => { - it('builds "' + f.description + '"', () => { - const txb = construct(f, undefined, useOldSignArgs); - const tx = f.incomplete ? txb.buildIncomplete() : txb.build(); - - assert.strictEqual(tx.toHex(), f.txHex); - }); - }); - - // TODO: remove duplicate test code - fixtures.invalid.build.forEach(f => { - describe('for ' + (f.description || f.exception), () => { - it('throws ' + f.exception, () => { - assert.throws(() => { - let txb; - if (f.txHex) { - txb = TransactionBuilder.fromTransaction( - Transaction.fromHex(f.txHex), - ); - } else { - txb = construct(f, undefined, useOldSignArgs); - } - - txb.build(); - }, new RegExp(f.exception)); - }); - - // if throws on incomplete too, enforce that - if (f.incomplete) { - it('throws ' + f.exception, () => { - assert.throws(() => { - let txb; - if (f.txHex) { - txb = TransactionBuilder.fromTransaction( - Transaction.fromHex(f.txHex), - ); - } else { - txb = construct(f, undefined, useOldSignArgs); - } - - txb.buildIncomplete(); - }, new RegExp(f.exception)); - }); - } else { - it('does not throw if buildIncomplete', () => { - let txb; - if (f.txHex) { - txb = TransactionBuilder.fromTransaction( - Transaction.fromHex(f.txHex), - ); - } else { - txb = construct(f, undefined, useOldSignArgs); - } - - txb.buildIncomplete(); - }); - } - }); - }); - - it('for incomplete with 0 signatures', () => { - const randomTxData = - '010000000001010001000000000000000000000000000000000000000000000000' + - '0000000000000000000000ffffffff01e8030000000000001976a9144c9c3dfac4' + - '207d5d8cb89df5722cb3d712385e3f88ac02483045022100aa5d8aa40a90f23ce2' + - 'c3d11bc845ca4a12acd99cbea37de6b9f6d86edebba8cb022022dedc2aa0a255f7' + - '4d04c0b76ece2d7c691f9dd11a64a8ac49f62a99c3a05f9d01232103596d345102' + - '5c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac00000000'; - const randomAddress = '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH'; - - const randomTx = Transaction.fromHex(randomTxData); - const txb = new TransactionBuilder(); - txb.addInput(randomTx, 0); - txb.addOutput(randomAddress, 1000); - const tx = txb.buildIncomplete(); - assert(tx); - }); - - it('for incomplete P2SH with 0 signatures', () => { - const inp = Buffer.from( - '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be9' + - '59391c100000000000ffffffff0100c817a80400000017a91471a8ec07ff69c6c4' + - 'fee489184c462a9b1b9237488700000000', - 'hex', - ); // arbitrary P2SH input - const inpTx = Transaction.fromBuffer(inp); - - const txb = new TransactionBuilder(NETWORKS.testnet); - txb.addInput(inpTx, 0); - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8); // arbitrary output - - txb.buildIncomplete(); - }); - - it('for incomplete P2WPKH with 0 signatures', () => { - const inp = Buffer.from( - '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80be9' + - '59391c100000000000ffffffff0100c817a8040000001600141a15805e1f4040c9' + - 'f68ccc887fca2e63547d794b00000000', - 'hex', - ); - const inpTx = Transaction.fromBuffer(inp); - - const txb = new TransactionBuilder(NETWORKS.testnet); - txb.addInput(inpTx, 0); - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8); // arbitrary output - - txb.buildIncomplete(); - }); - - it('for incomplete P2WSH with 0 signatures', () => { - const inpTx = Transaction.fromBuffer( - Buffer.from( - '010000000173120703f67318aef51f7251272a6816d3f7523bb25e34b136d80b' + - 'e959391c100000000000ffffffff0100c817a80400000022002072df76fcc0b2' + - '31b94bdf7d8c25d7eef4716597818d211e19ade7813bff7a250200000000', - 'hex', - ), - ); - - const txb = new TransactionBuilder(NETWORKS.testnet); - txb.addInput(inpTx, 0); - txb.addOutput('2NAkqp5xffoomp5RLBcakuGpZ12GU4twdz4', 1e8); // arbitrary output - - txb.buildIncomplete(); - }); - }); - - describe('multisig', () => { - fixtures.valid.multisig.forEach(f => { - it(f.description, () => { - const network = (NETWORKS as any)[f.network]; - let txb = construct(f, true); - let tx: Transaction; - - f.inputs.forEach((input, i) => { - const redeemScript = bscript.fromASM(input.redeemScript); - - input.signs.forEach(sign => { - // rebuild the transaction each-time after the first - if (tx) { - // manually override the scriptSig? - if (sign.scriptSigBefore) { - tx.ins[i].script = bscript.fromASM(sign.scriptSigBefore); - } - - // rebuild - txb = TransactionBuilder.fromTransaction(tx, network); - } - - const keyPair2 = ECPair.fromWIF(sign.keyPair, network); - txb.sign({ - prevOutScriptType: sign.prevOutScriptType, - vin: i, - keyPair: keyPair2, - redeemScript, - hashType: (sign as any).hashType, - }); - - // update the tx - tx = txb.buildIncomplete(); - - // now verify the serialized scriptSig is as expected - assert.strictEqual( - bscript.toASM(tx.ins[i].script), - sign.scriptSig, - ); - }); - }); - - tx = txb.build(); - assert.strictEqual(tx.toHex(), f.txHex); - }); - }); - }); - - describe('various edge case', () => { - const network = NETWORKS.testnet; - - it('should warn of high fee for segwit transaction based on VSize, not Size', () => { - const rawtx = - '01000000000104fdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a' + - '1df90000000000fffffffffdaac89627208b4733484ca56bc291f4cf4fa8d7c5f29893c52b46788a0a1df9' + - '0100000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca40000' + - '000000ffffffffa2ef7aaab316a3e5b5b0a78d1d35c774b95a079f9f0c762277a49caf1f26bca401000000' + - '00ffffffff0100040000000000001976a914cf307285359ab7ef6a2daa0522c7908ddf5fe7a988ac024730' + - '440220113324438816338406841775e079b04c50d04f241da652a4035b1017ea1ecf5502205802191eb49c' + - '54bf2a5667aea72e51c3ca92085efc60f12d1ebda3a64aff343201210283409659355b6d1cc3c32decd5d5' + - '61abaac86c37a353b52895a5e6c196d6f44802483045022100dc2892874e6d8708e3f5a058c5c9263cdf03' + - '969492270f89ee4933caf6daf8bb0220391dfe61a002709b63b9d64422d3db09b727839d1287e10a128a5d' + - 'b52a82309301210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f448024830' + - '450221009e3ed3a6ae93a018f443257b43e47b55cf7f7f3547d8807178072234686b22160220576121cfe6' + - '77c7eddf5575ea0a7c926247df6eca723c4f85df306e8bc08ea2df01210283409659355b6d1cc3c32decd5' + - 'd561abaac86c37a353b52895a5e6c196d6f44802473044022007be81ffd4297441ab10e740fc9bab9545a2' + - '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + - '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000'; - const txb = TransactionBuilder.fromTransaction( - Transaction.fromHex(rawtx), - ); - (txb as any).__INPUTS[0].value = 241530; - (txb as any).__INPUTS[1].value = 241530; - (txb as any).__INPUTS[2].value = 248920; - (txb as any).__INPUTS[3].value = 248920; - - assert.throws(() => { - txb.build(); - }, new RegExp('Transaction has absurd fees')); - }); - - it('should classify witness inputs with witness = true during multisigning', () => { - const innerKeyPair = ECPair.fromWIF( - 'cRAwuVuVSBZMPu7hdrYvMCZ8eevzmkExjFbaBLhqnDdrezxN3nTS', - network, - ); - const witnessScript = Buffer.from( - '522102bbbd6eb01efcbe4bd9664b886f26f69de5afcb2e479d72596c8bf21929e3' + - '52e22102d9c3f7180ef13ec5267723c9c2ffab56a4215241f837502ea8977c8532' + - 'b9ea1952ae', - 'hex', - ); - const redeemScript = Buffer.from( - '002024376a0a9abab599d0e028248d48ebe817bc899efcffa1cd2984d67289daf5af', - 'hex', - ); - const scriptPubKey = Buffer.from( - 'a914b64f1a3eacc1c8515592a6f10457e8ff90e4db6a87', - 'hex', - ); - const txb = new TransactionBuilder(network); - txb.setVersion(1); - txb.addInput( - 'a4696c4b0cd27ec2e173ab1fa7d1cc639a98ee237cec95a77ca7ff4145791529', - 1, - 0xffffffff, - scriptPubKey, - ); - txb.addOutput(scriptPubKey, 99000); - txb.sign({ - prevOutScriptType: 'p2sh-p2wsh-p2ms', - vin: 0, - keyPair: innerKeyPair, - redeemScript, - witnessValue: 100000, - witnessScript, - }); - - // 2-of-2 signed only once - const tx = txb.buildIncomplete(); - - // Only input is segwit, so txid should be accurate with the final tx - assert.strictEqual( - tx.getId(), - 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821', - ); - - const txHex = tx.toHex(); - TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)); - }); - - it('should handle badly pre-filled OP_0s', () => { - // OP_0 is used where a signature is missing - const redeemScripSig = bscript.fromASM( - 'OP_0 OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17' + - 'be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621eb' + - 'd9691d6b48c0d4283d7d01 52410479be667ef9dcbbac55a06295ce870b07029bf' + - 'cdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b44' + - '8a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778' + - 'e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f6326' + - '53266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c' + - '845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99' + - '934c2231b6cb9fd7584b8e67253ae', - ); - const redeemScript = bscript.fromASM( - 'OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f' + - '81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d' + - '4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c70' + - '9ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe5' + - '2a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce03' + - '6f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67' + - '2 OP_3 OP_CHECKMULTISIG', - ); - - const tx = new Transaction(); - tx.addInput( - Buffer.from( - 'cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f07149', - 'hex', - ), - 0, - undefined, - redeemScripSig, - ); - tx.addOutput( - Buffer.from( - '76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac', - 'hex', - ), - 1000, - ); - - // now import the Transaction - const txb = TransactionBuilder.fromTransaction(tx, NETWORKS.testnet); - - const keyPair2 = ECPair.fromWIF( - '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', - network, - ); - txb.sign({ - prevOutScriptType: 'p2sh-p2ms', - vin: 0, - keyPair: keyPair2, - redeemScript, - }); - - const tx2 = txb.build(); - assert.strictEqual( - tx2.getId(), - 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9', - ); - assert.strictEqual( - bscript.toASM(tx2.ins[0].script), - 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b' + - '63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691' + - 'd6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4' + - '466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881' + - 'd7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870' + - 'b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a' + - '8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07' + - 'cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceae' + - 'ef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5' + - '229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f35' + - '66500a99934c2231b6cb9fd7584b8e67253ae', - ); - }); - - it('should not classify blank scripts as nonstandard', () => { - let txb = new TransactionBuilder(); - txb.setVersion(1); - txb.addInput( - 'aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', - 0, - ); - - const incomplete = txb.buildIncomplete().toHex(); - const innerKeyPair = ECPair.fromWIF( - 'L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy', - ); - - // sign, as expected - txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair: innerKeyPair, - }); - const txId = txb.build().getId(); - assert.strictEqual( - txId, - '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3', - ); - - // and, repeat - txb = TransactionBuilder.fromTransaction( - Transaction.fromHex(incomplete), - ); - txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000); - txb.sign({ - prevOutScriptType: 'p2pkh', - vin: 0, - keyPair: innerKeyPair, - }); - const txId2 = txb.build().getId(); - assert.strictEqual(txId, txId2); - // TODO: Remove me in v6 - if (useOldSignArgs) { - console.warn = consoleWarn; - } - }); - }); - }); -} diff --git a/ts_src/index.ts b/ts_src/index.ts index b9aa49c..c425d96 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -12,7 +12,6 @@ export { Block } from './block'; export { Psbt, PsbtTxInput, PsbtTxOutput } from './psbt'; export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; -export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; export { ECPairInterface, Signer, SignerAsync } from './ecpair'; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index c55e6bc..d5eb9f9 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -117,8 +117,7 @@ export class Psbt { __NON_WITNESS_UTXO_BUF_CACHE: [], __TX_IN_CACHE: {}, __TX: (this.data.globalMap.unsignedTx as PsbtTransaction).tx, - // Old TransactionBuilder behavior was to not confirm input values - // before signing. Even though we highly encourage people to get + // Even though we highly encourage people to get // the full parent transaction to verify values, the ability to // sign non-segwit inputs without the full transaction was often // requested. So the only way to activate is to use @ts-ignore. @@ -1295,8 +1294,7 @@ function getHashForSig( console.warn( 'Warning: Signing non-segwit inputs without the full parent transaction ' + 'means there is a chance that a miner could feed you incorrect information ' + - 'to trick you into paying large fees. This behavior is the same as the old ' + - 'TransactionBuilder class when signing non-segwit scripts. You are not ' + + 'to trick you into paying large fees. You are not ' + 'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' + 'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' + '*********************', diff --git a/ts_src/transaction_builder.ts b/ts_src/transaction_builder.ts deleted file mode 100644 index 1edf0f2..0000000 --- a/ts_src/transaction_builder.ts +++ /dev/null @@ -1,1318 +0,0 @@ -import * as baddress from './address'; -import { reverseBuffer } from './bufferutils'; -import * as classify from './classify'; -import * as bcrypto from './crypto'; -import { Signer } from './ecpair'; -import * as ECPair from './ecpair'; -import { Network } from './networks'; -import * as networks from './networks'; -import { Payment } from './payments'; -import * as payments from './payments'; -import * as bscript from './script'; -import { OPS as ops } from './script'; -import { Output, Transaction } from './transaction'; -import * as types from './types'; -const typeforce = require('typeforce'); - -const SCRIPT_TYPES = classify.types; - -const PREVOUT_TYPES: Set<string> = new Set([ - // Raw - 'p2pkh', - 'p2pk', - 'p2wpkh', - 'p2ms', - // P2SH wrapped - 'p2sh-p2pkh', - 'p2sh-p2pk', - 'p2sh-p2wpkh', - 'p2sh-p2ms', - // P2WSH wrapped - 'p2wsh-p2pkh', - 'p2wsh-p2pk', - 'p2wsh-p2ms', - // P2SH-P2WSH wrapper - 'p2sh-p2wsh-p2pkh', - 'p2sh-p2wsh-p2pk', - 'p2sh-p2wsh-p2ms', -]); - -type MaybeBuffer = Buffer | undefined; -type TxbSignatures = Buffer[] | MaybeBuffer[]; -type TxbPubkeys = MaybeBuffer[]; -type TxbWitness = Buffer[]; -type TxbScriptType = string; -type TxbScript = Buffer; - -interface TxbInput { - value?: number; - hasWitness?: boolean; - signScript?: TxbScript; - signType?: TxbScriptType; - prevOutScript?: TxbScript; - redeemScript?: TxbScript; - redeemScriptType?: TxbScriptType; - prevOutType?: TxbScriptType; - pubkeys?: TxbPubkeys; - signatures?: TxbSignatures; - witness?: TxbWitness; - witnessScript?: TxbScript; - witnessScriptType?: TxbScriptType; - script?: TxbScript; - sequence?: number; - scriptSig?: TxbScript; - maxSignatures?: number; -} - -interface TxbOutput { - type: string; - pubkeys?: TxbPubkeys; - signatures?: TxbSignatures; - maxSignatures?: number; -} - -interface TxbSignArg { - prevOutScriptType: string; - vin: number; - keyPair: Signer; - redeemScript?: Buffer; - hashType?: number; - witnessValue?: number; - witnessScript?: Buffer; -} - -function tfMessage(type: any, value: any, message: string): void { - try { - typeforce(type, value); - } catch (err) { - throw new Error(message); - } -} - -function txIsString(tx: Buffer | string | Transaction): tx is string { - return typeof tx === 'string' || tx instanceof String; -} - -function txIsTransaction(tx: Buffer | string | Transaction): tx is Transaction { - return tx instanceof Transaction; -} - -export class TransactionBuilder { - static fromTransaction( - transaction: Transaction, - network?: Network, - ): TransactionBuilder { - const txb = new TransactionBuilder(network); - - // Copy transaction fields - txb.setVersion(transaction.version); - txb.setLockTime(transaction.locktime); - - // Copy outputs (done first to avoid signature invalidation) - transaction.outs.forEach(txOut => { - txb.addOutput(txOut.script, (txOut as Output).value); - }); - - // Copy inputs - transaction.ins.forEach(txIn => { - txb.__addInputUnsafe(txIn.hash, txIn.index, { - sequence: txIn.sequence, - script: txIn.script, - witness: txIn.witness, - }); - }); - - // fix some things not possible through the public API - txb.__INPUTS.forEach((input, i) => { - fixMultisigOrder(input, transaction, i); - }); - - return txb; - } - - private __PREV_TX_SET: { [index: string]: boolean }; - private __INPUTS: TxbInput[]; - private __TX: Transaction; - private __USE_LOW_R: boolean; - - // WARNING: maximumFeeRate is __NOT__ to be relied on, - // it's just another potential safety mechanism (safety in-depth) - constructor( - public network: Network = networks.bitcoin, - public maximumFeeRate: number = 2500, - ) { - this.__PREV_TX_SET = {}; - this.__INPUTS = []; - this.__TX = new Transaction(); - this.__TX.version = 2; - this.__USE_LOW_R = false; - console.warn( - 'Deprecation Warning: TransactionBuilder will be removed in the future. ' + - '(v6.x.x or later) Please use the Psbt class instead. Examples of usage ' + - 'are available in the transactions-psbt.js integration test file on our ' + - 'Github. A high level explanation is available in the psbt.ts and psbt.js ' + - 'files as well.', - ); - } - - setLowR(setting?: boolean): boolean { - typeforce(typeforce.maybe(typeforce.Boolean), setting); - if (setting === undefined) { - setting = true; - } - this.__USE_LOW_R = setting; - return setting; - } - - setLockTime(locktime: number): void { - typeforce(types.UInt32, locktime); - - // if any signatures exist, throw - if ( - this.__INPUTS.some(input => { - if (!input.signatures) return false; - - return input.signatures.some(s => s !== undefined); - }) - ) { - throw new Error('No, this would invalidate signatures'); - } - - this.__TX.locktime = locktime; - } - - setVersion(version: number): void { - typeforce(types.UInt32, version); - - // XXX: this might eventually become more complex depending on what the versions represent - this.__TX.version = version; - } - - addInput( - txHash: Buffer | string | Transaction, - vout: number, - sequence?: number, - prevOutScript?: Buffer, - ): number { - if (!this.__canModifyInputs()) { - throw new Error('No, this would invalidate signatures'); - } - - let value: number | undefined; - - // is it a hex string? - if (txIsString(txHash)) { - // transaction hashs's are displayed in reverse order, un-reverse it - txHash = reverseBuffer(Buffer.from(txHash, 'hex')); - - // is it a Transaction object? - } else if (txIsTransaction(txHash)) { - const txOut = txHash.outs[vout]; - prevOutScript = txOut.script; - value = (txOut as Output).value; - - txHash = txHash.getHash(false) as Buffer; - } - - return this.__addInputUnsafe(txHash, vout, { - sequence, - prevOutScript, - value, - }); - } - - addOutput(scriptPubKey: string | Buffer, value: number): number { - if (!this.__canModifyOutputs()) { - throw new Error('No, this would invalidate signatures'); - } - - // Attempt to get a script if it's a base58 or bech32 address string - if (typeof scriptPubKey === 'string') { - scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network); - } - - return this.__TX.addOutput(scriptPubKey, value); - } - - build(): Transaction { - return this.__build(false); - } - - buildIncomplete(): Transaction { - return this.__build(true); - } - - sign( - signParams: number | TxbSignArg, - keyPair?: Signer, - redeemScript?: Buffer, - hashType?: number, - witnessValue?: number, - witnessScript?: Buffer, - ): void { - trySign( - getSigningData( - this.network, - this.__INPUTS, - this.__needsOutputs.bind(this), - this.__TX, - signParams, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - this.__USE_LOW_R, - ), - ); - } - - private __addInputUnsafe( - txHash: Buffer, - vout: number, - options: TxbInput, - ): number { - if (Transaction.isCoinbaseHash(txHash)) { - throw new Error('coinbase inputs not supported'); - } - - const prevTxOut = txHash.toString('hex') + ':' + vout; - if (this.__PREV_TX_SET[prevTxOut] !== undefined) - throw new Error('Duplicate TxOut: ' + prevTxOut); - - let input: TxbInput = {}; - - // derive what we can from the scriptSig - if (options.script !== undefined) { - input = expandInput(options.script, options.witness || []); - } - - // if an input value was given, retain it - if (options.value !== undefined) { - input.value = options.value; - } - - // derive what we can from the previous transactions output script - if (!input.prevOutScript && options.prevOutScript) { - let prevOutType; - - if (!input.pubkeys && !input.signatures) { - const expanded = expandOutput(options.prevOutScript); - if (expanded.pubkeys) { - input.pubkeys = expanded.pubkeys; - input.signatures = expanded.signatures; - } - - prevOutType = expanded.type; - } - - input.prevOutScript = options.prevOutScript; - input.prevOutType = prevOutType || classify.output(options.prevOutScript); - } - - const vin = this.__TX.addInput( - txHash, - vout, - options.sequence, - options.scriptSig, - ); - this.__INPUTS[vin] = input; - this.__PREV_TX_SET[prevTxOut] = true; - return vin; - } - - private __build(allowIncomplete?: boolean): Transaction { - if (!allowIncomplete) { - if (!this.__TX.ins.length) throw new Error('Transaction has no inputs'); - if (!this.__TX.outs.length) throw new Error('Transaction has no outputs'); - } - - const tx = this.__TX.clone(); - - // create script signatures from inputs - this.__INPUTS.forEach((input, i) => { - if (!input.prevOutType && !allowIncomplete) - throw new Error('Transaction is not complete'); - - const result = build(input.prevOutType!, input, allowIncomplete); - if (!result) { - if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD) - throw new Error('Unknown input type'); - if (!allowIncomplete) throw new Error('Not enough information'); - return; - } - - tx.setInputScript(i, result.input!); - tx.setWitness(i, result.witness!); - }); - - if (!allowIncomplete) { - // do not rely on this, its merely a last resort - if (this.__overMaximumFees(tx.virtualSize())) { - throw new Error('Transaction has absurd fees'); - } - } - - return tx; - } - - private __canModifyInputs(): boolean { - return this.__INPUTS.every(input => { - if (!input.signatures) return true; - - return input.signatures.every(signature => { - if (!signature) return true; - const hashType = signatureHashType(signature); - - // if SIGHASH_ANYONECANPAY is set, signatures would not - // be invalidated by more inputs - return (hashType & Transaction.SIGHASH_ANYONECANPAY) !== 0; - }); - }); - } - - private __needsOutputs(signingHashType: number): boolean { - if (signingHashType === Transaction.SIGHASH_ALL) { - return this.__TX.outs.length === 0; - } - - // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs - // .build() will fail, but .buildIncomplete() is OK - return ( - this.__TX.outs.length === 0 && - this.__INPUTS.some(input => { - if (!input.signatures) return false; - - return input.signatures.some(signature => { - if (!signature) return false; // no signature, no issue - const hashType = signatureHashType(signature); - if (hashType & Transaction.SIGHASH_NONE) return false; // SIGHASH_NONE doesn't care about outputs - return true; // SIGHASH_* does care - }); - }) - ); - } - - private __canModifyOutputs(): boolean { - const nInputs = this.__TX.ins.length; - const nOutputs = this.__TX.outs.length; - - return this.__INPUTS.every(input => { - if (input.signatures === undefined) return true; - - return input.signatures.every(signature => { - if (!signature) return true; - const hashType = signatureHashType(signature); - - const hashTypeMod = hashType & 0x1f; - if (hashTypeMod === Transaction.SIGHASH_NONE) return true; - if (hashTypeMod === Transaction.SIGHASH_SINGLE) { - // if SIGHASH_SINGLE is set, and nInputs > nOutputs - // some signatures would be invalidated by the addition - // of more outputs - return nInputs <= nOutputs; - } - return false; - }); - }); - } - - private __overMaximumFees(bytes: number): boolean { - // not all inputs will have .value defined - const incoming = this.__INPUTS.reduce((a, x) => a + (x.value! >>> 0), 0); - - // but all outputs do, and if we have any input value - // we can immediately determine if the outputs are too small - const outgoing = this.__TX.outs.reduce( - (a, x) => a + (x as Output).value, - 0, - ); - const fee = incoming - outgoing; - const feeRate = fee / bytes; - - return feeRate > this.maximumFeeRate; - } -} - -function expandInput( - scriptSig: Buffer, - witnessStack: Buffer[], - type?: string, - scriptPubKey?: Buffer, -): TxbInput { - if (scriptSig.length === 0 && witnessStack.length === 0) return {}; - if (!type) { - let ssType: string | undefined = classify.input(scriptSig, true); - let wsType: string | undefined = classify.witness(witnessStack, true); - if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined; - if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined; - type = ssType || wsType; - } - - switch (type) { - case SCRIPT_TYPES.P2WPKH: { - const { output, pubkey, signature } = payments.p2wpkh({ - witness: witnessStack, - }); - - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2WPKH, - pubkeys: [pubkey], - signatures: [signature], - }; - } - - case SCRIPT_TYPES.P2PKH: { - const { output, pubkey, signature } = payments.p2pkh({ - input: scriptSig, - }); - - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2PKH, - pubkeys: [pubkey], - signatures: [signature], - }; - } - - case SCRIPT_TYPES.P2PK: { - const { signature } = payments.p2pk({ input: scriptSig }); - - return { - prevOutType: SCRIPT_TYPES.P2PK, - pubkeys: [undefined], - signatures: [signature], - }; - } - - case SCRIPT_TYPES.P2MS: { - const { m, pubkeys, signatures } = payments.p2ms( - { - input: scriptSig, - output: scriptPubKey, - }, - { allowIncomplete: true }, - ); - - return { - prevOutType: SCRIPT_TYPES.P2MS, - pubkeys, - signatures, - maxSignatures: m, - }; - } - } - - if (type === SCRIPT_TYPES.P2SH) { - const { output, redeem } = payments.p2sh({ - input: scriptSig, - witness: witnessStack, - }); - - const outputType = classify.output(redeem!.output!); - const expanded = expandInput( - redeem!.input!, - redeem!.witness!, - outputType, - redeem!.output, - ); - if (!expanded.prevOutType) return {}; - - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2SH, - redeemScript: redeem!.output, - redeemScriptType: expanded.prevOutType, - witnessScript: expanded.witnessScript, - witnessScriptType: expanded.witnessScriptType, - - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - }; - } - - if (type === SCRIPT_TYPES.P2WSH) { - const { output, redeem } = payments.p2wsh({ - input: scriptSig, - witness: witnessStack, - }); - const outputType = classify.output(redeem!.output!); - let expanded; - if (outputType === SCRIPT_TYPES.P2WPKH) { - expanded = expandInput(redeem!.input!, redeem!.witness!, outputType); - } else { - expanded = expandInput( - bscript.compile(redeem!.witness!), - [], - outputType, - redeem!.output, - ); - } - if (!expanded.prevOutType) return {}; - - return { - prevOutScript: output, - prevOutType: SCRIPT_TYPES.P2WSH, - witnessScript: redeem!.output, - witnessScriptType: expanded.prevOutType, - - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - }; - } - - return { - prevOutType: SCRIPT_TYPES.NONSTANDARD, - prevOutScript: scriptSig, - }; -} - -// could be done in expandInput, but requires the original Transaction for hashForSignature -function fixMultisigOrder( - input: TxbInput, - transaction: Transaction, - vin: number, -): void { - if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) - return; - if (input.pubkeys!.length === input.signatures!.length) return; - - const unmatched = input.signatures!.concat(); - - input.signatures = input.pubkeys!.map(pubKey => { - const keyPair = ECPair.fromPublicKey(pubKey!); - let match: Buffer | undefined; - - // check for a signature - unmatched.some((signature, i) => { - // skip if undefined || OP_0 - if (!signature) return false; - - // TODO: avoid O(n) hashForSignature - const parsed = bscript.signature.decode(signature); - const hash = transaction.hashForSignature( - vin, - input.redeemScript!, - parsed.hashType, - ); - - // skip if signature does not match pubKey - if (!keyPair.verify(hash, parsed.signature)) return false; - - // remove matched signature from unmatched - unmatched[i] = undefined; - match = signature; - - return true; - }); - - return match; - }); -} - -function expandOutput(script: Buffer, ourPubKey?: Buffer): TxbOutput { - typeforce(types.Buffer, script); - const type = classify.output(script); - - switch (type) { - case SCRIPT_TYPES.P2PKH: { - if (!ourPubKey) return { type }; - - // does our hash160(pubKey) match the output scripts? - const pkh1 = payments.p2pkh({ output: script }).hash; - const pkh2 = bcrypto.hash160(ourPubKey); - if (!pkh1!.equals(pkh2)) return { type }; - - return { - type, - pubkeys: [ourPubKey], - signatures: [undefined], - }; - } - - case SCRIPT_TYPES.P2WPKH: { - if (!ourPubKey) return { type }; - - // does our hash160(pubKey) match the output scripts? - const wpkh1 = payments.p2wpkh({ output: script }).hash; - const wpkh2 = bcrypto.hash160(ourPubKey); - if (!wpkh1!.equals(wpkh2)) return { type }; - - return { - type, - pubkeys: [ourPubKey], - signatures: [undefined], - }; - } - - case SCRIPT_TYPES.P2PK: { - const p2pk = payments.p2pk({ output: script }); - return { - type, - pubkeys: [p2pk.pubkey], - signatures: [undefined], - }; - } - - case SCRIPT_TYPES.P2MS: { - const p2ms = payments.p2ms({ output: script }); - return { - type, - pubkeys: p2ms.pubkeys, - signatures: p2ms.pubkeys!.map((): undefined => undefined), - maxSignatures: p2ms.m, - }; - } - } - - return { type }; -} - -function prepareInput( - input: TxbInput, - ourPubKey: Buffer, - redeemScript?: Buffer, - witnessScript?: Buffer, -): TxbInput { - if (redeemScript && witnessScript) { - const p2wsh = payments.p2wsh({ - redeem: { output: witnessScript }, - }) as Payment; - const p2wshAlt = payments.p2wsh({ output: redeemScript }) as Payment; - const p2sh = payments.p2sh({ redeem: { output: redeemScript } }) as Payment; - const p2shAlt = payments.p2sh({ redeem: p2wsh }) as Payment; - - // enforces P2SH(P2WSH(...)) - if (!p2wsh.hash!.equals(p2wshAlt.hash!)) - throw new Error('Witness script inconsistent with prevOutScript'); - if (!p2sh.hash!.equals(p2shAlt.hash!)) - throw new Error('Redeem script inconsistent with prevOutScript'); - - const expanded = expandOutput(p2wsh.redeem!.output!, ourPubKey); - if (!expanded.pubkeys) - throw new Error( - expanded.type + - ' not supported as witnessScript (' + - bscript.toASM(witnessScript) + - ')', - ); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - - const signScript = witnessScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) - throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure'); - - return { - redeemScript, - redeemScriptType: SCRIPT_TYPES.P2WSH, - - witnessScript, - witnessScriptType: expanded.type, - - prevOutType: SCRIPT_TYPES.P2SH, - prevOutScript: p2sh.output, - - hasWitness: true, - signScript, - signType: expanded.type, - - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - - if (redeemScript) { - const p2sh = payments.p2sh({ redeem: { output: redeemScript } }) as Payment; - - if (input.prevOutScript) { - let p2shAlt; - try { - p2shAlt = payments.p2sh({ output: input.prevOutScript }) as Payment; - } catch (e) { - throw new Error('PrevOutScript must be P2SH'); - } - if (!p2sh.hash!.equals(p2shAlt.hash!)) - throw new Error('Redeem script inconsistent with prevOutScript'); - } - - const expanded = expandOutput(p2sh.redeem!.output!, ourPubKey); - if (!expanded.pubkeys) - throw new Error( - expanded.type + - ' not supported as redeemScript (' + - bscript.toASM(redeemScript) + - ')', - ); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - - let signScript = redeemScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output!; - } - - return { - redeemScript, - redeemScriptType: expanded.type, - - prevOutType: SCRIPT_TYPES.P2SH, - prevOutScript: p2sh.output, - - hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, - signScript, - signType: expanded.type, - - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - - if (witnessScript) { - const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } }); - - if (input.prevOutScript) { - const p2wshAlt = payments.p2wsh({ output: input.prevOutScript }); - if (!p2wsh.hash!.equals(p2wshAlt.hash!)) - throw new Error('Witness script inconsistent with prevOutScript'); - } - - const expanded = expandOutput(p2wsh.redeem!.output!, ourPubKey); - if (!expanded.pubkeys) - throw new Error( - expanded.type + - ' not supported as witnessScript (' + - bscript.toASM(witnessScript) + - ')', - ); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - - const signScript = witnessScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) - throw new Error('P2WSH(P2WPKH) is a consensus failure'); - - return { - witnessScript, - witnessScriptType: expanded.type, - - prevOutType: SCRIPT_TYPES.P2WSH, - prevOutScript: p2wsh.output, - - hasWitness: true, - signScript, - signType: expanded.type, - - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - - if (input.prevOutType && input.prevOutScript) { - // embedded scripts are not possible without extra information - if (input.prevOutType === SCRIPT_TYPES.P2SH) - throw new Error( - 'PrevOutScript is ' + input.prevOutType + ', requires redeemScript', - ); - if (input.prevOutType === SCRIPT_TYPES.P2WSH) - throw new Error( - 'PrevOutScript is ' + input.prevOutType + ', requires witnessScript', - ); - if (!input.prevOutScript) throw new Error('PrevOutScript is missing'); - - const expanded = expandOutput(input.prevOutScript, ourPubKey); - if (!expanded.pubkeys) - throw new Error( - expanded.type + - ' not supported (' + - bscript.toASM(input.prevOutScript) + - ')', - ); - if (input.signatures && input.signatures.some(x => x !== undefined)) { - expanded.signatures = input.signatures; - } - - let signScript = input.prevOutScript; - if (expanded.type === SCRIPT_TYPES.P2WPKH) { - signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }) - .output as Buffer; - } - - return { - prevOutType: expanded.type, - prevOutScript: input.prevOutScript, - - hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH, - signScript, - signType: expanded.type, - - pubkeys: expanded.pubkeys, - signatures: expanded.signatures, - maxSignatures: expanded.maxSignatures, - }; - } - - const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output; - return { - prevOutType: SCRIPT_TYPES.P2PKH, - prevOutScript, - - hasWitness: false, - signScript: prevOutScript, - signType: SCRIPT_TYPES.P2PKH, - - pubkeys: [ourPubKey], - signatures: [undefined], - }; -} - -function build( - type: string, - input: TxbInput, - allowIncomplete?: boolean, -): Payment | undefined { - const pubkeys = (input.pubkeys || []) as Buffer[]; - let signatures = (input.signatures || []) as Buffer[]; - - switch (type) { - case SCRIPT_TYPES.P2PKH: { - if (pubkeys.length === 0) break; - if (signatures.length === 0) break; - - return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] }); - } - case SCRIPT_TYPES.P2WPKH: { - if (pubkeys.length === 0) break; - if (signatures.length === 0) break; - - return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] }); - } - case SCRIPT_TYPES.P2PK: { - if (pubkeys.length === 0) break; - if (signatures.length === 0) break; - - return payments.p2pk({ signature: signatures[0] }); - } - case SCRIPT_TYPES.P2MS: { - const m = input.maxSignatures; - if (allowIncomplete) { - signatures = signatures.map(x => x || ops.OP_0); - } else { - signatures = signatures.filter(x => x); - } - - // if the transaction is not not complete (complete), or if signatures.length === m, validate - // otherwise, the number of OP_0's may be >= m, so don't validate (boo) - const validate = !allowIncomplete || m === signatures.length; - return payments.p2ms( - { m, pubkeys, signatures }, - { allowIncomplete, validate }, - ); - } - case SCRIPT_TYPES.P2SH: { - const redeem = build(input.redeemScriptType!, input, allowIncomplete); - if (!redeem) return; - - return payments.p2sh({ - redeem: { - output: redeem.output || input.redeemScript, - input: redeem.input, - witness: redeem.witness, - }, - }); - } - case SCRIPT_TYPES.P2WSH: { - const redeem = build(input.witnessScriptType!, input, allowIncomplete); - if (!redeem) return; - - return payments.p2wsh({ - redeem: { - output: input.witnessScript, - input: redeem.input, - witness: redeem.witness, - }, - }); - } - } -} - -function canSign(input: TxbInput): boolean { - return ( - input.signScript !== undefined && - input.signType !== undefined && - input.pubkeys !== undefined && - input.signatures !== undefined && - input.signatures.length === input.pubkeys.length && - input.pubkeys.length > 0 && - (input.hasWitness === false || input.value !== undefined) - ); -} - -function signatureHashType(buffer: Buffer): number { - return buffer.readUInt8(buffer.length - 1); -} - -function checkSignArgs(inputs: TxbInput[], signParams: TxbSignArg): void { - if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) { - throw new TypeError( - `Unknown prevOutScriptType "${signParams.prevOutScriptType}"`, - ); - } - tfMessage( - typeforce.Number, - signParams.vin, - `sign must include vin parameter as Number (input index)`, - ); - tfMessage( - types.Signer, - signParams.keyPair, - `sign must include keyPair parameter as Signer interface`, - ); - tfMessage( - typeforce.maybe(typeforce.Number), - signParams.hashType, - `sign hashType parameter must be a number`, - ); - const prevOutType = (inputs[signParams.vin] || []).prevOutType; - const posType = signParams.prevOutScriptType; - switch (posType) { - case 'p2pkh': - if (prevOutType && prevOutType !== 'pubkeyhash') { - throw new TypeError( - `input #${signParams.vin} is not of type p2pkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2pk': - if (prevOutType && prevOutType !== 'pubkey') { - throw new TypeError( - `input #${signParams.vin} is not of type p2pk: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2wpkh': - if (prevOutType && prevOutType !== 'witnesspubkeyhash') { - throw new TypeError( - `input #${signParams.vin} is not of type p2wpkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - break; - case 'p2ms': - if (prevOutType && prevOutType !== 'multisig') { - throw new TypeError( - `input #${signParams.vin} is not of type p2ms: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2sh-p2wpkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${signParams.vin} is not of type p2sh-p2wpkh: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - break; - case 'p2sh-p2ms': - case 'p2sh-p2pk': - case 'p2sh-p2pkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.value(undefined), - signParams.witnessScript, - `${posType} requires NO witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires redeemScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.witnessValue, - `${posType} requires NO witnessValue`, - ); - break; - case 'p2wsh-p2ms': - case 'p2wsh-p2pk': - case 'p2wsh-p2pkh': - if (prevOutType && prevOutType !== 'witnessscripthash') { - throw new TypeError( - `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.Buffer, - signParams.witnessScript, - `${posType} requires witnessScript`, - ); - tfMessage( - typeforce.value(undefined), - signParams.redeemScript, - `${posType} requires NO redeemScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessValue`, - ); - break; - case 'p2sh-p2wsh-p2ms': - case 'p2sh-p2wsh-p2pk': - case 'p2sh-p2wsh-p2pkh': - if (prevOutType && prevOutType !== 'scripthash') { - throw new TypeError( - `input #${signParams.vin} is not of type ${posType}: ${prevOutType}`, - ); - } - tfMessage( - typeforce.Buffer, - signParams.witnessScript, - `${posType} requires witnessScript`, - ); - tfMessage( - typeforce.Buffer, - signParams.redeemScript, - `${posType} requires witnessScript`, - ); - tfMessage( - types.Satoshi, - signParams.witnessValue, - `${posType} requires witnessScript`, - ); - break; - } -} - -function trySign({ - input, - ourPubKey, - keyPair, - signatureHash, - hashType, - useLowR, -}: SigningData): void { - // enforce in order signing of public keys - let signed = false; - for (const [i, pubKey] of input.pubkeys!.entries()) { - if (!ourPubKey.equals(pubKey!)) continue; - if (input.signatures![i]) throw new Error('Signature already exists'); - - // TODO: add tests - if (ourPubKey.length !== 33 && input.hasWitness) { - throw new Error( - 'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH', - ); - } - - const signature = keyPair.sign(signatureHash, useLowR); - input.signatures![i] = bscript.signature.encode(signature, hashType); - signed = true; - } - - if (!signed) throw new Error('Key pair cannot sign for this input'); -} - -interface SigningData { - input: TxbInput; - ourPubKey: Buffer; - keyPair: Signer; - signatureHash: Buffer; - hashType: number; - useLowR: boolean; -} - -type HashTypeCheck = (hashType: number) => boolean; - -function getSigningData( - network: Network, - inputs: TxbInput[], - needsOutputs: HashTypeCheck, - tx: Transaction, - signParams: number | TxbSignArg, - keyPair?: Signer, - redeemScript?: Buffer, - hashType?: number, - witnessValue?: number, - witnessScript?: Buffer, - useLowR?: boolean, -): SigningData { - let vin: number; - if (typeof signParams === 'number') { - console.warn( - 'DEPRECATED: TransactionBuilder sign method arguments ' + - 'will change in v6, please use the TxbSignArg interface', - ); - vin = signParams; - } else if (typeof signParams === 'object') { - checkSignArgs(inputs, signParams); - ({ - vin, - keyPair, - redeemScript, - hashType, - witnessValue, - witnessScript, - } = signParams); - } else { - throw new TypeError( - 'TransactionBuilder sign first arg must be TxbSignArg or number', - ); - } - if (keyPair === undefined) { - throw new Error('sign requires keypair'); - } - // TODO: remove keyPair.network matching in 4.0.0 - if (keyPair.network && keyPair.network !== network) - throw new TypeError('Inconsistent network'); - if (!inputs[vin]) throw new Error('No input at index: ' + vin); - - hashType = hashType || Transaction.SIGHASH_ALL; - if (needsOutputs(hashType)) throw new Error('Transaction needs outputs'); - - const input = inputs[vin]; - - // if redeemScript was previously provided, enforce consistency - if ( - input.redeemScript !== undefined && - redeemScript && - !input.redeemScript.equals(redeemScript) - ) { - throw new Error('Inconsistent redeemScript'); - } - - const ourPubKey = - keyPair.publicKey || (keyPair.getPublicKey && keyPair.getPublicKey()); - if (!canSign(input)) { - if (witnessValue !== undefined) { - if (input.value !== undefined && input.value !== witnessValue) - throw new Error('Input did not match witnessValue'); - typeforce(types.Satoshi, witnessValue); - input.value = witnessValue; - } - - if (!canSign(input)) { - const prepared = prepareInput( - input, - ourPubKey, - redeemScript, - witnessScript, - ); - - // updates inline - Object.assign(input, prepared); - } - - if (!canSign(input)) throw Error(input.prevOutType + ' not supported'); - } - - // ready to sign - let signatureHash: Buffer; - if (input.hasWitness) { - signatureHash = tx.hashForWitnessV0( - vin, - input.signScript as Buffer, - input.value as number, - hashType, - ); - } else { - signatureHash = tx.hashForSignature( - vin, - input.signScript as Buffer, - hashType, - ); - } - - return { - input, - ourPubKey, - keyPair, - signatureHash, - hashType, - useLowR: !!useLowR, - }; -} diff --git a/types/index.d.ts b/types/index.d.ts index c8f2a00..f63a986 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -10,7 +10,6 @@ export { Block } from './block'; export { Psbt, PsbtTxInput, PsbtTxOutput } from './psbt'; export { OPS as opcodes } from './script'; export { Transaction } from './transaction'; -export { TransactionBuilder } from './transaction_builder'; export { BIP32Interface } from 'bip32'; export { ECPairInterface, Signer, SignerAsync } from './ecpair'; export { Network } from './networks'; diff --git a/types/transaction_builder.d.ts b/types/transaction_builder.d.ts deleted file mode 100644 index a80fc0f..0000000 --- a/types/transaction_builder.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Signer } from './ecpair'; -import { Network } from './networks'; -import { Transaction } from './transaction'; -interface TxbSignArg { - prevOutScriptType: string; - vin: number; - keyPair: Signer; - redeemScript?: Buffer; - hashType?: number; - witnessValue?: number; - witnessScript?: Buffer; -} -export declare class TransactionBuilder { - network: Network; - maximumFeeRate: number; - static fromTransaction(transaction: Transaction, network?: Network): TransactionBuilder; - private __PREV_TX_SET; - private __INPUTS; - private __TX; - private __USE_LOW_R; - constructor(network?: Network, maximumFeeRate?: number); - setLowR(setting?: boolean): boolean; - setLockTime(locktime: number): void; - setVersion(version: number): void; - addInput(txHash: Buffer | string | Transaction, vout: number, sequence?: number, prevOutScript?: Buffer): number; - addOutput(scriptPubKey: string | Buffer, value: number): number; - build(): Transaction; - buildIncomplete(): Transaction; - sign(signParams: number | TxbSignArg, keyPair?: Signer, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void; - private __addInputUnsafe; - private __build; - private __canModifyInputs; - private __needsOutputs; - private __canModifyOutputs; - private __overMaximumFees; -} -export {}; From c2175518842e580da60994858db02c98528679ef Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Fri, 27 Nov 2020 12:02:07 +0200 Subject: [PATCH 537/568] #1477 remove non-public dependecies of TransactionBuilder (classify & templates)) --- src/classify.js | 59 ---- src/templates/multisig/index.js | 6 - src/templates/multisig/input.js | 23 -- src/templates/multisig/output.js | 27 -- src/templates/nulldata.js | 15 - src/templates/pubkey/index.js | 6 - src/templates/pubkey/input.js | 12 - src/templates/pubkey/output.js | 17 -- src/templates/pubkeyhash/index.js | 6 - src/templates/pubkeyhash/input.js | 16 - src/templates/pubkeyhash/output.js | 20 -- src/templates/scripthash/index.js | 6 - src/templates/scripthash/input.js | 50 ---- src/templates/scripthash/output.js | 18 -- src/templates/witnesscommitment/index.js | 4 - src/templates/witnesscommitment/output.js | 34 --- src/templates/witnesspubkeyhash/index.js | 6 - src/templates/witnesspubkeyhash/input.js | 19 -- src/templates/witnesspubkeyhash/output.js | 17 -- src/templates/witnessscripthash/index.js | 6 - src/templates/witnessscripthash/input.js | 39 --- src/templates/witnessscripthash/output.js | 17 -- test/classify.spec.ts | 180 ------------ test/fixtures/templates.json | 273 +----------------- ts_src/classify.ts | 71 ----- ts_src/templates/multisig/index.ts | 4 - ts_src/templates/multisig/input.ts | 31 -- ts_src/templates/multisig/output.ts | 33 --- ts_src/templates/nulldata.ts | 16 - ts_src/templates/pubkey/index.ts | 4 - ts_src/templates/pubkey/input.ts | 16 - ts_src/templates/pubkey/output.ts | 18 -- ts_src/templates/pubkeyhash/index.ts | 4 - ts_src/templates/pubkeyhash/input.ts | 17 -- ts_src/templates/pubkeyhash/output.ts | 20 -- ts_src/templates/scripthash/index.ts | 4 - ts_src/templates/scripthash/input.ts | 61 ---- ts_src/templates/scripthash/output.ts | 18 -- ts_src/templates/witnesscommitment/index.ts | 3 - ts_src/templates/witnesscommitment/output.ts | 40 --- ts_src/templates/witnesspubkeyhash/index.ts | 4 - ts_src/templates/witnesspubkeyhash/input.ts | 21 -- ts_src/templates/witnesspubkeyhash/output.ts | 13 - ts_src/templates/witnessscripthash/index.ts | 4 - ts_src/templates/witnessscripthash/input.ts | 47 --- ts_src/templates/witnessscripthash/output.ts | 13 - types/classify.d.ts | 15 - types/templates/multisig/index.d.ts | 3 - types/templates/multisig/input.d.ts | 5 - types/templates/multisig/output.d.ts | 5 - types/templates/nulldata.d.ts | 8 - types/templates/pubkey/index.d.ts | 3 - types/templates/pubkey/input.d.ts | 5 - types/templates/pubkey/output.d.ts | 5 - types/templates/pubkeyhash/index.d.ts | 3 - types/templates/pubkeyhash/input.d.ts | 5 - types/templates/pubkeyhash/output.d.ts | 4 - types/templates/scripthash/index.d.ts | 3 - types/templates/scripthash/input.d.ts | 4 - types/templates/scripthash/output.d.ts | 4 - types/templates/witnesscommitment/index.d.ts | 2 - types/templates/witnesscommitment/output.d.ts | 6 - types/templates/witnesspubkeyhash/index.d.ts | 3 - types/templates/witnesspubkeyhash/input.d.ts | 5 - types/templates/witnesspubkeyhash/output.d.ts | 4 - types/templates/witnessscripthash/index.d.ts | 3 - types/templates/witnessscripthash/input.d.ts | 4 - types/templates/witnessscripthash/output.d.ts | 4 - 68 files changed, 1 insertion(+), 1440 deletions(-) delete mode 100644 src/classify.js delete mode 100644 src/templates/multisig/index.js delete mode 100644 src/templates/multisig/input.js delete mode 100644 src/templates/multisig/output.js delete mode 100644 src/templates/nulldata.js delete mode 100644 src/templates/pubkey/index.js delete mode 100644 src/templates/pubkey/input.js delete mode 100644 src/templates/pubkey/output.js delete mode 100644 src/templates/pubkeyhash/index.js delete mode 100644 src/templates/pubkeyhash/input.js delete mode 100644 src/templates/pubkeyhash/output.js delete mode 100644 src/templates/scripthash/index.js delete mode 100644 src/templates/scripthash/input.js delete mode 100644 src/templates/scripthash/output.js delete mode 100644 src/templates/witnesscommitment/index.js delete mode 100644 src/templates/witnesscommitment/output.js delete mode 100644 src/templates/witnesspubkeyhash/index.js delete mode 100644 src/templates/witnesspubkeyhash/input.js delete mode 100644 src/templates/witnesspubkeyhash/output.js delete mode 100644 src/templates/witnessscripthash/index.js delete mode 100644 src/templates/witnessscripthash/input.js delete mode 100644 src/templates/witnessscripthash/output.js delete mode 100644 test/classify.spec.ts delete mode 100644 ts_src/classify.ts delete mode 100644 ts_src/templates/multisig/index.ts delete mode 100644 ts_src/templates/multisig/input.ts delete mode 100644 ts_src/templates/multisig/output.ts delete mode 100644 ts_src/templates/nulldata.ts delete mode 100644 ts_src/templates/pubkey/index.ts delete mode 100644 ts_src/templates/pubkey/input.ts delete mode 100644 ts_src/templates/pubkey/output.ts delete mode 100644 ts_src/templates/pubkeyhash/index.ts delete mode 100644 ts_src/templates/pubkeyhash/input.ts delete mode 100644 ts_src/templates/pubkeyhash/output.ts delete mode 100644 ts_src/templates/scripthash/index.ts delete mode 100644 ts_src/templates/scripthash/input.ts delete mode 100644 ts_src/templates/scripthash/output.ts delete mode 100644 ts_src/templates/witnesscommitment/index.ts delete mode 100644 ts_src/templates/witnesscommitment/output.ts delete mode 100644 ts_src/templates/witnesspubkeyhash/index.ts delete mode 100644 ts_src/templates/witnesspubkeyhash/input.ts delete mode 100644 ts_src/templates/witnesspubkeyhash/output.ts delete mode 100644 ts_src/templates/witnessscripthash/index.ts delete mode 100644 ts_src/templates/witnessscripthash/input.ts delete mode 100644 ts_src/templates/witnessscripthash/output.ts delete mode 100644 types/classify.d.ts delete mode 100644 types/templates/multisig/index.d.ts delete mode 100644 types/templates/multisig/input.d.ts delete mode 100644 types/templates/multisig/output.d.ts delete mode 100644 types/templates/nulldata.d.ts delete mode 100644 types/templates/pubkey/index.d.ts delete mode 100644 types/templates/pubkey/input.d.ts delete mode 100644 types/templates/pubkey/output.d.ts delete mode 100644 types/templates/pubkeyhash/index.d.ts delete mode 100644 types/templates/pubkeyhash/input.d.ts delete mode 100644 types/templates/pubkeyhash/output.d.ts delete mode 100644 types/templates/scripthash/index.d.ts delete mode 100644 types/templates/scripthash/input.d.ts delete mode 100644 types/templates/scripthash/output.d.ts delete mode 100644 types/templates/witnesscommitment/index.d.ts delete mode 100644 types/templates/witnesscommitment/output.d.ts delete mode 100644 types/templates/witnesspubkeyhash/index.d.ts delete mode 100644 types/templates/witnesspubkeyhash/input.d.ts delete mode 100644 types/templates/witnesspubkeyhash/output.d.ts delete mode 100644 types/templates/witnessscripthash/index.d.ts delete mode 100644 types/templates/witnessscripthash/input.d.ts delete mode 100644 types/templates/witnessscripthash/output.d.ts diff --git a/src/classify.js b/src/classify.js deleted file mode 100644 index 70c600c..0000000 --- a/src/classify.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const script_1 = require('./script'); -const multisig = require('./templates/multisig'); -const nullData = require('./templates/nulldata'); -const pubKey = require('./templates/pubkey'); -const pubKeyHash = require('./templates/pubkeyhash'); -const scriptHash = require('./templates/scripthash'); -const witnessCommitment = require('./templates/witnesscommitment'); -const witnessPubKeyHash = require('./templates/witnesspubkeyhash'); -const witnessScriptHash = require('./templates/witnessscripthash'); -const types = { - P2MS: 'multisig', - NONSTANDARD: 'nonstandard', - NULLDATA: 'nulldata', - P2PK: 'pubkey', - P2PKH: 'pubkeyhash', - P2SH: 'scripthash', - P2WPKH: 'witnesspubkeyhash', - P2WSH: 'witnessscripthash', - WITNESS_COMMITMENT: 'witnesscommitment', -}; -exports.types = types; -function classifyOutput(script) { - if (witnessPubKeyHash.output.check(script)) return types.P2WPKH; - if (witnessScriptHash.output.check(script)) return types.P2WSH; - if (pubKeyHash.output.check(script)) return types.P2PKH; - if (scriptHash.output.check(script)) return types.P2SH; - // XXX: optimization, below functions .decompile before use - const chunks = script_1.decompile(script); - if (!chunks) throw new TypeError('Invalid script'); - if (multisig.output.check(chunks)) return types.P2MS; - if (pubKey.output.check(chunks)) return types.P2PK; - if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT; - if (nullData.output.check(chunks)) return types.NULLDATA; - return types.NONSTANDARD; -} -exports.output = classifyOutput; -function classifyInput(script, allowIncomplete) { - // XXX: optimization, below functions .decompile before use - const chunks = script_1.decompile(script); - if (!chunks) throw new TypeError('Invalid script'); - if (pubKeyHash.input.check(chunks)) return types.P2PKH; - if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH; - if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS; - if (pubKey.input.check(chunks)) return types.P2PK; - return types.NONSTANDARD; -} -exports.input = classifyInput; -function classifyWitness(script, allowIncomplete) { - // XXX: optimization, below functions .decompile before use - const chunks = script_1.decompile(script); - if (!chunks) throw new TypeError('Invalid script'); - if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH; - if (witnessScriptHash.input.check(chunks, allowIncomplete)) - return types.P2WSH; - return types.NONSTANDARD; -} -exports.witness = classifyWitness; diff --git a/src/templates/multisig/index.js b/src/templates/multisig/index.js deleted file mode 100644 index b8cd6c4..0000000 --- a/src/templates/multisig/index.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const input = require('./input'); -exports.input = input; -const output = require('./output'); -exports.output = output; diff --git a/src/templates/multisig/input.js b/src/templates/multisig/input.js deleted file mode 100644 index 403c2f7..0000000 --- a/src/templates/multisig/input.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; -// OP_0 [signatures ...] -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const script_1 = require('../../script'); -function partialSignature(value) { - return ( - value === script_1.OPS.OP_0 || bscript.isCanonicalScriptSignature(value) - ); -} -function check(script, allowIncomplete) { - const chunks = bscript.decompile(script); - if (chunks.length < 2) return false; - if (chunks[0] !== script_1.OPS.OP_0) return false; - if (allowIncomplete) { - return chunks.slice(1).every(partialSignature); - } - return chunks.slice(1).every(bscript.isCanonicalScriptSignature); -} -exports.check = check; -check.toJSON = () => { - return 'multisig input'; -}; diff --git a/src/templates/multisig/output.js b/src/templates/multisig/output.js deleted file mode 100644 index 0896605..0000000 --- a/src/templates/multisig/output.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; -// m [pubKeys ...] n OP_CHECKMULTISIG -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const script_1 = require('../../script'); -const types = require('../../types'); -const OP_INT_BASE = script_1.OPS.OP_RESERVED; // OP_1 - 1 -function check(script, allowIncomplete) { - const chunks = bscript.decompile(script); - if (chunks.length < 4) return false; - if (chunks[chunks.length - 1] !== script_1.OPS.OP_CHECKMULTISIG) return false; - if (!types.Number(chunks[0])) return false; - if (!types.Number(chunks[chunks.length - 2])) return false; - const m = chunks[0] - OP_INT_BASE; - const n = chunks[chunks.length - 2] - OP_INT_BASE; - if (m <= 0) return false; - if (n > 16) return false; - if (m > n) return false; - if (n !== chunks.length - 3) return false; - if (allowIncomplete) return true; - const keys = chunks.slice(1, -2); - return keys.every(bscript.isCanonicalPubKey); -} -exports.check = check; -check.toJSON = () => { - return 'multi-sig output'; -}; diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js deleted file mode 100644 index 50355d3..0000000 --- a/src/templates/nulldata.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -// OP_RETURN {data} -const bscript = require('../script'); -const OPS = bscript.OPS; -function check(script) { - const buffer = bscript.compile(script); - return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; -} -exports.check = check; -check.toJSON = () => { - return 'null data output'; -}; -const output = { check }; -exports.output = output; diff --git a/src/templates/pubkey/index.js b/src/templates/pubkey/index.js deleted file mode 100644 index b8cd6c4..0000000 --- a/src/templates/pubkey/index.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const input = require('./input'); -exports.input = input; -const output = require('./output'); -exports.output = output; diff --git a/src/templates/pubkey/input.js b/src/templates/pubkey/input.js deleted file mode 100644 index 9715b80..0000000 --- a/src/templates/pubkey/input.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; -// {signature} -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -function check(script) { - const chunks = bscript.decompile(script); - return chunks.length === 1 && bscript.isCanonicalScriptSignature(chunks[0]); -} -exports.check = check; -check.toJSON = () => { - return 'pubKey input'; -}; diff --git a/src/templates/pubkey/output.js b/src/templates/pubkey/output.js deleted file mode 100644 index 2edb731..0000000 --- a/src/templates/pubkey/output.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; -// {pubKey} OP_CHECKSIG -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const script_1 = require('../../script'); -function check(script) { - const chunks = bscript.decompile(script); - return ( - chunks.length === 2 && - bscript.isCanonicalPubKey(chunks[0]) && - chunks[1] === script_1.OPS.OP_CHECKSIG - ); -} -exports.check = check; -check.toJSON = () => { - return 'pubKey output'; -}; diff --git a/src/templates/pubkeyhash/index.js b/src/templates/pubkeyhash/index.js deleted file mode 100644 index b8cd6c4..0000000 --- a/src/templates/pubkeyhash/index.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const input = require('./input'); -exports.input = input; -const output = require('./output'); -exports.output = output; diff --git a/src/templates/pubkeyhash/input.js b/src/templates/pubkeyhash/input.js deleted file mode 100644 index 14d72cc..0000000 --- a/src/templates/pubkeyhash/input.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; -// {signature} {pubKey} -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -function check(script) { - const chunks = bscript.decompile(script); - return ( - chunks.length === 2 && - bscript.isCanonicalScriptSignature(chunks[0]) && - bscript.isCanonicalPubKey(chunks[1]) - ); -} -exports.check = check; -check.toJSON = () => { - return 'pubKeyHash input'; -}; diff --git a/src/templates/pubkeyhash/output.js b/src/templates/pubkeyhash/output.js deleted file mode 100644 index 079e1ed..0000000 --- a/src/templates/pubkeyhash/output.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; -// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const script_1 = require('../../script'); -function check(script) { - const buffer = bscript.compile(script); - return ( - buffer.length === 25 && - buffer[0] === script_1.OPS.OP_DUP && - buffer[1] === script_1.OPS.OP_HASH160 && - buffer[2] === 0x14 && - buffer[23] === script_1.OPS.OP_EQUALVERIFY && - buffer[24] === script_1.OPS.OP_CHECKSIG - ); -} -exports.check = check; -check.toJSON = () => { - return 'pubKeyHash output'; -}; diff --git a/src/templates/scripthash/index.js b/src/templates/scripthash/index.js deleted file mode 100644 index b8cd6c4..0000000 --- a/src/templates/scripthash/index.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const input = require('./input'); -exports.input = input; -const output = require('./output'); -exports.output = output; diff --git a/src/templates/scripthash/input.js b/src/templates/scripthash/input.js deleted file mode 100644 index 999cc83..0000000 --- a/src/templates/scripthash/input.js +++ /dev/null @@ -1,50 +0,0 @@ -'use strict'; -// <scriptSig> {serialized scriptPubKey script} -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const p2ms = require('../multisig'); -const p2pk = require('../pubkey'); -const p2pkh = require('../pubkeyhash'); -const p2wpkho = require('../witnesspubkeyhash/output'); -const p2wsho = require('../witnessscripthash/output'); -function check(script, allowIncomplete) { - const chunks = bscript.decompile(script); - if (chunks.length < 1) return false; - const lastChunk = chunks[chunks.length - 1]; - if (!Buffer.isBuffer(lastChunk)) return false; - const scriptSigChunks = bscript.decompile( - bscript.compile(chunks.slice(0, -1)), - ); - const redeemScriptChunks = bscript.decompile(lastChunk); - // is redeemScript a valid script? - if (!redeemScriptChunks) return false; - // is redeemScriptSig push only? - if (!bscript.isPushOnly(scriptSigChunks)) return false; - // is witness? - if (chunks.length === 1) { - return ( - p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks) - ); - } - // match types - if ( - p2pkh.input.check(scriptSigChunks) && - p2pkh.output.check(redeemScriptChunks) - ) - return true; - if ( - p2ms.input.check(scriptSigChunks, allowIncomplete) && - p2ms.output.check(redeemScriptChunks) - ) - return true; - if ( - p2pk.input.check(scriptSigChunks) && - p2pk.output.check(redeemScriptChunks) - ) - return true; - return false; -} -exports.check = check; -check.toJSON = () => { - return 'scriptHash input'; -}; diff --git a/src/templates/scripthash/output.js b/src/templates/scripthash/output.js deleted file mode 100644 index 3797003..0000000 --- a/src/templates/scripthash/output.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; -// OP_HASH160 {scriptHash} OP_EQUAL -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const script_1 = require('../../script'); -function check(script) { - const buffer = bscript.compile(script); - return ( - buffer.length === 23 && - buffer[0] === script_1.OPS.OP_HASH160 && - buffer[1] === 0x14 && - buffer[22] === script_1.OPS.OP_EQUAL - ); -} -exports.check = check; -check.toJSON = () => { - return 'scriptHash output'; -}; diff --git a/src/templates/witnesscommitment/index.js b/src/templates/witnesscommitment/index.js deleted file mode 100644 index 099ac72..0000000 --- a/src/templates/witnesscommitment/index.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const output = require('./output'); -exports.output = output; diff --git a/src/templates/witnesscommitment/output.js b/src/templates/witnesscommitment/output.js deleted file mode 100644 index fb1d59c..0000000 --- a/src/templates/witnesscommitment/output.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; -// OP_RETURN {aa21a9ed} {commitment} -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const script_1 = require('../../script'); -const types = require('../../types'); -const typeforce = require('typeforce'); -const HEADER = Buffer.from('aa21a9ed', 'hex'); -function check(script) { - const buffer = bscript.compile(script); - return ( - buffer.length > 37 && - buffer[0] === script_1.OPS.OP_RETURN && - buffer[1] === 0x24 && - buffer.slice(2, 6).equals(HEADER) - ); -} -exports.check = check; -check.toJSON = () => { - return 'Witness commitment output'; -}; -function encode(commitment) { - typeforce(types.Hash256bit, commitment); - const buffer = Buffer.allocUnsafe(36); - HEADER.copy(buffer, 0); - commitment.copy(buffer, 4); - return bscript.compile([script_1.OPS.OP_RETURN, buffer]); -} -exports.encode = encode; -function decode(buffer) { - typeforce(check, buffer); - return bscript.decompile(buffer)[1].slice(4, 36); -} -exports.decode = decode; diff --git a/src/templates/witnesspubkeyhash/index.js b/src/templates/witnesspubkeyhash/index.js deleted file mode 100644 index b8cd6c4..0000000 --- a/src/templates/witnesspubkeyhash/index.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const input = require('./input'); -exports.input = input; -const output = require('./output'); -exports.output = output; diff --git a/src/templates/witnesspubkeyhash/input.js b/src/templates/witnesspubkeyhash/input.js deleted file mode 100644 index 4343584..0000000 --- a/src/templates/witnesspubkeyhash/input.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; -// {signature} {pubKey} -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -function isCompressedCanonicalPubKey(pubKey) { - return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; -} -function check(script) { - const chunks = bscript.decompile(script); - return ( - chunks.length === 2 && - bscript.isCanonicalScriptSignature(chunks[0]) && - isCompressedCanonicalPubKey(chunks[1]) - ); -} -exports.check = check; -check.toJSON = () => { - return 'witnessPubKeyHash input'; -}; diff --git a/src/templates/witnesspubkeyhash/output.js b/src/templates/witnesspubkeyhash/output.js deleted file mode 100644 index ea5ed1e..0000000 --- a/src/templates/witnesspubkeyhash/output.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; -// OP_0 {pubKeyHash} -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const script_1 = require('../../script'); -function check(script) { - const buffer = bscript.compile(script); - return ( - buffer.length === 22 && - buffer[0] === script_1.OPS.OP_0 && - buffer[1] === 0x14 - ); -} -exports.check = check; -check.toJSON = () => { - return 'Witness pubKeyHash output'; -}; diff --git a/src/templates/witnessscripthash/index.js b/src/templates/witnessscripthash/index.js deleted file mode 100644 index b8cd6c4..0000000 --- a/src/templates/witnessscripthash/index.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const input = require('./input'); -exports.input = input; -const output = require('./output'); -exports.output = output; diff --git a/src/templates/witnessscripthash/input.js b/src/templates/witnessscripthash/input.js deleted file mode 100644 index f69a810..0000000 --- a/src/templates/witnessscripthash/input.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; -// <scriptSig> {serialized scriptPubKey script} -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const typeforce = require('typeforce'); -const p2ms = require('../multisig'); -const p2pk = require('../pubkey'); -const p2pkh = require('../pubkeyhash'); -function check(chunks, allowIncomplete) { - typeforce(typeforce.Array, chunks); - if (chunks.length < 1) return false; - const witnessScript = chunks[chunks.length - 1]; - if (!Buffer.isBuffer(witnessScript)) return false; - const witnessScriptChunks = bscript.decompile(witnessScript); - // is witnessScript a valid script? - if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false; - const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); - // match types - if ( - p2pkh.input.check(witnessRawScriptSig) && - p2pkh.output.check(witnessScriptChunks) - ) - return true; - if ( - p2ms.input.check(witnessRawScriptSig, allowIncomplete) && - p2ms.output.check(witnessScriptChunks) - ) - return true; - if ( - p2pk.input.check(witnessRawScriptSig) && - p2pk.output.check(witnessScriptChunks) - ) - return true; - return false; -} -exports.check = check; -check.toJSON = () => { - return 'witnessScriptHash input'; -}; diff --git a/src/templates/witnessscripthash/output.js b/src/templates/witnessscripthash/output.js deleted file mode 100644 index f69a429..0000000 --- a/src/templates/witnessscripthash/output.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; -// OP_0 {scriptHash} -Object.defineProperty(exports, '__esModule', { value: true }); -const bscript = require('../../script'); -const script_1 = require('../../script'); -function check(script) { - const buffer = bscript.compile(script); - return ( - buffer.length === 34 && - buffer[0] === script_1.OPS.OP_0 && - buffer[1] === 0x20 - ); -} -exports.check = check; -check.toJSON = () => { - return 'Witness scriptHash output'; -}; diff --git a/test/classify.spec.ts b/test/classify.spec.ts deleted file mode 100644 index 931250e..0000000 --- a/test/classify.spec.ts +++ /dev/null @@ -1,180 +0,0 @@ -import * as assert from 'assert'; -import { describe, it } from 'mocha'; -import * as classify from '../src/classify'; -import * as bscript from '../src/script'; - -import * as fixtures from './fixtures/templates.json'; - -import * as multisig from '../src/templates/multisig'; -import * as nullData from '../src/templates/nulldata'; -import * as pubKey from '../src/templates/pubkey'; -import * as pubKeyHash from '../src/templates/pubkeyhash'; -import * as scriptHash from '../src/templates/scripthash'; -import * as witnessCommitment from '../src/templates/witnesscommitment'; -import * as witnessPubKeyHash from '../src/templates/witnesspubkeyhash'; -import * as witnessScriptHash from '../src/templates/witnessscripthash'; - -const tmap = { - pubKey, - pubKeyHash, - scriptHash, - witnessPubKeyHash, - witnessScriptHash, - multisig, - nullData, - witnessCommitment, -}; - -describe('classify', () => { - describe('input', () => { - fixtures.valid.forEach(f => { - if (!f.input) return; - - it('classifies ' + f.input + ' as ' + f.type, () => { - const input = bscript.fromASM(f.input); - const type = classify.input(input); - - assert.strictEqual(type, f.type); - }); - }); - - fixtures.valid.forEach(f => { - if (!f.input) return; - if (!f.typeIncomplete) return; - - it('classifies incomplete ' + f.input + ' as ' + f.typeIncomplete, () => { - const input = bscript.fromASM(f.input); - const type = classify.input(input, true); - - assert.strictEqual(type, f.typeIncomplete); - }); - }); - }); - - describe('classifyOutput', () => { - fixtures.valid.forEach(f => { - if (!f.output) return; - - it('classifies ' + f.output + ' as ' + f.type, () => { - const output = bscript.fromASM(f.output); - const type = classify.output(output); - - assert.strictEqual(type, f.type); - }); - }); - }); - [ - 'pubKey', - 'pubKeyHash', - 'scriptHash', - 'witnessPubKeyHash', - 'witnessScriptHash', - 'multisig', - 'nullData', - 'witnessCommitment', - ].forEach(name => { - const inputType = (tmap as any)[name].input; - const outputType = (tmap as any)[name].output; - - describe(name + '.input.check', () => { - fixtures.valid.forEach(f => { - if (name.toLowerCase() === classify.types.P2WPKH) return; - if (name.toLowerCase() === classify.types.P2WSH) return; - const expected = name.toLowerCase() === f.type.toLowerCase(); - - if (inputType && f.input) { - const input = bscript.fromASM(f.input); - - it('returns ' + expected + ' for ' + f.input, () => { - assert.strictEqual(inputType.check(input), expected); - }); - - if (f.typeIncomplete) { - const expectedIncomplete = name.toLowerCase() === f.typeIncomplete; - - it('returns ' + expected + ' for ' + f.input, () => { - assert.strictEqual( - inputType.check(input, true), - expectedIncomplete, - ); - }); - } - } - }); - - if (!(fixtures.invalid as any)[name]) return; - - (fixtures.invalid as any)[name].inputs.forEach((f: any) => { - if (!f.input && !f.inputHex) return; - - it( - 'returns false for ' + - f.description + - ' (' + - (f.input || f.inputHex) + - ')', - () => { - let input; - - if (f.input) { - input = bscript.fromASM(f.input); - } else { - input = Buffer.from(f.inputHex, 'hex'); - } - - assert.strictEqual(inputType.check(input), false); - }, - ); - }); - }); - - describe(name + '.output.check', () => { - fixtures.valid.forEach(f => { - const expected = name.toLowerCase() === f.type; - - if (outputType && f.output) { - it('returns ' + expected + ' for ' + f.output, () => { - const output = bscript.fromASM(f.output); - - if ( - name.toLowerCase() === 'nulldata' && - f.type === classify.types.WITNESS_COMMITMENT - ) - return; - if ( - name.toLowerCase() === 'witnesscommitment' && - f.type === classify.types.NULLDATA - ) - return; - assert.strictEqual(outputType.check(output), expected); - }); - } - }); - - if (!(fixtures.invalid as any)[name]) return; - - (fixtures.invalid as any)[name].outputs.forEach((f: any) => { - if (!f.output && !f.outputHex) return; - - it( - 'returns false for ' + - f.description + - ' (' + - (f.output || f.outputHex) + - ')', - () => { - let output; - - if (f.output) { - output = bscript.fromASM(f.output); - } else { - output = Buffer.from(f.outputHex, 'hex'); - } - - assert.strictEqual(outputType.check(output), false); - }, - ); - }); - }); - }); -}); diff --git a/test/fixtures/templates.json b/test/fixtures/templates.json index 61728c0..848d264 100644 --- a/test/fixtures/templates.json +++ b/test/fixtures/templates.json @@ -255,276 +255,5 @@ "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ] } - ], - "invalid": { - "pubKey": { - "inputs": [ - { - "description": "non-canonical signature (too short)", - "input": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf7593" - }, - { - "description": "non-canonical signature (too long)", - "input": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28ffffffff01" - }, - { - "description": "non-canonical signature (invalid hashType)", - "input": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28ff" - } - ], - "outputs": [ - { - "description": "non-canonical pubkey (too short)", - "output": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce OP_CHECKSIG" - }, - { - "description": "non-canonical pubkey (too long)", - "output": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1ffffff OP_CHECKSIG" - }, - { - "description": "last operator is wrong for pubkey-output", - "outputHex": "21027a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752ae" - }, - { - "description": "missing OP_CHECKSIG", - "outputHex": "21027a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752" - }, - { - "description": "non-canonical pubkey (bad prefix)", - "output": "427a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752 OP_CHECKSIG" - }, - { - "description": "has extra opcode at the end isPubKeyOutput", - "output": "027a71801ab59336de37785c50005b6abd8ea859eecce1edbe8e81afa74ee5c752 OP_CHECKSIG OP_0" - } - ] - }, - "pubKeyHash": { - "inputs": [ - { - "description": "pubKeyHash input : extraneous data", - "input": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 ffffffff" - } - ], - "outputs": [ - { - "description": "non-minimal encoded isPubKeyHashOutput (non BIP62 compliant)", - "outputHex": "76a94c14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" - }, - { - "description": "bad OP_DUP isPubKeyHashOutput", - "outputHex": "aca914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" - }, - { - "description": "bad OP_HASH160 isPubKeyHashOutput", - "outputHex": "76ac14aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" - }, - { - "description": "bad OP_EQUALVERIFY isPubKeyHashOutput", - "outputHex": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5acac" - }, - { - "description": "bad OP_CHECKSIG isPubKeyHashOutput", - "outputHex": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c58888" - }, - { - "description": "bad length isPubKeyHashOutput", - "outputHex": "76a920aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac" - }, - { - "description": "has something at the end isPubKeyHashOutput", - "outputHex": "76a920aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00" - }, - { - "exception": "Expected Buffer\\(Length: 20\\), got Buffer\\(Length: 2\\)", - "hash": "ffff" - } - ] - }, - "scriptHash": { - "inputs": [ - { - "description": "redeemScript not data", - "input": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 OP_RESERVED" - }, - { - "description": "redeemScript is a signature, therefore not a valid script", - "input": "OP_0 3045022100e12b17b3a4c80c401a1687487bd2bafee9e5f1f8f1ffc6180ce186672ad7b43a02205e316d1e5e71822f5ef301b694e578fa9c94af4f5f098c952c833f4691307f4e01" - } - ], - "outputs": [ - { - "description": "non-minimal encoded isScriptHashOutput (non BIP62 compliant)", - "outputHex": "a94c14c286a1af0947f58d1ad787385b1c2c4a976f9e7187" - }, - { - "description": "wrong OP_HASH160 opcode", - "outputHex": "ac4c14c286a1af0947f58d1ad787385b1c2c4a976f9e7187" - }, - { - "description": "wrong length marker", - "outputHex": "a916c286a1af0947f58d1ad787385b1c2c4a976f9e7187" - }, - { - "description": "wrong OP_EQUAL opcode", - "outputHex": "a914c286a1af0947f58d1ad787385b1c2c4a976f9e7188" - }, - { - "exception": "Expected Buffer\\(Length: 20\\), got Buffer\\(Length: 3\\)", - "hash": "ffffff" - } - ] - }, - "multisig": { - "inputs": [ - { - "description": "Not enough signatures provided", - "type": "multisig", - "output": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG", - "signatures": [] - }, - { - "exception": "Not enough signatures provided", - "output": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", - "signatures": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" - ] - }, - { - "exception": "Too many signatures provided", - "output": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", - "signatures": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" - ] - } - ], - "outputs": [ - { - "description": "OP_CHECKMULTISIG not found", - "output": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_HASH160" - }, - { - "description": "less than 4 chunks", - "output": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 OP_HASH160" - }, - { - "description": "m === 0", - "output": "OP_0 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "m < OP_1", - "output": "OP_1NEGATE 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "m > OP_16", - "output": "OP_NOP 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "n === 0", - "output": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_0 OP_CHECKMULTISIG" - }, - { - "description": "n < OP_1", - "output": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1NEGATE OP_CHECKMULTISIG" - }, - { - "description": "n > OP_16", - "output": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_NOP OP_CHECKMULTISIG" - }, - { - "description": "n < m", - "output": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_1 OP_CHECKMULTISIG" - }, - { - "description": "n < len(pubKeys)", - "output": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "n > len(pubKeys)", - "output": "OP_1 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_2 OP_CHECKMULTISIG" - }, - { - "description": "m is data", - "output": "ffff 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_1 OP_CHECKMULTISIG" - }, - { - "description": "n is data", - "output": "OP_1 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 ffff OP_CHECKMULTISIG" - }, - { - "description": "non-canonical pubKey (bad length)", - "output": "OP_1 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffff OP_1 OP_CHECKMULTISIG" - }, - { - "exception": "Not enough pubKeys provided", - "m": 4, - "pubKeys": [ - "02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f", - "02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f", - "036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19" - ], - "signatures": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" - ] - } - ] - }, - "witnessPubKeyHash": { - "inputs": [], - "outputs": [ - { - "description": "wrong version", - "outputHex": "51149090909090909090909090909090909090909090" - }, - { - "description": "wrong length marker", - "outputHex": "00209090909090909090909090909090909090909090" - }, - { - "exception": "Expected Buffer\\(Length: 20\\), got Buffer\\(Length: 3\\)", - "hash": "ffffff" - } - ] - }, - "witnessScriptHash": { - "inputs": [], - "outputs": [ - { - "description": "wrong version", - "outputHex": "51209090909090909090909090909090909090909090909090909090909090909090" - }, - { - "description": "wrong length marker", - "outputHex": "00219090909090909090909090909090909090909090909090909090909090909090" - }, - { - "exception": "Expected Buffer\\(Length: 32\\), got Buffer\\(Length: 3\\)", - "hash": "ffffff" - } - ] - }, - "witnessCommitment": { - "inputs": [], - "outputs": [ - { - "exception": "", - "commitment": "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd" - }, - { - "description": "wrong OPCODE at the start", - "scriptPubKeyHex": "6024aa21a9ed4db4fb830efe3e804337413ffe8ad7393af301e0ec8e71b6e6f2b860a56f4dcd" - }, - { - "description": "wrong length marker", - "scriptPubKeyHex": "6a23aa21a9ed4db4fb830efe3e804337413ffe8ad7393af301e0ec8e71b6e6f2b860a56f4dcd" - }, - { - "description": "commitment of wrong length", - "scriptPubKeyHex": "6a23aa21a9ed4db4fb830efe3e804337413ffe8ad7393af301e0ec8e71b6e6f2b860a56f4d" - } - ] - } - } + ] } diff --git a/ts_src/classify.ts b/ts_src/classify.ts deleted file mode 100644 index 319e934..0000000 --- a/ts_src/classify.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { decompile } from './script'; -import * as multisig from './templates/multisig'; -import * as nullData from './templates/nulldata'; -import * as pubKey from './templates/pubkey'; -import * as pubKeyHash from './templates/pubkeyhash'; -import * as scriptHash from './templates/scripthash'; -import * as witnessCommitment from './templates/witnesscommitment'; -import * as witnessPubKeyHash from './templates/witnesspubkeyhash'; -import * as witnessScriptHash from './templates/witnessscripthash'; - -const types = { - P2MS: 'multisig' as string, - NONSTANDARD: 'nonstandard' as string, - NULLDATA: 'nulldata' as string, - P2PK: 'pubkey' as string, - P2PKH: 'pubkeyhash' as string, - P2SH: 'scripthash' as string, - P2WPKH: 'witnesspubkeyhash' as string, - P2WSH: 'witnessscripthash' as string, - WITNESS_COMMITMENT: 'witnesscommitment' as string, -}; - -function classifyOutput(script: Buffer): string { - if (witnessPubKeyHash.output.check(script)) return types.P2WPKH; - if (witnessScriptHash.output.check(script)) return types.P2WSH; - if (pubKeyHash.output.check(script)) return types.P2PKH; - if (scriptHash.output.check(script)) return types.P2SH; - - // XXX: optimization, below functions .decompile before use - const chunks = decompile(script); - if (!chunks) throw new TypeError('Invalid script'); - - if (multisig.output.check(chunks)) return types.P2MS; - if (pubKey.output.check(chunks)) return types.P2PK; - if (witnessCommitment.output.check(chunks)) return types.WITNESS_COMMITMENT; - if (nullData.output.check(chunks)) return types.NULLDATA; - - return types.NONSTANDARD; -} - -function classifyInput(script: Buffer, allowIncomplete?: boolean): string { - // XXX: optimization, below functions .decompile before use - const chunks = decompile(script); - if (!chunks) throw new TypeError('Invalid script'); - - if (pubKeyHash.input.check(chunks)) return types.P2PKH; - if (scriptHash.input.check(chunks, allowIncomplete)) return types.P2SH; - if (multisig.input.check(chunks, allowIncomplete)) return types.P2MS; - if (pubKey.input.check(chunks)) return types.P2PK; - - return types.NONSTANDARD; -} - -function classifyWitness(script: Buffer[], allowIncomplete?: boolean): string { - // XXX: optimization, below functions .decompile before use - const chunks = decompile(script); - if (!chunks) throw new TypeError('Invalid script'); - - if (witnessPubKeyHash.input.check(chunks)) return types.P2WPKH; - if (witnessScriptHash.input.check(chunks as Buffer[], allowIncomplete)) - return types.P2WSH; - - return types.NONSTANDARD; -} - -export { - classifyInput as input, - classifyOutput as output, - classifyWitness as witness, - types, -}; diff --git a/ts_src/templates/multisig/index.ts b/ts_src/templates/multisig/index.ts deleted file mode 100644 index aa2bdcb..0000000 --- a/ts_src/templates/multisig/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as input from './input'; -import * as output from './output'; - -export { input, output }; diff --git a/ts_src/templates/multisig/input.ts b/ts_src/templates/multisig/input.ts deleted file mode 100644 index 31fe416..0000000 --- a/ts_src/templates/multisig/input.ts +++ /dev/null @@ -1,31 +0,0 @@ -// OP_0 [signatures ...] - -import { Stack } from '../../payments'; -import * as bscript from '../../script'; -import { OPS } from '../../script'; - -function partialSignature(value: number | Buffer): boolean { - return ( - value === OPS.OP_0 || bscript.isCanonicalScriptSignature(value as Buffer) - ); -} - -export function check( - script: Buffer | Stack, - allowIncomplete?: boolean, -): boolean { - const chunks = bscript.decompile(script) as Stack; - if (chunks.length < 2) return false; - if (chunks[0] !== OPS.OP_0) return false; - - if (allowIncomplete) { - return chunks.slice(1).every(partialSignature); - } - - return (chunks.slice(1) as Buffer[]).every( - bscript.isCanonicalScriptSignature, - ); -} -check.toJSON = (): string => { - return 'multisig input'; -}; diff --git a/ts_src/templates/multisig/output.ts b/ts_src/templates/multisig/output.ts deleted file mode 100644 index 20c2162..0000000 --- a/ts_src/templates/multisig/output.ts +++ /dev/null @@ -1,33 +0,0 @@ -// m [pubKeys ...] n OP_CHECKMULTISIG - -import { Stack } from '../../payments'; -import * as bscript from '../../script'; -import { OPS } from '../../script'; -import * as types from '../../types'; -const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 - -export function check( - script: Buffer | Stack, - allowIncomplete?: boolean, -): boolean { - const chunks = bscript.decompile(script) as Stack; - - if (chunks.length < 4) return false; - if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false; - if (!types.Number(chunks[0])) return false; - if (!types.Number(chunks[chunks.length - 2])) return false; - const m = (chunks[0] as number) - OP_INT_BASE; - const n = (chunks[chunks.length - 2] as number) - OP_INT_BASE; - - if (m <= 0) return false; - if (n > 16) return false; - if (m > n) return false; - if (n !== chunks.length - 3) return false; - if (allowIncomplete) return true; - - const keys = chunks.slice(1, -2) as Buffer[]; - return keys.every(bscript.isCanonicalPubKey); -} -check.toJSON = (): string => { - return 'multi-sig output'; -}; diff --git a/ts_src/templates/nulldata.ts b/ts_src/templates/nulldata.ts deleted file mode 100644 index 99b5c44..0000000 --- a/ts_src/templates/nulldata.ts +++ /dev/null @@ -1,16 +0,0 @@ -// OP_RETURN {data} -import * as bscript from '../script'; -const OPS = bscript.OPS; - -export function check(script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script); - - return buffer.length > 1 && buffer[0] === OPS.OP_RETURN; -} -check.toJSON = (): string => { - return 'null data output'; -}; - -const output = { check }; - -export { output }; diff --git a/ts_src/templates/pubkey/index.ts b/ts_src/templates/pubkey/index.ts deleted file mode 100644 index aa2bdcb..0000000 --- a/ts_src/templates/pubkey/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as input from './input'; -import * as output from './output'; - -export { input, output }; diff --git a/ts_src/templates/pubkey/input.ts b/ts_src/templates/pubkey/input.ts deleted file mode 100644 index 745e6d9..0000000 --- a/ts_src/templates/pubkey/input.ts +++ /dev/null @@ -1,16 +0,0 @@ -// {signature} - -import { Stack } from '../../payments'; -import * as bscript from '../../script'; - -export function check(script: Buffer | Stack): boolean { - const chunks = bscript.decompile(script) as Stack; - - return ( - chunks.length === 1 && - bscript.isCanonicalScriptSignature(chunks[0] as Buffer) - ); -} -check.toJSON = (): string => { - return 'pubKey input'; -}; diff --git a/ts_src/templates/pubkey/output.ts b/ts_src/templates/pubkey/output.ts deleted file mode 100644 index 1b2c391..0000000 --- a/ts_src/templates/pubkey/output.ts +++ /dev/null @@ -1,18 +0,0 @@ -// {pubKey} OP_CHECKSIG - -import { Stack } from '../../payments'; -import * as bscript from '../../script'; -import { OPS } from '../../script'; - -export function check(script: Buffer | Stack): boolean { - const chunks = bscript.decompile(script) as Stack; - - return ( - chunks.length === 2 && - bscript.isCanonicalPubKey(chunks[0] as Buffer) && - chunks[1] === OPS.OP_CHECKSIG - ); -} -check.toJSON = (): string => { - return 'pubKey output'; -}; diff --git a/ts_src/templates/pubkeyhash/index.ts b/ts_src/templates/pubkeyhash/index.ts deleted file mode 100644 index aa2bdcb..0000000 --- a/ts_src/templates/pubkeyhash/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as input from './input'; -import * as output from './output'; - -export { input, output }; diff --git a/ts_src/templates/pubkeyhash/input.ts b/ts_src/templates/pubkeyhash/input.ts deleted file mode 100644 index de93968..0000000 --- a/ts_src/templates/pubkeyhash/input.ts +++ /dev/null @@ -1,17 +0,0 @@ -// {signature} {pubKey} - -import { Stack } from '../../payments'; -import * as bscript from '../../script'; - -export function check(script: Buffer | Stack): boolean { - const chunks = bscript.decompile(script) as Stack; - - return ( - chunks.length === 2 && - bscript.isCanonicalScriptSignature(chunks[0] as Buffer) && - bscript.isCanonicalPubKey(chunks[1] as Buffer) - ); -} -check.toJSON = (): string => { - return 'pubKeyHash input'; -}; diff --git a/ts_src/templates/pubkeyhash/output.ts b/ts_src/templates/pubkeyhash/output.ts deleted file mode 100644 index 248c210..0000000 --- a/ts_src/templates/pubkeyhash/output.ts +++ /dev/null @@ -1,20 +0,0 @@ -// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG - -import * as bscript from '../../script'; -import { OPS } from '../../script'; - -export function check(script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script); - - return ( - buffer.length === 25 && - buffer[0] === OPS.OP_DUP && - buffer[1] === OPS.OP_HASH160 && - buffer[2] === 0x14 && - buffer[23] === OPS.OP_EQUALVERIFY && - buffer[24] === OPS.OP_CHECKSIG - ); -} -check.toJSON = (): string => { - return 'pubKeyHash output'; -}; diff --git a/ts_src/templates/scripthash/index.ts b/ts_src/templates/scripthash/index.ts deleted file mode 100644 index aa2bdcb..0000000 --- a/ts_src/templates/scripthash/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as input from './input'; -import * as output from './output'; - -export { input, output }; diff --git a/ts_src/templates/scripthash/input.ts b/ts_src/templates/scripthash/input.ts deleted file mode 100644 index 3ef8aab..0000000 --- a/ts_src/templates/scripthash/input.ts +++ /dev/null @@ -1,61 +0,0 @@ -// <scriptSig> {serialized scriptPubKey script} - -import * as bscript from '../../script'; -import * as p2ms from '../multisig'; -import * as p2pk from '../pubkey'; -import * as p2pkh from '../pubkeyhash'; -import * as p2wpkho from '../witnesspubkeyhash/output'; -import * as p2wsho from '../witnessscripthash/output'; - -export function check( - script: Buffer | Array<number | Buffer>, - allowIncomplete?: boolean, -): boolean { - const chunks = bscript.decompile(script)!; - if (chunks.length < 1) return false; - - const lastChunk = chunks[chunks.length - 1]; - if (!Buffer.isBuffer(lastChunk)) return false; - - const scriptSigChunks = bscript.decompile( - bscript.compile(chunks.slice(0, -1)), - )!; - const redeemScriptChunks = bscript.decompile(lastChunk); - - // is redeemScript a valid script? - if (!redeemScriptChunks) return false; - - // is redeemScriptSig push only? - if (!bscript.isPushOnly(scriptSigChunks)) return false; - - // is witness? - if (chunks.length === 1) { - return ( - p2wsho.check(redeemScriptChunks) || p2wpkho.check(redeemScriptChunks) - ); - } - - // match types - if ( - p2pkh.input.check(scriptSigChunks) && - p2pkh.output.check(redeemScriptChunks) - ) - return true; - - if ( - p2ms.input.check(scriptSigChunks, allowIncomplete) && - p2ms.output.check(redeemScriptChunks) - ) - return true; - - if ( - p2pk.input.check(scriptSigChunks) && - p2pk.output.check(redeemScriptChunks) - ) - return true; - - return false; -} -check.toJSON = (): string => { - return 'scriptHash input'; -}; diff --git a/ts_src/templates/scripthash/output.ts b/ts_src/templates/scripthash/output.ts deleted file mode 100644 index aea8e24..0000000 --- a/ts_src/templates/scripthash/output.ts +++ /dev/null @@ -1,18 +0,0 @@ -// OP_HASH160 {scriptHash} OP_EQUAL - -import * as bscript from '../../script'; -import { OPS } from '../../script'; - -export function check(script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script); - - return ( - buffer.length === 23 && - buffer[0] === OPS.OP_HASH160 && - buffer[1] === 0x14 && - buffer[22] === OPS.OP_EQUAL - ); -} -check.toJSON = (): string => { - return 'scriptHash output'; -}; diff --git a/ts_src/templates/witnesscommitment/index.ts b/ts_src/templates/witnesscommitment/index.ts deleted file mode 100644 index d5c166d..0000000 --- a/ts_src/templates/witnesscommitment/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as output from './output'; - -export { output }; diff --git a/ts_src/templates/witnesscommitment/output.ts b/ts_src/templates/witnesscommitment/output.ts deleted file mode 100644 index 482798f..0000000 --- a/ts_src/templates/witnesscommitment/output.ts +++ /dev/null @@ -1,40 +0,0 @@ -// OP_RETURN {aa21a9ed} {commitment} - -import * as bscript from '../../script'; -import { OPS } from '../../script'; -import * as types from '../../types'; - -const typeforce = require('typeforce'); - -const HEADER: Buffer = Buffer.from('aa21a9ed', 'hex'); - -export function check(script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script); - - return ( - buffer.length > 37 && - buffer[0] === OPS.OP_RETURN && - buffer[1] === 0x24 && - buffer.slice(2, 6).equals(HEADER) - ); -} - -check.toJSON = (): string => { - return 'Witness commitment output'; -}; - -export function encode(commitment: Buffer): Buffer { - typeforce(types.Hash256bit, commitment); - - const buffer = Buffer.allocUnsafe(36); - HEADER.copy(buffer, 0); - commitment.copy(buffer, 4); - - return bscript.compile([OPS.OP_RETURN, buffer]); -} - -export function decode(buffer: Buffer): Buffer { - typeforce(check, buffer); - - return (bscript.decompile(buffer)![1] as Buffer).slice(4, 36); -} diff --git a/ts_src/templates/witnesspubkeyhash/index.ts b/ts_src/templates/witnesspubkeyhash/index.ts deleted file mode 100644 index aa2bdcb..0000000 --- a/ts_src/templates/witnesspubkeyhash/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as input from './input'; -import * as output from './output'; - -export { input, output }; diff --git a/ts_src/templates/witnesspubkeyhash/input.ts b/ts_src/templates/witnesspubkeyhash/input.ts deleted file mode 100644 index 26fe63d..0000000 --- a/ts_src/templates/witnesspubkeyhash/input.ts +++ /dev/null @@ -1,21 +0,0 @@ -// {signature} {pubKey} - -import { Stack } from '../../payments'; -import * as bscript from '../../script'; - -function isCompressedCanonicalPubKey(pubKey: Buffer): boolean { - return bscript.isCanonicalPubKey(pubKey) && pubKey.length === 33; -} - -export function check(script: Buffer | Stack): boolean { - const chunks = bscript.decompile(script) as Stack; - - return ( - chunks.length === 2 && - bscript.isCanonicalScriptSignature(chunks[0] as Buffer) && - isCompressedCanonicalPubKey(chunks[1] as Buffer) - ); -} -check.toJSON = (): string => { - return 'witnessPubKeyHash input'; -}; diff --git a/ts_src/templates/witnesspubkeyhash/output.ts b/ts_src/templates/witnesspubkeyhash/output.ts deleted file mode 100644 index bfd6690..0000000 --- a/ts_src/templates/witnesspubkeyhash/output.ts +++ /dev/null @@ -1,13 +0,0 @@ -// OP_0 {pubKeyHash} - -import * as bscript from '../../script'; -import { OPS } from '../../script'; - -export function check(script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script); - - return buffer.length === 22 && buffer[0] === OPS.OP_0 && buffer[1] === 0x14; -} -check.toJSON = (): string => { - return 'Witness pubKeyHash output'; -}; diff --git a/ts_src/templates/witnessscripthash/index.ts b/ts_src/templates/witnessscripthash/index.ts deleted file mode 100644 index aa2bdcb..0000000 --- a/ts_src/templates/witnessscripthash/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as input from './input'; -import * as output from './output'; - -export { input, output }; diff --git a/ts_src/templates/witnessscripthash/input.ts b/ts_src/templates/witnessscripthash/input.ts deleted file mode 100644 index 0f63330..0000000 --- a/ts_src/templates/witnessscripthash/input.ts +++ /dev/null @@ -1,47 +0,0 @@ -// <scriptSig> {serialized scriptPubKey script} - -import * as bscript from '../../script'; -const typeforce = require('typeforce'); - -import * as p2ms from '../multisig'; -import * as p2pk from '../pubkey'; -import * as p2pkh from '../pubkeyhash'; - -export function check(chunks: Buffer[], allowIncomplete?: boolean): boolean { - typeforce(typeforce.Array, chunks); - if (chunks.length < 1) return false; - - const witnessScript = chunks[chunks.length - 1]; - if (!Buffer.isBuffer(witnessScript)) return false; - - const witnessScriptChunks = bscript.decompile(witnessScript); - - // is witnessScript a valid script? - if (!witnessScriptChunks || witnessScriptChunks.length === 0) return false; - - const witnessRawScriptSig = bscript.compile(chunks.slice(0, -1)); - - // match types - if ( - p2pkh.input.check(witnessRawScriptSig) && - p2pkh.output.check(witnessScriptChunks) - ) - return true; - - if ( - p2ms.input.check(witnessRawScriptSig, allowIncomplete) && - p2ms.output.check(witnessScriptChunks) - ) - return true; - - if ( - p2pk.input.check(witnessRawScriptSig) && - p2pk.output.check(witnessScriptChunks) - ) - return true; - - return false; -} -check.toJSON = (): string => { - return 'witnessScriptHash input'; -}; diff --git a/ts_src/templates/witnessscripthash/output.ts b/ts_src/templates/witnessscripthash/output.ts deleted file mode 100644 index 7d4f33a..0000000 --- a/ts_src/templates/witnessscripthash/output.ts +++ /dev/null @@ -1,13 +0,0 @@ -// OP_0 {scriptHash} - -import * as bscript from '../../script'; -import { OPS } from '../../script'; - -export function check(script: Buffer | Array<number | Buffer>): boolean { - const buffer = bscript.compile(script); - - return buffer.length === 34 && buffer[0] === OPS.OP_0 && buffer[1] === 0x20; -} -check.toJSON = (): string => { - return 'Witness scriptHash output'; -}; diff --git a/types/classify.d.ts b/types/classify.d.ts deleted file mode 100644 index 6ea4754..0000000 --- a/types/classify.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -declare const types: { - P2MS: string; - NONSTANDARD: string; - NULLDATA: string; - P2PK: string; - P2PKH: string; - P2SH: string; - P2WPKH: string; - P2WSH: string; - WITNESS_COMMITMENT: string; -}; -declare function classifyOutput(script: Buffer): string; -declare function classifyInput(script: Buffer, allowIncomplete?: boolean): string; -declare function classifyWitness(script: Buffer[], allowIncomplete?: boolean): string; -export { classifyInput as input, classifyOutput as output, classifyWitness as witness, types, }; diff --git a/types/templates/multisig/index.d.ts b/types/templates/multisig/index.d.ts deleted file mode 100644 index f6288e2..0000000 --- a/types/templates/multisig/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as input from './input'; -import * as output from './output'; -export { input, output }; diff --git a/types/templates/multisig/input.d.ts b/types/templates/multisig/input.d.ts deleted file mode 100644 index 6d46515..0000000 --- a/types/templates/multisig/input.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Stack } from '../../payments'; -export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/multisig/output.d.ts b/types/templates/multisig/output.d.ts deleted file mode 100644 index 6d46515..0000000 --- a/types/templates/multisig/output.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Stack } from '../../payments'; -export declare function check(script: Buffer | Stack, allowIncomplete?: boolean): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/nulldata.d.ts b/types/templates/nulldata.d.ts deleted file mode 100644 index bf6497c..0000000 --- a/types/templates/nulldata.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export declare function check(script: Buffer | Array<number | Buffer>): boolean; -export declare namespace check { - var toJSON: () => string; -} -declare const output: { - check: typeof check; -}; -export { output }; diff --git a/types/templates/pubkey/index.d.ts b/types/templates/pubkey/index.d.ts deleted file mode 100644 index f6288e2..0000000 --- a/types/templates/pubkey/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as input from './input'; -import * as output from './output'; -export { input, output }; diff --git a/types/templates/pubkey/input.d.ts b/types/templates/pubkey/input.d.ts deleted file mode 100644 index 4b70d2d..0000000 --- a/types/templates/pubkey/input.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Stack } from '../../payments'; -export declare function check(script: Buffer | Stack): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/pubkey/output.d.ts b/types/templates/pubkey/output.d.ts deleted file mode 100644 index 4b70d2d..0000000 --- a/types/templates/pubkey/output.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Stack } from '../../payments'; -export declare function check(script: Buffer | Stack): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/pubkeyhash/index.d.ts b/types/templates/pubkeyhash/index.d.ts deleted file mode 100644 index f6288e2..0000000 --- a/types/templates/pubkeyhash/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as input from './input'; -import * as output from './output'; -export { input, output }; diff --git a/types/templates/pubkeyhash/input.d.ts b/types/templates/pubkeyhash/input.d.ts deleted file mode 100644 index 4b70d2d..0000000 --- a/types/templates/pubkeyhash/input.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Stack } from '../../payments'; -export declare function check(script: Buffer | Stack): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/pubkeyhash/output.d.ts b/types/templates/pubkeyhash/output.d.ts deleted file mode 100644 index d66d8ac..0000000 --- a/types/templates/pubkeyhash/output.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export declare function check(script: Buffer | Array<number | Buffer>): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/scripthash/index.d.ts b/types/templates/scripthash/index.d.ts deleted file mode 100644 index f6288e2..0000000 --- a/types/templates/scripthash/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as input from './input'; -import * as output from './output'; -export { input, output }; diff --git a/types/templates/scripthash/input.d.ts b/types/templates/scripthash/input.d.ts deleted file mode 100644 index 14bbd5b..0000000 --- a/types/templates/scripthash/input.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export declare function check(script: Buffer | Array<number | Buffer>, allowIncomplete?: boolean): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/scripthash/output.d.ts b/types/templates/scripthash/output.d.ts deleted file mode 100644 index d66d8ac..0000000 --- a/types/templates/scripthash/output.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export declare function check(script: Buffer | Array<number | Buffer>): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/witnesscommitment/index.d.ts b/types/templates/witnesscommitment/index.d.ts deleted file mode 100644 index c37ee7c..0000000 --- a/types/templates/witnesscommitment/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import * as output from './output'; -export { output }; diff --git a/types/templates/witnesscommitment/output.d.ts b/types/templates/witnesscommitment/output.d.ts deleted file mode 100644 index ec155a2..0000000 --- a/types/templates/witnesscommitment/output.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export declare function check(script: Buffer | Array<number | Buffer>): boolean; -export declare namespace check { - var toJSON: () => string; -} -export declare function encode(commitment: Buffer): Buffer; -export declare function decode(buffer: Buffer): Buffer; diff --git a/types/templates/witnesspubkeyhash/index.d.ts b/types/templates/witnesspubkeyhash/index.d.ts deleted file mode 100644 index f6288e2..0000000 --- a/types/templates/witnesspubkeyhash/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as input from './input'; -import * as output from './output'; -export { input, output }; diff --git a/types/templates/witnesspubkeyhash/input.d.ts b/types/templates/witnesspubkeyhash/input.d.ts deleted file mode 100644 index 4b70d2d..0000000 --- a/types/templates/witnesspubkeyhash/input.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Stack } from '../../payments'; -export declare function check(script: Buffer | Stack): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/witnesspubkeyhash/output.d.ts b/types/templates/witnesspubkeyhash/output.d.ts deleted file mode 100644 index d66d8ac..0000000 --- a/types/templates/witnesspubkeyhash/output.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export declare function check(script: Buffer | Array<number | Buffer>): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/witnessscripthash/index.d.ts b/types/templates/witnessscripthash/index.d.ts deleted file mode 100644 index f6288e2..0000000 --- a/types/templates/witnessscripthash/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as input from './input'; -import * as output from './output'; -export { input, output }; diff --git a/types/templates/witnessscripthash/input.d.ts b/types/templates/witnessscripthash/input.d.ts deleted file mode 100644 index 767df3a..0000000 --- a/types/templates/witnessscripthash/input.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export declare function check(chunks: Buffer[], allowIncomplete?: boolean): boolean; -export declare namespace check { - var toJSON: () => string; -} diff --git a/types/templates/witnessscripthash/output.d.ts b/types/templates/witnessscripthash/output.d.ts deleted file mode 100644 index d66d8ac..0000000 --- a/types/templates/witnessscripthash/output.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export declare function check(script: Buffer | Array<number | Buffer>): boolean; -export declare namespace check { - var toJSON: () => string; -} From b56273a391d8434b5e079b4bd6310916be8c979d Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Fri, 27 Nov 2020 12:09:04 +0200 Subject: [PATCH 538/568] #1477 move script.ts test data from templates.json to script.json; remove templates.json --- test/fixtures/script.json | 257 ++++++++++++++++++++++++++++++++++ test/fixtures/templates.json | 259 ----------------------------------- test/script.spec.ts | 3 +- 3 files changed, 258 insertions(+), 261 deletions(-) delete mode 100644 test/fixtures/templates.json diff --git a/test/fixtures/script.json b/test/fixtures/script.json index 5046757..545c8f3 100644 --- a/test/fixtures/script.json +++ b/test/fixtures/script.json @@ -175,6 +175,263 @@ "script": "6a24aa21a9ed4db4fb830efe3e804337413ffe8ad7393af301e0ec8e71b6e6f2b860a56f4dcd" } ], + "valid2": [ + { + "type": "pubkey", + "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "output": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 OP_CHECKSIG", + "input": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "inputHex": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "outputHex": "2102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1ac", + "inputStack": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" + ] + }, + { + "type": "pubkeyhash", + "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "output": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", + "input": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "inputHex": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28012102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "outputHex": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac", + "inputStack": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1" + ] + }, + { + "type": "multisig", + "pubKeys": [ + "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a" + ], + "signatures": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" + ], + "output": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", + "input": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "inputHex": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "outputHex": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", + "inputStack": [ + "", + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" + ] + }, + { + "type": "multisig", + "pubKeys": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", + "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34" + ], + "signatures": [ + "3045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01", + "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", + "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" + ], + "output": "OP_3 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_3 OP_CHECKMULTISIG", + "input": "OP_0 3045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01 3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901 3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", + "inputHex": "00483045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01473044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901483045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", + "outputHex": "53210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817982102b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f84834021024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a3453ae", + "inputStack": [ + "", + "3045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01", + "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", + "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" + ] + }, + { + "type": "scripthash", + "redeemScript": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", + "redeemScriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "input": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", + "output": "OP_HASH160 722ff0bc2c3f47b35c20df646c395594da24e90e OP_EQUAL", + "inputHex": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", + "outputHex": "a914722ff0bc2c3f47b35c20df646c395594da24e90e87", + "inputStack": [ + "", + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae" + ] + }, + { + "type": "witnesspubkeyhash", + "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", + "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "output": "OP_0 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", + "outputHex": "0014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", + "inputStack": [ + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1" + ] + }, + { + "type": "witnessscripthash", + "witnessScript": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", + "witnessData": [ + "", + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" + ], + "output": "OP_0 32447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b80", + "outputHex": "002032447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b80", + "inputStack": [ + "", + "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", + "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", + "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae" + ] + }, + { + "type": "nulldata", + "data": [ + "06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474" + ], + "output": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", + "outputHex": "6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474" + }, + { + "type": "nulldata", + "data": [ + "deadffffffffffffffffffffffffffffffffbeef", + "deadffffffffffffffffffffffffffffffffbeef" + ], + "output": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef deadffffffffffffffffffffffffffffffffbeef", + "outputHex": "6a14deadffffffffffffffffffffffffffffffffbeef14deadffffffffffffffffffffffffffffffffbeef" + }, + { + "type": "nulldata", + "data": [ + "deadffffffffffffffffffffffffffffffffbeef" + ], + "output": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef", + "outputHex": "6a14deadffffffffffffffffffffffffffffffffbeef" + }, + { + "type": "nonstandard", + "typeIncomplete": "multisig", + "pubKeys": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", + "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34" + ], + "signatures": [ + null, + "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", + "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" + ], + "input": "OP_0 OP_0 3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901 3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", + "inputHex": "0000473044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901483045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", + "inputStack": [ + "", + "", + "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", + "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" + ] + }, + { + "type": "nonstandard", + "typeIncomplete": "multisig", + "pubKeys": [ + "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", + "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34" + ], + "signatures": [ + null, + null, + null + ], + "input": "OP_0 OP_0 OP_0 OP_0", + "inputHex": "00000000", + "inputStack": [ + "", + "", + "", + "" + ] + }, + { + "type": "scripthash", + "redeemScript": "OP_2 0327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a49372 03251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f 02cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde47 OP_3 OP_CHECKMULTISIG", + "redeemScriptSig": "OP_0 304502210093efc26facedc5f51e304aa270a7b4f1a911b2d912c3674e5c6e2ad4ac7a410402201cf0b62c240461902f9f16d8a0bc3a210b7bfcd2c06523dd4b4b63be22e8525201", + "input": "OP_0 304502210093efc26facedc5f51e304aa270a7b4f1a911b2d912c3674e5c6e2ad4ac7a410402201cf0b62c240461902f9f16d8a0bc3a210b7bfcd2c06523dd4b4b63be22e8525201 52210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753ae", + "inputHex": "0048304502210093efc26facedc5f51e304aa270a7b4f1a911b2d912c3674e5c6e2ad4ac7a410402201cf0b62c240461902f9f16d8a0bc3a210b7bfcd2c06523dd4b4b63be22e85252014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753ae", + "output": "OP_HASH160 fcc42dd4aa770d75cb6796bbd7853a325e746659 OP_EQUAL", + "outputHex": "a914fcc42dd4aa770d75cb6796bbd7853a325e74665987" + }, + { + "type": "nonstandard", + "typeIncomplete": "scripthash", + "pubKeys": [ + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a" + ], + "signatures": [ + null, + "30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801" + ], + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", + "redeemScriptSig": "OP_0 OP_0 30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801", + "input": "OP_0 OP_0 30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae", + "inputHex": "00004830450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a8014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae", + "inputStack": [ + "", + "", + "30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801", + "52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" + ] + }, + { + "type": "nonstandard", + "output": "OP_HASH256 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 OP_EQUAL", + "outputHex": "aa206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000087" + }, + { + "type": "nonstandard", + "output": "OP_0 OP_0 OP_0 OP_CHECKMULTISIG", + "outputHex": "000000ae" + }, + { + "type": "nonstandard", + "output": "OP_0", + "outputHex": "00" + }, + { + "type": "nonstandard", + "input": "OP_0 00", + "inputHex": "000100", + "inputStack": [ + "", + "00" + ] + }, + { + "type": "nonstandard", + "input": "OP_6", + "inputHex": "56", + "nonstandard": { + "input": "06", + "inputHex": "0106" + }, + "inputStack": [ + "06" + ] + }, + { + "type": "nonstandard", + "input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "inputHex": "4cff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "inputStack": [ + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ] + } + ], "invalid": { "decompile": [ { diff --git a/test/fixtures/templates.json b/test/fixtures/templates.json deleted file mode 100644 index 848d264..0000000 --- a/test/fixtures/templates.json +++ /dev/null @@ -1,259 +0,0 @@ -{ - "valid": [ - { - "type": "pubkey", - "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "output": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 OP_CHECKSIG", - "input": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "inputHex": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "outputHex": "2102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1ac", - "inputStack": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" - ] - }, - { - "type": "pubkeyhash", - "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "output": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", - "input": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "inputHex": "47304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca28012102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "outputHex": "76a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac", - "inputStack": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1" - ] - }, - { - "type": "multisig", - "pubKeys": [ - "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a" - ], - "signatures": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" - ], - "output": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", - "input": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", - "inputHex": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", - "outputHex": "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", - "inputStack": [ - "", - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" - ] - }, - { - "type": "multisig", - "pubKeys": [ - "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", - "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34" - ], - "signatures": [ - "3045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01", - "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", - "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" - ], - "output": "OP_3 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34 OP_3 OP_CHECKMULTISIG", - "input": "OP_0 3045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01 3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901 3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", - "inputHex": "00483045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01473044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901483045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", - "outputHex": "53210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817982102b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f84834021024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a3453ae", - "inputStack": [ - "", - "3045022100fe324541215798b2df68cbd44039615e23c506d4ec1a05572064392a98196b82022068c849fa6699206da2fc6d7848efc1d3804a5816d6293615fe34c1a7f34e1c2f01", - "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", - "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" - ] - }, - { - "type": "scripthash", - "redeemScript": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", - "redeemScriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", - "input": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", - "output": "OP_HASH160 722ff0bc2c3f47b35c20df646c395594da24e90e OP_EQUAL", - "inputHex": "0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae", - "outputHex": "a914722ff0bc2c3f47b35c20df646c395594da24e90e87", - "inputStack": [ - "", - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", - "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae" - ] - }, - { - "type": "witnesspubkeyhash", - "pubKey": "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "signature": "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "output": "OP_0 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", - "outputHex": "0014aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5", - "inputStack": [ - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1" - ] - }, - { - "type": "witnessscripthash", - "witnessScript": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", - "witnessData": [ - "", - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501" - ], - "output": "OP_0 32447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b80", - "outputHex": "002032447752937d355ca2defddcd1f6b4fc53d182f8901cebbcff42f5e381bf0b80", - "inputStack": [ - "", - "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", - "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", - "522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae" - ] - }, - { - "type": "nulldata", - "data": [ - "06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474" - ], - "output": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", - "outputHex": "6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474" - }, - { - "type": "nulldata", - "data": [ - "deadffffffffffffffffffffffffffffffffbeef", - "deadffffffffffffffffffffffffffffffffbeef" - ], - "output": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef deadffffffffffffffffffffffffffffffffbeef", - "outputHex": "6a14deadffffffffffffffffffffffffffffffffbeef14deadffffffffffffffffffffffffffffffffbeef" - }, - { - "type": "nulldata", - "data": [ - "deadffffffffffffffffffffffffffffffffbeef" - ], - "output": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef", - "outputHex": "6a14deadffffffffffffffffffffffffffffffffbeef" - }, - { - "type": "nonstandard", - "typeIncomplete": "multisig", - "pubKeys": [ - "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", - "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34" - ], - "signatures": [ - null, - "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", - "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" - ], - "input": "OP_0 OP_0 3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901 3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", - "inputHex": "0000473044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901483045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", - "inputStack": [ - "", - "", - "3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901", - "3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01" - ] - }, - { - "type": "nonstandard", - "typeIncomplete": "multisig", - "pubKeys": [ - "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", - "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34" - ], - "signatures": [ - null, - null, - null - ], - "input": "OP_0 OP_0 OP_0 OP_0", - "inputHex": "00000000", - "inputStack": [ - "", - "", - "", - "" - ] - }, - { - "type": "scripthash", - "redeemScript": "OP_2 0327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a49372 03251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f 02cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde47 OP_3 OP_CHECKMULTISIG", - "redeemScriptSig": "OP_0 304502210093efc26facedc5f51e304aa270a7b4f1a911b2d912c3674e5c6e2ad4ac7a410402201cf0b62c240461902f9f16d8a0bc3a210b7bfcd2c06523dd4b4b63be22e8525201", - "input": "OP_0 304502210093efc26facedc5f51e304aa270a7b4f1a911b2d912c3674e5c6e2ad4ac7a410402201cf0b62c240461902f9f16d8a0bc3a210b7bfcd2c06523dd4b4b63be22e8525201 52210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753ae", - "inputHex": "0048304502210093efc26facedc5f51e304aa270a7b4f1a911b2d912c3674e5c6e2ad4ac7a410402201cf0b62c240461902f9f16d8a0bc3a210b7bfcd2c06523dd4b4b63be22e85252014c6952210327e023a353d111808f61d554c2e1934721eaf87f33b7a771e807006908a493722103251670bb6a179a0d43b75476c7e580c0ba274378a18077e8de0832c870e5381f2102cca7f9a64425a0466d26d5c7e9eb3ad6b64cd48ea89edb38bc08f58a792dde4753ae", - "output": "OP_HASH160 fcc42dd4aa770d75cb6796bbd7853a325e746659 OP_EQUAL", - "outputHex": "a914fcc42dd4aa770d75cb6796bbd7853a325e74665987" - }, - { - "type": "nonstandard", - "typeIncomplete": "scripthash", - "pubKeys": [ - "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - "04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a" - ], - "signatures": [ - null, - "30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801" - ], - "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "redeemScriptSig": "OP_0 OP_0 30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801", - "input": "OP_0 OP_0 30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae", - "inputHex": "00004830450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a8014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae", - "inputStack": [ - "", - "", - "30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801", - "52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" - ] - }, - { - "type": "nonstandard", - "output": "OP_HASH256 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 OP_EQUAL", - "outputHex": "aa206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000087" - }, - { - "type": "nonstandard", - "output": "OP_0 OP_0 OP_0 OP_CHECKMULTISIG", - "outputHex": "000000ae" - }, - { - "type": "nonstandard", - "output": "OP_0", - "outputHex": "00" - }, - { - "type": "nonstandard", - "input": "OP_0 00", - "inputHex": "000100", - "inputStack": [ - "", - "00" - ] - }, - { - "type": "nonstandard", - "input": "OP_6", - "inputHex": "56", - "nonstandard": { - "input": "06", - "inputHex": "0106" - }, - "inputStack": [ - "06" - ] - }, - { - "type": "nonstandard", - "input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "inputHex": "4cff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "inputStack": [ - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ] - } - ] -} diff --git a/test/script.spec.ts b/test/script.spec.ts index fe5fa63..d593ab1 100644 --- a/test/script.spec.ts +++ b/test/script.spec.ts @@ -2,7 +2,6 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; import * as bscript from '../src/script'; import * as fixtures from './fixtures/script.json'; -import * as fixtures2 from './fixtures/templates.json'; const minimalData = require('minimaldata'); describe('script', () => { @@ -57,7 +56,7 @@ describe('script', () => { }); describe('fromASM/toASM (templates)', () => { - fixtures2.valid.forEach(f => { + fixtures.valid2.forEach(f => { if (f.inputHex) { const ih = bscript.toASM(Buffer.from(f.inputHex, 'hex')); From 14cfb558842b7e2a6f26f3fedb823e5c30568dde Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Wed, 2 Dec 2020 11:40:29 +0200 Subject: [PATCH 539/568] #1470 update tiny-secp256k1 to v1.1.6 --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 829bfd7..39cde2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2246,9 +2246,9 @@ } }, "tiny-secp256k1": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.5.tgz", - "integrity": "sha512-duE2hSLSQIpHGzmK48OgRrGTi+4OTkXLC6aa86uOYQ6LLCYZSarVKIAvEtY7MoXjoL6bOXMSerEGMzrvW4SkDw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", + "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", "requires": { "bindings": "^1.3.0", "bn.js": "^4.11.8", diff --git a/package.json b/package.json index c10870f..8eb7783 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "merkle-lib": "^2.0.10", "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", - "tiny-secp256k1": "^1.1.5", + "tiny-secp256k1": "^1.1.6", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.0.4", "wif": "^2.0.1" From ec61b996ad3c783208fe200275660066d70d7248 Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Wed, 2 Dec 2020 11:54:32 +0200 Subject: [PATCH 540/568] #1477 do not remove comments refering TransactionBuilder, but instead mention that TransactionBuilder was removed --- ts_src/psbt.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d5eb9f9..6c56132 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -117,6 +117,8 @@ export class Psbt { __NON_WITNESS_UTXO_BUF_CACHE: [], __TX_IN_CACHE: {}, __TX: (this.data.globalMap.unsignedTx as PsbtTransaction).tx, + // Psbt's predecesor (TransactionBuilder - now removed) behavior + // was to not confirm input values before signing. // Even though we highly encourage people to get // the full parent transaction to verify values, the ability to // sign non-segwit inputs without the full transaction was often @@ -1294,7 +1296,8 @@ function getHashForSig( console.warn( 'Warning: Signing non-segwit inputs without the full parent transaction ' + 'means there is a chance that a miner could feed you incorrect information ' + - 'to trick you into paying large fees. You are not ' + + 'to trick you into paying large fees. This behavior is the same as Psbt\'s predecesor ' + + '(TransactionBuilder - now removed) when signing non-segwit scripts. You are not ' + 'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' + 'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' + '*********************', From 34412d3e628fb568ee1e65b7802d374f6232417f Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Mon, 1 Feb 2021 12:28:54 +0200 Subject: [PATCH 541/568] #1654 add link to Web UI in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0d6f2a0..6327d47 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Mistakes and bugs happen, but with your help in resolving and reporting [issues] ## Documentation Presently, we do not have any formal documentation other than our [examples](#examples), please [ask for help](https://github.com/bitcoinjs/bitcoinjs-lib/issues/new) if our examples aren't enough to guide you. +You can find a [Web UI](https://bitcoincore.tech/apps/bitcoinjs-ui/index.html) that covers most of the `psbt.ts`, `transaction.ts` and `p2*.ts` APIs [here](https://bitcoincore.tech/apps/bitcoinjs-ui/index.html). ## Installation ``` bash From f5217e3acb82bea47b484cac47f12dbdcf4ba050 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 4 Feb 2021 11:52:49 +0900 Subject: [PATCH 542/568] Use Github Actions for CI --- .github/workflows/main_ci.yml | 80 +++++++++++++++++++++++++++++++++++ .travis.yml | 29 ------------- 2 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/main_ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml new file mode 100644 index 0000000..bf67c58 --- /dev/null +++ b/.github/workflows/main_ci.yml @@ -0,0 +1,80 @@ +name: Run Tests + +on: [push] + +jobs: + unit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm run unit + coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm run coverage + integration: + runs-on: ubuntu-latest + services: + regtest: + image: junderw/bitcoinjs-regtest-server@sha256:a46ec1a651ca5b1a5408f2b2526ea5f435421dd2bc2f28fae3bc33e1fd614552 + ports: + - 8080:8080 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: APIURL=http://127.0.0.1:8080/1 npm run integration + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm run format:ci + gitdiff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm run gitdiff:ci + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm run lint + lint-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm run lint:tests diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f7aad0e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -sudo: false -language: node_js -services: - - docker -before_install: - - if [ $TEST_SUITE = "integration" ]; then - docker pull junderw/bitcoinjs-regtest-server && - docker run -d -p 127.0.0.1:8080:8080 junderw/bitcoinjs-regtest-server && - docker ps -a; - fi -node_js: - - "8" - - "lts/*" -matrix: - include: - - node_js: "lts/*" - env: TEST_SUITE=format:ci - - node_js: "lts/*" - env: TEST_SUITE=gitdiff:ci - - node_js: "lts/*" - env: TEST_SUITE=lint - - node_js: "lts/*" - env: TEST_SUITE=lint:tests - - node_js: "lts/*" - env: TEST_SUITE=coverage -env: - - TEST_SUITE=unit - - TEST_SUITE=integration APIURL=http://127.0.0.1:8080/1 -script: npm run-script $TEST_SUITE From 79e65eeb39a89cfb85476ca45576cc2dd04adf44 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 5 Feb 2021 14:16:17 +0900 Subject: [PATCH 543/568] Add npm audit CI job --- .github/workflows/main_ci.yml | 10 + .npm-audit-whitelister.json | 1 + package-lock.json | 458 ++++++++++++++++++++++------------ package.json | 4 +- 4 files changed, 319 insertions(+), 154 deletions(-) create mode 100644 .npm-audit-whitelister.json diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml index bf67c58..85ea8f8 100644 --- a/.github/workflows/main_ci.yml +++ b/.github/workflows/main_ci.yml @@ -3,6 +3,16 @@ name: Run Tests on: [push] jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm run audit unit: runs-on: ubuntu-latest steps: diff --git a/.npm-audit-whitelister.json b/.npm-audit-whitelister.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/.npm-audit-whitelister.json @@ -0,0 +1 @@ +[] diff --git a/package-lock.json b/package-lock.json index 39cde2f..2567f97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,89 +14,185 @@ } }, "@babel/core": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", - "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.13.tgz", + "integrity": "sha512-BQKE9kXkPlXHPeqissfxo0lySWJcYdEP0hdtJOH/iJfDdhOCcgtNCjftCJg3qqauB4h+lz2N6ixM++b9DN1Tcw==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.7", - "@babel/helpers": "^7.8.4", - "@babel/parser": "^7.8.7", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.8.6", - "@babel/types": "^7.8.7", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-module-transforms": "^7.12.13", + "@babel/helpers": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", + "json5": "^2.1.2", + "lodash": "^4.17.19", "semver": "^5.4.1", "source-map": "^0.5.0" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "ms": "^2.1.1" + "@babel/highlight": "^7.12.13" } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true } } }, "@babel/generator": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.8.tgz", - "integrity": "sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.15.tgz", + "integrity": "sha512-6F2xHxBiFXWNSGb7vyCUTBF8RCLY66rS0zEPcP8t/nQyXjha5EuK4z7H5o7fWG8B4M7y6mqVWq1J+1PuwRhecQ==", "dev": true, "requires": { - "@babel/types": "^7.8.7", + "@babel/types": "^7.12.13", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.13.tgz", + "integrity": "sha512-B+7nN0gIL8FZ8SvMcF+EPyB21KnCcZHQZFczCxbiNGV/O0rsrSBlWGLzmtBJ3GMjSVMIm4lpFhR+VdVBuIsUcQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.13.tgz", + "integrity": "sha512-acKF7EjqOR67ASIlDTupwkKM1eUisNAjaSduo5Cz+793ikfnpe7p4Q7B7EWU2PCoSTPWsQkR7hRUWEIZPiVLGA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-replace-supers": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.13.tgz", + "integrity": "sha512-pctAOIAMVStI2TMLhozPKbf5yTEXc0OJa0eENheb4w09SrgOWEs+P4nTOZYJQCqs8JlErGLDPDJTiGIp3ygbLg==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" } }, "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.13" } }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, "@babel/helpers": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", - "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.13.tgz", + "integrity": "sha512-oohVzLRZ3GQEk4Cjhfs9YkJA4TdIDTObdBEZGrd6F/T0GPSnuV6l22eMcxlvcvzVIPH3VTtxbseudM1zIE+rPQ==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.4", - "@babel/types": "^7.8.3" + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/highlight": { @@ -111,69 +207,118 @@ } }, "@babel/parser": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.8.tgz", - "integrity": "sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==", + "version": "7.12.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.15.tgz", + "integrity": "sha512-AQBOU2Z9kWwSZMd6lNjCX0GUgFonL1wAM1db8L8PMk9UDaGsRCArBkU4Sc+UCM3AE4hjbXx+h58Lb3QT4oRmrA==", "dev": true }, "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/traverse": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", - "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.6", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "dev": true, "requires": { - "ms": "^2.1.1" + "@babel/highlight": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" } } } }, - "@babel/types": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", - "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "@babel/traverse": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.13.tgz", + "integrity": "sha512-3Zb4w7eE/OslI0fTp8c7b286/cQps3+vdLW3UcwC8VSJC6GbKn55aeVVu2QJNuCDoeKyptLOFrPq8WqZZBodyA==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.13.tgz", + "integrity": "sha512-oKrdZTld2im1z8bDwTOQvUbxKwE+854zc16qWZQlcTqMN00pWxHQ4ZeOq0yDMnisOpRykH2/5Qqcrk/OlbAjiQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } }, "@istanbuljs/load-nyc-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", - "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "requires": { "camelcase": "^5.3.1", "find-up": "^4.1.0", + "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" }, @@ -238,12 +383,6 @@ "@types/base-x": "*" } }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, "@types/mocha": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", @@ -263,9 +402,9 @@ "dev": true }, "aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", @@ -680,9 +819,9 @@ } }, "cross-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", - "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -887,9 +1026,9 @@ } }, "fromentries": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", - "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true }, "fs.realpath": { @@ -912,9 +1051,9 @@ "dev": true }, "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { @@ -923,6 +1062,12 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -953,9 +1098,9 @@ "dev": true }, "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, "growl": { @@ -1004,9 +1149,9 @@ } }, "hasha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", - "integrity": "sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, "requires": { "is-stream": "^2.0.0", @@ -1036,9 +1181,9 @@ "dev": true }, "html-escaper": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", - "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, "imurmurhash": { @@ -1186,15 +1331,12 @@ } }, "istanbul-lib-instrument": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", - "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "requires": { "@babel/core": "^7.7.5", - "@babel/parser": "^7.7.5", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" @@ -1252,9 +1394,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -1274,14 +1416,20 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1291,9 +1439,9 @@ } }, "istanbul-reports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.0.tgz", - "integrity": "sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -1323,9 +1471,9 @@ "dev": true }, "json5": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz", - "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -1363,9 +1511,9 @@ } }, "make-dir": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", - "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { "semver": "^6.0.0" @@ -1524,10 +1672,16 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "npm-audit-whitelister": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/npm-audit-whitelister/-/npm-audit-whitelister-1.0.2.tgz", + "integrity": "sha512-MNaYMUPI4P1cGcnLNvMv0XW4F5NkVEJv2aAfLqXXKY4cgo5lXCHl1h9eUIQnWLKM3WHVOqKzUipMzfunzQZXUg==", + "dev": true + }, "nyc": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.0.tgz", - "integrity": "sha512-qcLBlNCKMDVuKb7d1fpxjPR8sHeMVX0CHarXAVzrVWoFrigCkYR8xcrjfXSPi5HXM7EU78L6ywO7w1c5rZNCNg==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", "dev": true, "requires": { "@istanbuljs/load-nyc-config": "^1.0.0", @@ -1538,6 +1692,7 @@ "find-cache-dir": "^3.2.0", "find-up": "^4.1.0", "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", "glob": "^7.1.6", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-hook": "^3.0.0", @@ -1545,10 +1700,9 @@ "istanbul-lib-processinfo": "^2.0.2", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.0", - "js-yaml": "^3.13.1", + "istanbul-reports": "^3.0.2", "make-dir": "^3.0.0", - "node-preload": "^0.2.0", + "node-preload": "^0.2.1", "p-map": "^3.0.0", "process-on-spawn": "^1.0.0", "resolve-from": "^5.0.0", @@ -1556,7 +1710,6 @@ "signal-exit": "^3.0.2", "spawn-wrap": "^2.0.0", "test-exclude": "^6.0.0", - "uuid": "^3.3.3", "yargs": "^15.0.2" }, "dependencies": { @@ -1567,12 +1720,11 @@ "dev": true }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -1703,9 +1855,9 @@ } }, "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -1718,13 +1870,13 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" + "yargs-parser": "^18.1.2" } }, "yargs-parser": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.1.tgz", - "integrity": "sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -2083,9 +2235,9 @@ "dev": true }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, "source-map": { diff --git a/package.json b/package.json index 8eb7783..d83f765 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "bitcoinjs" ], "scripts": { + "audit": "NPM_AUDIT_IGNORE_DEV=1 NPM_AUDIT_IGNORE_LEVEL=low npm-audit-whitelister .npm-audit-whitelister.json", "build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs", "build:tests": "npm run clean:jstests && tsc -p ./test/tsconfig.json", "clean": "rimraf src types", @@ -79,7 +80,8 @@ "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^7.1.1", - "nyc": "^15.0.0", + "npm-audit-whitelister": "^1.0.2", + "nyc": "^15.1.0", "prettier": "1.16.4", "proxyquire": "^2.0.1", "regtest-client": "0.2.0", From 40e73b489859f47948cbeec477998aa8a308856b Mon Sep 17 00:00:00 2001 From: Andrew Toth <andrewstoth@gmail.com> Date: Mon, 15 Feb 2021 14:47:17 -0500 Subject: [PATCH 544/568] Bump bech32 to v2.0.0 --- README.md | 2 +- package-lock.json | 6 +++--- package.json | 2 +- src/address.js | 2 +- src/payments/p2wpkh.js | 2 +- src/payments/p2wsh.js | 2 +- ts_src/address.ts | 2 +- ts_src/payments/p2wpkh.ts | 2 +- ts_src/payments/p2wsh.ts | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6327d47..f54dab4 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ npm run-script coverage - [BIP69](https://github.com/bitcoinjs/bip69) - Lexicographical Indexing of Transaction Inputs and Outputs - [Base58](https://github.com/cryptocoinjs/bs58) - Base58 encoding/decoding - [Base58 Check](https://github.com/bitcoinjs/bs58check) - Base58 check encoding/decoding -- [Bech32](https://github.com/bitcoinjs/bech32) - A BIP173 compliant Bech32 encoding library +- [Bech32](https://github.com/bitcoinjs/bech32) - A BIP173/BIP350 compliant Bech32/Bech32m encoding library - [coinselect](https://github.com/bitcoinjs/coinselect) - A fee-optimizing, transaction input selection module for bitcoinjs-lib. - [merkle-lib](https://github.com/bitcoinjs/merkle-lib) - A performance conscious library for merkle root and tree calculations. - [minimaldata](https://github.com/bitcoinjs/minimaldata) - A module to check bitcoin policy: SCRIPT_VERIFY_MINIMALDATA diff --git a/package-lock.json b/package-lock.json index 39cde2f..ddafc4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -348,9 +348,9 @@ } }, "bech32": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.3.tgz", - "integrity": "sha512-yuVFUvrNcoJi0sv5phmqc6P+Fl1HjRDRNOOkHY2X/3LBy2bIGNSFx4fZ95HMaXHupuS7cZR15AsvtmCIF4UEyg==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, "binary-extensions": { "version": "2.0.0", diff --git a/package.json b/package.json index 8eb7783..9d19a3e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "types" ], "dependencies": { - "bech32": "^1.1.2", + "bech32": "^2.0.0", "bip174": "^2.0.1", "bip32": "^2.0.4", "bip66": "^1.1.0", diff --git a/src/address.js b/src/address.js index e15c55e..677c43f 100644 --- a/src/address.js +++ b/src/address.js @@ -4,7 +4,7 @@ const networks = require('./networks'); const payments = require('./payments'); const bscript = require('./script'); const types = require('./types'); -const bech32 = require('bech32'); +const { bech32 } = require('bech32'); const bs58check = require('bs58check'); const typeforce = require('typeforce'); function fromBase58Check(address) { diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index 0ba4a51..9e9e685 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -7,7 +7,7 @@ const lazy = require('./lazy'); const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); -const bech32 = require('bech32'); +const { bech32 } = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); // witness: {signature} {pubKey} // input: <> diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index e100c6d..20432a9 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -7,7 +7,7 @@ const lazy = require('./lazy'); const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); -const bech32 = require('bech32'); +const { bech32 } = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); function stacksEqual(a, b) { if (a.length !== b.length) return false; diff --git a/ts_src/address.ts b/ts_src/address.ts index ad791ad..577ea92 100644 --- a/ts_src/address.ts +++ b/ts_src/address.ts @@ -4,7 +4,7 @@ import * as payments from './payments'; import * as bscript from './script'; import * as types from './types'; -const bech32 = require('bech32'); +const { bech32 } = require('bech32'); const bs58check = require('bs58check'); const typeforce = require('typeforce'); diff --git a/ts_src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts index 27c61c9..3d8f86f 100644 --- a/ts_src/payments/p2wpkh.ts +++ b/ts_src/payments/p2wpkh.ts @@ -7,7 +7,7 @@ const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); -const bech32 = require('bech32'); +const { bech32 } = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index 6653448..e28cd57 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -7,7 +7,7 @@ const typef = require('typeforce'); const OPS = bscript.OPS; const ecc = require('tiny-secp256k1'); -const bech32 = require('bech32'); +const { bech32 } = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); From 2f7c83b286e0a58962df38e606c516983903e1a0 Mon Sep 17 00:00:00 2001 From: Andrew Toth <andrewstoth@gmail.com> Date: Tue, 16 Feb 2021 16:48:05 -0500 Subject: [PATCH 545/568] Support address de/serialization from/to future bech32m outputs --- src/address.js | 58 ++++++++++++++++++++++++++++++++++++---- ts_src/address.ts | 67 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 115 insertions(+), 10 deletions(-) diff --git a/src/address.js b/src/address.js index 677c43f..1709c37 100644 --- a/src/address.js +++ b/src/address.js @@ -4,9 +4,31 @@ const networks = require('./networks'); const payments = require('./payments'); const bscript = require('./script'); const types = require('./types'); -const { bech32 } = require('bech32'); +const { bech32, bech32m } = require('bech32'); const bs58check = require('bs58check'); const typeforce = require('typeforce'); +const FUTURE_SEGWIT_MAX_SIZE = 40; +const FUTURE_SEGWIT_MIN_SIZE = 2; +const FUTURE_SEGWIT_MAX_VERSION = 16; +const FUTURE_SEGWIT_MIN_VERSION = 1; +const FUTURE_SEGWIT_VERSION_DIFF = 0x50; +function _toFutureSegwitAddress(output, network) { + const data = output.slice(2); + if ( + data.length < FUTURE_SEGWIT_MIN_SIZE || + data.length > FUTURE_SEGWIT_MAX_SIZE + ) + throw new TypeError('Invalid program length for segwit address'); + const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF; + if ( + version < FUTURE_SEGWIT_MIN_VERSION || + version > FUTURE_SEGWIT_MAX_VERSION + ) + throw new TypeError('Invalid version for segwit address'); + if (output[1] !== data.length) + throw new TypeError('Invalid script for segwit address'); + return toBech32(data, version, network.bech32); +} function fromBase58Check(address) { const payload = bs58check.decode(address); // TODO: 4.0.0, move to "toOutputScript" @@ -18,10 +40,22 @@ function fromBase58Check(address) { } exports.fromBase58Check = fromBase58Check; function fromBech32(address) { - const result = bech32.decode(address); + let result; + let version; + try { + result = bech32.decode(address); + } catch (e) {} + if (result) { + version = result.words[0]; + if (version !== 0) throw new TypeError(address + ' uses wrong encoding'); + } else { + result = bech32m.decode(address); + version = result.words[0]; + if (version === 0) throw new TypeError(address + ' uses wrong encoding'); + } const data = bech32.fromWords(result.words.slice(1)); return { - version: result.words[0], + version, prefix: result.prefix, data: Buffer.from(data), }; @@ -38,7 +72,9 @@ exports.toBase58Check = toBase58Check; function toBech32(data, version, prefix) { const words = bech32.toWords(data); words.unshift(version); - return bech32.encode(prefix, words); + return version === 0 + ? bech32.encode(prefix, words) + : bech32m.encode(prefix, words); } exports.toBech32 = toBech32; function fromOutputScript(output, network) { @@ -56,6 +92,9 @@ function fromOutputScript(output, network) { try { return payments.p2wsh({ output, network }).address; } catch (e) {} + try { + return _toFutureSegwitAddress(output, network); + } catch (e) {} throw new Error(bscript.toASM(output) + ' has no matching Address'); } exports.fromOutputScript = fromOutputScript; @@ -83,7 +122,16 @@ function toOutputScript(address, network) { return payments.p2wpkh({ hash: decodeBech32.data }).output; if (decodeBech32.data.length === 32) return payments.p2wsh({ hash: decodeBech32.data }).output; - } + } else if ( + decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION && + decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION && + decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE && + decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE + ) + return bscript.compile([ + decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF, + decodeBech32.data, + ]); } } throw new Error(address + ' has no matching Script'); diff --git a/ts_src/address.ts b/ts_src/address.ts index 577ea92..11be51b 100644 --- a/ts_src/address.ts +++ b/ts_src/address.ts @@ -4,7 +4,7 @@ import * as payments from './payments'; import * as bscript from './script'; import * as types from './types'; -const { bech32 } = require('bech32'); +const { bech32, bech32m } = require('bech32'); const bs58check = require('bs58check'); const typeforce = require('typeforce'); @@ -19,6 +19,35 @@ export interface Bech32Result { data: Buffer; } +const FUTURE_SEGWIT_MAX_SIZE: number = 40; +const FUTURE_SEGWIT_MIN_SIZE: number = 2; +const FUTURE_SEGWIT_MAX_VERSION: number = 16; +const FUTURE_SEGWIT_MIN_VERSION: number = 1; +const FUTURE_SEGWIT_VERSION_DIFF: number = 0x50; + +function _toFutureSegwitAddress(output: Buffer, network: Network): string { + const data = output.slice(2); + + if ( + data.length < FUTURE_SEGWIT_MIN_SIZE || + data.length > FUTURE_SEGWIT_MAX_SIZE + ) + throw new TypeError('Invalid program length for segwit address'); + + const version = output[0] - FUTURE_SEGWIT_VERSION_DIFF; + + if ( + version < FUTURE_SEGWIT_MIN_VERSION || + version > FUTURE_SEGWIT_MAX_VERSION + ) + throw new TypeError('Invalid version for segwit address'); + + if (output[1] !== data.length) + throw new TypeError('Invalid script for segwit address'); + + return toBech32(data, version, network.bech32); +} + export function fromBase58Check(address: string): Base58CheckResult { const payload = bs58check.decode(address); @@ -33,11 +62,25 @@ export function fromBase58Check(address: string): Base58CheckResult { } export function fromBech32(address: string): Bech32Result { - const result = bech32.decode(address); + let result; + let version; + try { + result = bech32.decode(address); + } catch (e) {} + + if (result) { + version = result.words[0]; + if (version !== 0) throw new TypeError(address + ' uses wrong encoding'); + } else { + result = bech32m.decode(address); + version = result.words[0]; + if (version === 0) throw new TypeError(address + ' uses wrong encoding'); + } + const data = bech32.fromWords(result.words.slice(1)); return { - version: result.words[0], + version, prefix: result.prefix, data: Buffer.from(data), }; @@ -61,7 +104,9 @@ export function toBech32( const words = bech32.toWords(data); words.unshift(version); - return bech32.encode(prefix, words); + return version === 0 + ? bech32.encode(prefix, words) + : bech32m.encode(prefix, words); } export function fromOutputScript(output: Buffer, network?: Network): string { @@ -80,6 +125,9 @@ export function fromOutputScript(output: Buffer, network?: Network): string { try { return payments.p2wsh({ output, network }).address as string; } catch (e) {} + try { + return _toFutureSegwitAddress(output, network); + } catch (e) {} throw new Error(bscript.toASM(output) + ' has no matching Address'); } @@ -111,7 +159,16 @@ export function toOutputScript(address: string, network?: Network): Buffer { return payments.p2wpkh({ hash: decodeBech32.data }).output as Buffer; if (decodeBech32.data.length === 32) return payments.p2wsh({ hash: decodeBech32.data }).output as Buffer; - } + } else if ( + decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION && + decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION && + decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE && + decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE + ) + return bscript.compile([ + decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF, + decodeBech32.data, + ]); } } From a75f32f7f9ef3bc91923ccd77f3fb05113b0e3d1 Mon Sep 17 00:00:00 2001 From: Andrew Toth <andrewstoth@gmail.com> Date: Tue, 16 Feb 2021 16:48:18 -0500 Subject: [PATCH 546/568] Update test fixtures for bech32m address de/serialization --- test/fixtures/address.json | 136 ++++++++++++++++++++++++++++++++----- 1 file changed, 118 insertions(+), 18 deletions(-) diff --git a/test/fixtures/address.json b/test/fixtures/address.json index 1b428d9..765ea8a 100644 --- a/test/fixtures/address.json +++ b/test/fixtures/address.json @@ -77,23 +77,58 @@ "bech32": "bcrt1qqqqqqqqqqqqqqahrwf6d62emdxmpq8gu3xe9au9fjwc9sxxn4k2qujfh7u", "data": "000000000000000076e37274dd2b3b69b6101d1c89b25ef0a993b05818d3ad94", "script": "OP_0 000000000000000076e37274dd2b3b69b6101d1c89b25ef0a993b05818d3ad94" + }, + { + "network": "bitcoin", + "bech32": "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", + "version": 1, + "data": "751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", + "script": "OP_1 751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6" + }, + { + "network": "bitcoin", + "bech32": "BC1SW50QGDZ25J", + "version": 16, + "data": "751e", + "script": "OP_16 751e" + }, + { + "network": "bitcoin", + "bech32": "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", + "version": 2, + "data": "751e76e8199196d454941c45d1b3a323", + "script": "OP_2 751e76e8199196d454941c45d1b3a323" + }, + { + "network": "testnet", + "bech32": "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", + "version": 1, + "data": "000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + "script": "OP_1 000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433" + }, + { + "network": "bitcoin", + "bech32": "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", + "version": 1, + "data": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "script": "OP_1 79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" } ], "bech32": [ { - "address": "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", + "address": "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", "version": 1, "prefix": "bc", "data": "751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6" }, { - "address": "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", + "address": "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", "version": 2, "prefix": "bc", "data": "751e76e8199196d454941c45d1b3a323" }, { - "address": "BC1SW50QA3JX3S", + "address": "BC1SW50QGDZ25J", "version": 16, "prefix": "bc", "data": "751e" @@ -110,16 +145,24 @@ "exception": "Mixed-case string" }, { - "address": "tb1pw508d6qejxtdg4y5r3zarqfsj6c3", + "address": "tb1pw508d6qejxtdg4y5r3zarquvzkan", "exception": "Excess padding" }, { - "address": "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", + "address": "bc1zw508d6qejxtdg4y5r3zarvaryvq37eag7", "exception": "Excess padding" }, { "address": "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", "exception": "Non-zero padding" + }, + { + "address": "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", + "exception": "uses wrong encoding" + }, + { + "address": "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd", + "exception": "uses wrong encoding" } ], "fromBase58Check": [ @@ -161,7 +204,7 @@ }, { "exception": "has an invalid prefix", - "address": "BC1SW50QA3JX3S", + "address": "BC1SW50QGDZ25J", "network": { "bech32": "foo" } @@ -170,18 +213,6 @@ "exception": "has no matching Script", "address": "bc1rw5uspcuh" }, - { - "exception": "has no matching Script", - "address": "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx" - }, - { - "exception": "has no matching Script", - "address": "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj" - }, - { - "exception": "has no matching Script", - "address": "BC1SW50QA3JX3S" - }, { "exception": "has no matching Script", "address": "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90" @@ -197,6 +228,75 @@ { "exception": "has no matching Script", "address": "bc1qqqqqqqqqqv9qus" + }, + { + "address": "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut", + "exception": "has an invalid prefix" + }, + { + "address": "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd", + "exception": "has no matching Script" + }, + { + "address": "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf", + "exception": "has no matching Script", + "network": { + "bech32": "tb" + } + }, + { + "address": "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL", + "exception": "has no matching Script" + }, + { + "address": "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh", + "exception": "has no matching Script" + }, + { + "address": "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47", + "exception": "has no matching Script", + "network": { + "bech32": "tb" + } + }, + { + "address": "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4", + "exception": "has no matching Script" + }, + { + "address": "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R", + "exception": "has no matching Script" + }, + { + "address": "bc1pw5dgrnzv", + "exception": "has no matching Script" + }, + { + "address": "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav", + "exception": "has no matching Script" + }, + { + "address": "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", + "exception": "has no matching Script" + }, + { + "address": "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq", + "exception": "has no matching Script", + "network": { + "bech32": "tb" + } + }, + { + "address": "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf", + "exception": "has no matching Script" + }, + { + "address": "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j", + "exception": "has no matching Script" + }, + { + "address": "bc1gmk9yu", + "exception": "has no matching Script" } ] } From c7b3506f48d93d33980b43a0d25738ea4d3276bd Mon Sep 17 00:00:00 2001 From: Jonathan Underwood <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Feb 2021 21:00:57 +0900 Subject: [PATCH 547/568] Update main_ci.yml --- .github/workflows/main_ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml index bf67c58..55c4574 100644 --- a/.github/workflows/main_ci.yml +++ b/.github/workflows/main_ci.yml @@ -1,6 +1,10 @@ name: Run Tests -on: [push] +on: + push: + branches: + - master + pull_request: jobs: unit: From ef9f80906cffac3471ede1c118997cb51bb0e868 Mon Sep 17 00:00:00 2001 From: Jonathan Underwood <junderwood@bitcoinbank.co.jp> Date: Fri, 19 Feb 2021 21:03:58 +0900 Subject: [PATCH 548/568] Update main_ci.yml --- .github/workflows/main_ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml index bf67c58..55c4574 100644 --- a/.github/workflows/main_ci.yml +++ b/.github/workflows/main_ci.yml @@ -1,6 +1,10 @@ name: Run Tests -on: [push] +on: + push: + branches: + - master + pull_request: jobs: unit: From 397ef6d23a8a6ca922362c8a52831edc8e8a6020 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 07:41:11 +0000 Subject: [PATCH 549/568] Bump y18n from 4.0.0 to 4.0.3 Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.3. - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/y18n-v4.0.3/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/compare/v4.0.0...y18n-v4.0.3) Signed-off-by: dependabot[bot] <support@github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ddafc4f..e1008eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2467,9 +2467,9 @@ } }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs": { From 6b71eb0c65076715723dafc58318fad7e984cef5 Mon Sep 17 00:00:00 2001 From: Otto Allmendinger <otto@bitgo.com> Date: Tue, 17 Aug 2021 09:25:33 +0200 Subject: [PATCH 550/568] fix(transaction): use writeInt32 to write version We are reading `version` as `int32` so we should write it as that as well. --- src/transaction.js | 2 +- test/bitcoin.core.spec.ts | 10 ++++++++++ ts_src/transaction.ts | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index b47cfe9..8db57c3 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -316,7 +316,7 @@ class Transaction { tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)); bufferWriter = new bufferutils_1.BufferWriter(tbuffer, 0); const input = this.ins[inIndex]; - bufferWriter.writeUInt32(this.version); + bufferWriter.writeInt32(this.version); bufferWriter.writeSlice(hashPrevouts); bufferWriter.writeSlice(hashSequence); bufferWriter.writeSlice(input.hash); diff --git a/test/bitcoin.core.spec.ts b/test/bitcoin.core.spec.ts index 94b74e3..0263266 100644 --- a/test/bitcoin.core.spec.ts +++ b/test/bitcoin.core.spec.ts @@ -214,6 +214,16 @@ describe('Bitcoin-core', () => { (hash.reverse() as Buffer).toString('hex'), expectedHash, ); + + assert.doesNotThrow(() => + transaction.hashForWitnessV0( + inIndex, + script, + 0, + // convert to UInt32 + hashType < 0 ? 0x100000000 + hashType : hashType, + ), + ); }, ); }); diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index 561ee8a..b1ac302 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -416,7 +416,7 @@ export class Transaction { bufferWriter = new BufferWriter(tbuffer, 0); const input = this.ins[inIndex]; - bufferWriter.writeUInt32(this.version); + bufferWriter.writeInt32(this.version); bufferWriter.writeSlice(hashPrevouts); bufferWriter.writeSlice(hashSequence); bufferWriter.writeSlice(input.hash); From 58698e33daf2cf2ae2e897a39cecb9975f28230a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Sep 2021 02:08:06 +0000 Subject: [PATCH 551/568] Bump lodash from 4.17.19 to 4.17.21 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21) --- updated-dependencies: - dependency-name: lodash dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e1008eb..8f39a36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1342,9 +1342,9 @@ } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lodash.flattendeep": { From 4ee422655858d21f725a0595377b646d4fe20e52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Sep 2021 02:08:16 +0000 Subject: [PATCH 552/568] Bump glob-parent from 5.1.0 to 5.1.2 Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.0 to 5.1.2. - [Release notes](https://github.com/gulpjs/glob-parent/releases) - [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md) - [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.0...v5.1.2) --- updated-dependencies: - dependency-name: glob-parent dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e1008eb..5d543a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -938,9 +938,9 @@ } }, "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" From f08fa4810ea38373241e3d756cf09fa6a9af4e90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Sep 2021 02:08:16 +0000 Subject: [PATCH 553/568] Bump path-parse from 1.0.6 to 1.0.7 Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7. - [Release notes](https://github.com/jbgutierrez/path-parse/releases) - [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7) --- updated-dependencies: - dependency-name: path-parse dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e1008eb..b60cd72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1840,9 +1840,9 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "pbkdf2": { From e5a77304fa5f39f521163f2f4a68377181bb0839 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 20 Oct 2021 19:18:01 +0900 Subject: [PATCH 554/568] Refactor: Remove all require statements, remove ECPair, remove tiny-secp256k1 dep --- package-lock.json | 161 +++++++----- package.json | 27 +- {types => src}/address.d.ts | 1 + src/address.js | 17 +- src/bip66.d.ts | 7 + src/bip66.js | 102 ++++++++ {types => src}/block.d.ts | 1 + src/block.js | 40 +-- {types => src}/bufferutils.d.ts | 3 + src/bufferutils.js | 8 +- {types => src}/crypto.d.ts | 1 + src/crypto.js | 1 + src/ecpair.js | 107 -------- {types => src}/index.d.ts | 9 +- src/index.js | 33 ++- src/merkle.d.ts | 2 + src/merkle.js | 22 ++ {types => src}/networks.d.ts | 0 src/networks.js | 1 + src/ops.d.ts | 7 + src/ops.js | 130 ++++++++++ {types => src}/payments/embed.d.ts | 0 src/payments/embed.js | 15 +- {types => src}/payments/index.d.ts | 1 + src/payments/index.js | 50 +++- {types => src}/payments/lazy.d.ts | 0 src/payments/lazy.js | 1 + {types => src}/payments/p2ms.d.ts | 0 src/payments/p2ms.js | 31 ++- {types => src}/payments/p2pk.d.ts | 0 src/payments/p2pk.js | 18 +- {types => src}/payments/p2pkh.d.ts | 0 src/payments/p2pkh.js | 24 +- {types => src}/payments/p2sh.d.ts | 0 src/payments/p2sh.js | 33 +-- {types => src}/payments/p2wpkh.d.ts | 0 src/payments/p2wpkh.js | 38 +-- {types => src}/payments/p2wsh.d.ts | 0 src/payments/p2wsh.js | 44 ++-- {types => src}/psbt.d.ts | 35 ++- src/psbt.js | 101 ++++---- src/push_data.d.ts | 8 + src/push_data.js | 61 +++++ {types => src}/script.d.ts | 7 +- src/script.js | 39 +-- {types => src}/script_number.d.ts | 1 + src/script_number.js | 1 + {types => src}/script_signature.d.ts | 1 + src/script_signature.js | 5 +- {types => src}/transaction.d.ts | 1 + src/transaction.js | 40 +-- {types => src}/types.d.ts | 3 + src/types.js | 89 ++++--- test/bitcoin.core.spec.ts | 43 ---- test/ecpair.spec.ts | 341 -------------------------- test/integration/addresses.spec.ts | 13 +- test/integration/cltv.spec.ts | 5 +- test/integration/csv.spec.ts | 9 +- test/integration/payments.spec.ts | 5 +- test/integration/transactions.spec.ts | 45 ++-- test/psbt.spec.ts | 27 +- test/tsconfig.json | 2 +- ts_src/address.ts | 7 +- ts_src/bip66.ts | 111 +++++++++ ts_src/block.ts | 13 +- ts_src/bufferutils.ts | 6 +- ts_src/crypto.ts | 2 +- ts_src/ecpair.ts | 161 ------------ ts_src/index.ts | 17 +- ts_src/merkle.ts | 27 ++ ts_src/ops.ts | 141 +++++++++++ ts_src/payments/embed.ts | 2 +- ts_src/payments/p2ms.ts | 7 +- ts_src/payments/p2pk.ts | 8 +- ts_src/payments/p2pkh.ts | 13 +- ts_src/payments/p2sh.ts | 7 +- ts_src/payments/p2wpkh.ts | 14 +- ts_src/payments/p2wsh.ts | 10 +- ts_src/psbt.ts | 60 +++-- ts_src/push_data.ts | 76 ++++++ ts_src/script.ts | 15 +- ts_src/script_signature.ts | 5 +- ts_src/transaction.ts | 11 +- ts_src/types.ts | 27 +- tsconfig.json | 3 +- tslint.json | 4 +- types/ecpair.d.ts | 44 ---- 87 files changed, 1447 insertions(+), 1161 deletions(-) rename {types => src}/address.d.ts (95%) create mode 100644 src/bip66.d.ts create mode 100644 src/bip66.js rename {types => src}/block.d.ts (96%) rename {types => src}/bufferutils.d.ts (92%) rename {types => src}/crypto.d.ts (90%) delete mode 100644 src/ecpair.js rename {types => src}/index.d.ts (63%) create mode 100644 src/merkle.d.ts create mode 100644 src/merkle.js rename {types => src}/networks.d.ts (100%) create mode 100644 src/ops.d.ts create mode 100644 src/ops.js rename {types => src}/payments/embed.d.ts (100%) rename {types => src}/payments/index.d.ts (97%) rename {types => src}/payments/lazy.d.ts (100%) rename {types => src}/payments/p2ms.d.ts (100%) rename {types => src}/payments/p2pk.d.ts (100%) rename {types => src}/payments/p2pkh.d.ts (100%) rename {types => src}/payments/p2sh.d.ts (100%) rename {types => src}/payments/p2wpkh.d.ts (100%) rename {types => src}/payments/p2wsh.d.ts (100%) rename {types => src}/psbt.d.ts (88%) create mode 100644 src/push_data.d.ts create mode 100644 src/push_data.js rename {types => src}/script.d.ts (90%) rename {types => src}/script_number.d.ts (83%) rename {types => src}/script_signature.d.ts (88%) rename {types => src}/transaction.d.ts (98%) rename {types => src}/types.d.ts (86%) delete mode 100644 test/ecpair.spec.ts create mode 100644 ts_src/bip66.ts delete mode 100644 ts_src/ecpair.ts create mode 100644 ts_src/merkle.ts create mode 100644 ts_src/ops.ts create mode 100644 ts_src/push_data.ts delete mode 100644 types/ecpair.d.ts diff --git a/package-lock.json b/package-lock.json index a1ba1ec..f932d24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,12 +5,12 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.14.5" } }, "@babel/core": { @@ -196,14 +196,22 @@ } }, "@babel/highlight": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", - "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { + "@babel/helper-validator-identifier": "^7.14.5", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + } } }, "@babel/parser": { @@ -383,6 +391,24 @@ "@types/base-x": "*" } }, + "@types/bs58check": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/bs58check/-/bs58check-2.1.0.tgz", + "integrity": "sha512-OxsysnJQh82vy9DRbOcw9m2j/WiyqZLn0YBhKxdQ+aCwoHj+tWzyCgpwAkr79IfDXZKxc6h7k89T9pwS78CqTQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/create-hash": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/create-hash/-/create-hash-1.2.2.tgz", + "integrity": "sha512-Fg8/kfMJObbETFU/Tn+Y0jieYewryLrbKwLCEIwPyklZZVY2qB+64KFjhplGSw+cseZosfFXctXO+PyIYD8iZQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/mocha": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", @@ -390,9 +416,9 @@ "dev": true }, "@types/node": { - "version": "12.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz", - "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==", + "version": "16.11.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", + "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", "dev": true }, "@types/proxyquire": { @@ -401,6 +427,24 @@ "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==", "dev": true }, + "@types/randombytes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.0.tgz", + "integrity": "sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/wif": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.2.tgz", + "integrity": "sha512-IiIuBeJzlh4LWJ7kVTrX0nwB60OG0vvGTaWC/SgSbVFw7uYUTF6gEuvDZ1goWkeirekJDD58Y8g7NljQh2fNkA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -557,14 +601,6 @@ "integrity": "sha512-RQ1nc7xtnLa5XltnCqkoR2zmhuz498RjMJwrLKQzOE049D1HUqnYfon7cVSbwS5UGm0/EQlC2CH+NY3MyITA4Q==", "dev": true }, - "bip66": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", - "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "bip68": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/bip68/-/bip68-1.0.4.tgz", @@ -574,7 +610,8 @@ "bitcoin-ops": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz", - "integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==" + "integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow==", + "dev": true }, "bn.js": { "version": "4.11.8", @@ -888,18 +925,42 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "ecpair": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ecpair/-/ecpair-1.0.0.tgz", + "integrity": "sha512-1L+P/ivLC3eKHgqcX1M9tFYQWXDoqwJ3zQnN7zDaTtLpiCQKpFTaAZvnsPC5PkWB4q3EPFAHffCLvjfCqRjuwQ==", + "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "randombytes": "^2.0.1", + "tiny-secp256k1": "^1.1.6", + "typeforce": "^1.11.3", + "wif": "^2.0.1" + } + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } } }, "emoji-regex": { @@ -956,12 +1017,6 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -1549,11 +1604,6 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, - "merkle-lib": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz", - "integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY=" - }, "minimaldata": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/minimaldata/-/minimaldata-1.0.2.tgz", @@ -2091,6 +2141,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz", "integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=", + "dev": true, "requires": { "bitcoin-ops": "^1.3.0" } @@ -2446,15 +2497,15 @@ } }, "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "tslint": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", - "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -2465,10 +2516,10 @@ "glob": "^7.1.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", + "mkdirp": "^0.5.3", "resolve": "^1.3.2", "semver": "^5.3.0", - "tslib": "^1.8.0", + "tslib": "^1.13.0", "tsutils": "^2.29.0" }, "dependencies": { @@ -2510,9 +2561,9 @@ "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" }, "typescript": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", - "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true }, "uuid": { @@ -2522,9 +2573,9 @@ "dev": true }, "varuint-bitcoin": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.0.tgz", - "integrity": "sha512-jCEPG+COU/1Rp84neKTyDJQr478/hAfVp5xxYn09QEH0yBjbmPeMfuuQIrp+BUD83hybtYZKhr5elV3bvdV1bA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", + "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==", "requires": { "safe-buffer": "^5.1.1" } diff --git a/package.json b/package.json index 887dff8..03df9d5 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "5.2.0", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", - "types": "./types/index.d.ts", + "types": "./src/index.d.ts", "engines": { "node": ">=8.0.0" }, @@ -18,7 +18,7 @@ "audit": "NPM_AUDIT_IGNORE_DEV=1 NPM_AUDIT_IGNORE_LEVEL=low npm-audit-whitelister .npm-audit-whitelister.json", "build": "npm run clean && tsc -p ./tsconfig.json && npm run formatjs", "build:tests": "npm run clean:jstests && tsc -p ./test/tsconfig.json", - "clean": "rimraf src types", + "clean": "rimraf src", "clean:jstests": "rimraf 'test/**/!(ts-node-register)*.js'", "coverage-report": "npm run build && npm run nobuild:coverage-report", "coverage-html": "npm run build && npm run nobuild:coverage-html", @@ -46,37 +46,36 @@ "url": "https://github.com/bitcoinjs/bitcoinjs-lib.git" }, "files": [ - "src", - "types" + "src" ], "dependencies": { "bech32": "^2.0.0", "bip174": "^2.0.1", "bip32": "^2.0.4", - "bip66": "^1.1.0", - "bitcoin-ops": "^1.4.0", - "bs58check": "^2.0.0", + "bs58check": "^2.1.2", "create-hash": "^1.1.0", "create-hmac": "^1.1.3", - "merkle-lib": "^2.0.10", - "pushdata-bitcoin": "^1.0.1", "randombytes": "^2.0.1", - "tiny-secp256k1": "^1.1.6", "typeforce": "^1.11.3", - "varuint-bitcoin": "^1.0.4", + "varuint-bitcoin": "^1.1.2", "wif": "^2.0.1" }, "devDependencies": { "@types/bs58": "^4.0.0", + "@types/bs58check": "^2.1.0", + "@types/create-hash": "^1.2.2", "@types/mocha": "^5.2.7", - "@types/node": "12.7.5", + "@types/node": "^16.11.1", "@types/proxyquire": "^1.3.28", + "@types/randombytes": "^2.0.0", + "@types/wif": "^2.0.2", "bip39": "^3.0.2", "bip65": "^1.0.1", "bip68": "^1.0.3", "bn.js": "^4.11.8", "bs58": "^4.0.0", "dhttp": "^3.0.0", + "ecpair": "^1.0.0", "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^7.1.1", @@ -87,8 +86,8 @@ "regtest-client": "0.2.0", "rimraf": "^2.6.3", "ts-node": "^8.3.0", - "tslint": "^5.20.1", - "typescript": "3.2.2" + "tslint": "^6.1.3", + "typescript": "^4.4.4" }, "license": "MIT" } diff --git a/types/address.d.ts b/src/address.d.ts similarity index 95% rename from types/address.d.ts rename to src/address.d.ts index 5c7ed5a..be0e00a 100644 --- a/types/address.d.ts +++ b/src/address.d.ts @@ -1,3 +1,4 @@ +/// <reference types="node" /> import { Network } from './networks'; export interface Base58CheckResult { hash: Buffer; diff --git a/src/address.js b/src/address.js index 1709c37..12938fc 100644 --- a/src/address.js +++ b/src/address.js @@ -1,12 +1,13 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.toOutputScript = exports.fromOutputScript = exports.toBech32 = exports.toBase58Check = exports.fromBech32 = exports.fromBase58Check = void 0; const networks = require('./networks'); const payments = require('./payments'); const bscript = require('./script'); const types = require('./types'); -const { bech32, bech32m } = require('bech32'); +const bech32_1 = require('bech32'); const bs58check = require('bs58check'); -const typeforce = require('typeforce'); +const { typeforce } = types; const FUTURE_SEGWIT_MAX_SIZE = 40; const FUTURE_SEGWIT_MIN_SIZE = 2; const FUTURE_SEGWIT_MAX_VERSION = 16; @@ -43,17 +44,17 @@ function fromBech32(address) { let result; let version; try { - result = bech32.decode(address); + result = bech32_1.bech32.decode(address); } catch (e) {} if (result) { version = result.words[0]; if (version !== 0) throw new TypeError(address + ' uses wrong encoding'); } else { - result = bech32m.decode(address); + result = bech32_1.bech32m.decode(address); version = result.words[0]; if (version === 0) throw new TypeError(address + ' uses wrong encoding'); } - const data = bech32.fromWords(result.words.slice(1)); + const data = bech32_1.bech32.fromWords(result.words.slice(1)); return { version, prefix: result.prefix, @@ -70,11 +71,11 @@ function toBase58Check(hash, version) { } exports.toBase58Check = toBase58Check; function toBech32(data, version, prefix) { - const words = bech32.toWords(data); + const words = bech32_1.bech32.toWords(data); words.unshift(version); return version === 0 - ? bech32.encode(prefix, words) - : bech32m.encode(prefix, words); + ? bech32_1.bech32.encode(prefix, words) + : bech32_1.bech32m.encode(prefix, words); } exports.toBech32 = toBech32; function fromOutputScript(output, network) { diff --git a/src/bip66.d.ts b/src/bip66.d.ts new file mode 100644 index 0000000..547c57f --- /dev/null +++ b/src/bip66.d.ts @@ -0,0 +1,7 @@ +/// <reference types="node" /> +export declare function check(buffer: Buffer): boolean; +export declare function decode(buffer: Buffer): { + r: Buffer; + s: Buffer; +}; +export declare function encode(r: Buffer, s: Buffer): Buffer; diff --git a/src/bip66.js b/src/bip66.js new file mode 100644 index 0000000..0070f99 --- /dev/null +++ b/src/bip66.js @@ -0,0 +1,102 @@ +'use strict'; +// Reference https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki +// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] +// NOTE: SIGHASH byte ignored AND restricted, truncate before use +Object.defineProperty(exports, '__esModule', { value: true }); +exports.encode = exports.decode = exports.check = void 0; +function check(buffer) { + if (buffer.length < 8) return false; + if (buffer.length > 72) return false; + if (buffer[0] !== 0x30) return false; + if (buffer[1] !== buffer.length - 2) return false; + if (buffer[2] !== 0x02) return false; + const lenR = buffer[3]; + if (lenR === 0) return false; + if (5 + lenR >= buffer.length) return false; + if (buffer[4 + lenR] !== 0x02) return false; + const lenS = buffer[5 + lenR]; + if (lenS === 0) return false; + if (6 + lenR + lenS !== buffer.length) return false; + if (buffer[4] & 0x80) return false; + if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) return false; + if (buffer[lenR + 6] & 0x80) return false; + if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80)) + return false; + return true; +} +exports.check = check; +function decode(buffer) { + if (buffer.length < 8) throw new Error('DER sequence length is too short'); + if (buffer.length > 72) throw new Error('DER sequence length is too long'); + if (buffer[0] !== 0x30) throw new Error('Expected DER sequence'); + if (buffer[1] !== buffer.length - 2) + throw new Error('DER sequence length is invalid'); + if (buffer[2] !== 0x02) throw new Error('Expected DER integer'); + const lenR = buffer[3]; + if (lenR === 0) throw new Error('R length is zero'); + if (5 + lenR >= buffer.length) throw new Error('R length is too long'); + if (buffer[4 + lenR] !== 0x02) throw new Error('Expected DER integer (2)'); + const lenS = buffer[5 + lenR]; + if (lenS === 0) throw new Error('S length is zero'); + if (6 + lenR + lenS !== buffer.length) throw new Error('S length is invalid'); + if (buffer[4] & 0x80) throw new Error('R value is negative'); + if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) + throw new Error('R value excessively padded'); + if (buffer[lenR + 6] & 0x80) throw new Error('S value is negative'); + if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80)) + throw new Error('S value excessively padded'); + // non-BIP66 - extract R, S values + return { + r: buffer.slice(4, 4 + lenR), + s: buffer.slice(6 + lenR), + }; +} +exports.decode = decode; +/* + * Expects r and s to be positive DER integers. + * + * The DER format uses the most significant bit as a sign bit (& 0x80). + * If the significant bit is set AND the integer is positive, a 0x00 is prepended. + * + * Examples: + * + * 0 => 0x00 + * 1 => 0x01 + * -1 => 0xff + * 127 => 0x7f + * -127 => 0x81 + * 128 => 0x0080 + * -128 => 0x80 + * 255 => 0x00ff + * -255 => 0xff01 + * 16300 => 0x3fac + * -16300 => 0xc054 + * 62300 => 0x00f35c + * -62300 => 0xff0ca4 + */ +function encode(r, s) { + const lenR = r.length; + const lenS = s.length; + if (lenR === 0) throw new Error('R length is zero'); + if (lenS === 0) throw new Error('S length is zero'); + if (lenR > 33) throw new Error('R length is too long'); + if (lenS > 33) throw new Error('S length is too long'); + if (r[0] & 0x80) throw new Error('R value is negative'); + if (s[0] & 0x80) throw new Error('S value is negative'); + if (lenR > 1 && r[0] === 0x00 && !(r[1] & 0x80)) + throw new Error('R value excessively padded'); + if (lenS > 1 && s[0] === 0x00 && !(s[1] & 0x80)) + throw new Error('S value excessively padded'); + const signature = Buffer.allocUnsafe(6 + lenR + lenS); + // 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] + signature[0] = 0x30; + signature[1] = signature.length - 2; + signature[2] = 0x02; + signature[3] = r.length; + r.copy(signature, 4); + signature[4 + lenR] = 0x02; + signature[5 + lenR] = s.length; + s.copy(signature, 6 + lenR); + return signature; +} +exports.encode = encode; diff --git a/types/block.d.ts b/src/block.d.ts similarity index 96% rename from types/block.d.ts rename to src/block.d.ts index 7d8309c..1d90c13 100644 --- a/types/block.d.ts +++ b/src/block.d.ts @@ -1,3 +1,4 @@ +/// <reference types="node" /> import { Transaction } from './transaction'; export declare class Block { static fromBuffer(buffer: Buffer): Block; diff --git a/src/block.js b/src/block.js index cb3ee4b..4e6ca84 100644 --- a/src/block.js +++ b/src/block.js @@ -1,12 +1,12 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.Block = void 0; const bufferutils_1 = require('./bufferutils'); const bcrypto = require('./crypto'); +const merkle_1 = require('./merkle'); const transaction_1 = require('./transaction'); const types = require('./types'); -const fastMerkleRoot = require('merkle-lib/fastRoot'); -const typeforce = require('typeforce'); -const varuint = require('varuint-bitcoin'); +const { typeforce } = types; const errorMerkleNoTxes = new TypeError( 'Cannot compute merkle root for zero transactions', ); @@ -14,16 +14,6 @@ const errorWitnessNotSegwit = new TypeError( 'Cannot compute witness commit for non-segwit block', ); class Block { - constructor() { - this.version = 1; - this.prevHash = undefined; - this.merkleRoot = undefined; - this.timestamp = 0; - this.witnessCommit = undefined; - this.bits = 0; - this.nonce = 0; - this.transactions = undefined; - } static fromBuffer(buffer) { if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); const bufferReader = new bufferutils_1.BufferReader(buffer); @@ -72,13 +62,21 @@ class Block { const hashes = transactions.map(transaction => transaction.getHash(forWitness), ); - const rootHash = fastMerkleRoot(hashes, bcrypto.hash256); + const rootHash = (0, merkle_1.fastMerkleRoot)(hashes, bcrypto.hash256); return forWitness ? bcrypto.hash256( Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]), ) : rootHash; } + version = 1; + prevHash = undefined; + merkleRoot = undefined; + timestamp = 0; + witnessCommit = undefined; + bits = 0; + nonce = 0; + transactions = undefined; getWitnessCommit() { if (!txesHaveWitnessCommit(this.transactions)) return null; // The merkle root for the witness data is in an OP_RETURN output. @@ -117,7 +115,7 @@ class Block { if (headersOnly || !this.transactions) return 80; return ( 80 + - varuint.encodingLength(this.transactions.length) + + bufferutils_1.varuint.encodingLength(this.transactions.length) + this.transactions.reduce((a, x) => a + x.byteLength(allowWitness), 0) ); } @@ -125,7 +123,7 @@ class Block { return bcrypto.hash256(this.toBuffer(true)); } getId() { - return bufferutils_1.reverseBuffer(this.getHash()).toString('hex'); + return (0, bufferutils_1.reverseBuffer)(this.getHash()).toString('hex'); } getUTCDate() { const date = new Date(0); // epoch @@ -143,8 +141,12 @@ class Block { bufferWriter.writeUInt32(this.bits); bufferWriter.writeUInt32(this.nonce); if (headersOnly || !this.transactions) return buffer; - varuint.encode(this.transactions.length, buffer, bufferWriter.offset); - bufferWriter.offset += varuint.encode.bytes; + bufferutils_1.varuint.encode( + this.transactions.length, + buffer, + bufferWriter.offset, + ); + bufferWriter.offset += bufferutils_1.varuint.encode.bytes; this.transactions.forEach(tx => { const txSize = tx.byteLength(); // TODO: extract from toBuffer? tx.toBuffer(buffer, bufferWriter.offset); @@ -166,7 +168,7 @@ class Block { ); } checkProofOfWork() { - const hash = bufferutils_1.reverseBuffer(this.getHash()); + const hash = (0, bufferutils_1.reverseBuffer)(this.getHash()); const target = Block.calculateTarget(this.bits); return hash.compare(target) <= 0; } diff --git a/types/bufferutils.d.ts b/src/bufferutils.d.ts similarity index 92% rename from types/bufferutils.d.ts rename to src/bufferutils.d.ts index 95a48ba..40f89b3 100644 --- a/types/bufferutils.d.ts +++ b/src/bufferutils.d.ts @@ -1,3 +1,6 @@ +/// <reference types="node" /> +import * as varuint from 'varuint-bitcoin'; +export { varuint }; export declare function readUInt64LE(buffer: Buffer, offset: number): number; export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number; export declare function reverseBuffer(buffer: Buffer): Buffer; diff --git a/src/bufferutils.js b/src/bufferutils.js index a68fd31..933bebc 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -1,8 +1,10 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.BufferReader = exports.BufferWriter = exports.cloneBuffer = exports.reverseBuffer = exports.writeUInt64LE = exports.readUInt64LE = exports.varuint = void 0; const types = require('./types'); -const typeforce = require('typeforce'); +const { typeforce } = types; const varuint = require('varuint-bitcoin'); +exports.varuint = varuint; // https://github.com/feross/buffer/blob/master/index.js#L1127 function verifuint(value, max) { if (typeof value !== 'number') @@ -51,6 +53,8 @@ exports.cloneBuffer = cloneBuffer; * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ class BufferWriter { + buffer; + offset; constructor(buffer, offset = 0) { this.buffer = buffer; this.offset = offset; @@ -92,6 +96,8 @@ exports.BufferWriter = BufferWriter; * Helper class for reading of bitcoin data types from a buffer. */ class BufferReader { + buffer; + offset; constructor(buffer, offset = 0) { this.buffer = buffer; this.offset = offset; diff --git a/types/crypto.d.ts b/src/crypto.d.ts similarity index 90% rename from types/crypto.d.ts rename to src/crypto.d.ts index 5d93acd..1743681 100644 --- a/types/crypto.d.ts +++ b/src/crypto.d.ts @@ -1,3 +1,4 @@ +/// <reference types="node" /> export declare function ripemd160(buffer: Buffer): Buffer; export declare function sha1(buffer: Buffer): Buffer; export declare function sha256(buffer: Buffer): Buffer; diff --git a/src/crypto.js b/src/crypto.js index e7dd596..f53b041 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.hash256 = exports.hash160 = exports.sha256 = exports.sha1 = exports.ripemd160 = void 0; const createHash = require('create-hash'); function ripemd160(buffer) { try { diff --git a/src/ecpair.js b/src/ecpair.js deleted file mode 100644 index 91fe3a9..0000000 --- a/src/ecpair.js +++ /dev/null @@ -1,107 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { value: true }); -const NETWORKS = require('./networks'); -const types = require('./types'); -const ecc = require('tiny-secp256k1'); -const randomBytes = require('randombytes'); -const typeforce = require('typeforce'); -const wif = require('wif'); -const isOptions = typeforce.maybe( - typeforce.compile({ - compressed: types.maybe(types.Boolean), - network: types.maybe(types.Network), - }), -); -class ECPair { - constructor(__D, __Q, options) { - this.__D = __D; - this.__Q = __Q; - this.lowR = false; - if (options === undefined) options = {}; - this.compressed = - options.compressed === undefined ? true : options.compressed; - this.network = options.network || NETWORKS.bitcoin; - if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed); - } - get privateKey() { - return this.__D; - } - get publicKey() { - if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__D, this.compressed); - return this.__Q; - } - toWIF() { - if (!this.__D) throw new Error('Missing private key'); - return wif.encode(this.network.wif, this.__D, this.compressed); - } - sign(hash, lowR) { - if (!this.__D) throw new Error('Missing private key'); - if (lowR === undefined) lowR = this.lowR; - if (lowR === false) { - return ecc.sign(hash, this.__D); - } else { - let sig = ecc.sign(hash, this.__D); - const extraData = Buffer.alloc(32, 0); - let counter = 0; - // if first try is lowR, skip the loop - // for second try and on, add extra entropy counting up - while (sig[0] > 0x7f) { - counter++; - extraData.writeUIntLE(counter, 0, 6); - sig = ecc.signWithEntropy(hash, this.__D, extraData); - } - return sig; - } - } - verify(hash, signature) { - return ecc.verify(hash, this.publicKey, signature); - } -} -function fromPrivateKey(buffer, options) { - typeforce(types.Buffer256bit, buffer); - if (!ecc.isPrivate(buffer)) - throw new TypeError('Private key not in range [1, n)'); - typeforce(isOptions, options); - return new ECPair(buffer, undefined, options); -} -exports.fromPrivateKey = fromPrivateKey; -function fromPublicKey(buffer, options) { - typeforce(ecc.isPoint, buffer); - typeforce(isOptions, options); - return new ECPair(undefined, buffer, options); -} -exports.fromPublicKey = fromPublicKey; -function fromWIF(wifString, network) { - const decoded = wif.decode(wifString); - const version = decoded.version; - // list of networks? - if (types.Array(network)) { - network = network - .filter(x => { - return version === x.wif; - }) - .pop(); - if (!network) throw new Error('Unknown network version'); - // otherwise, assume a network object (or default to bitcoin) - } else { - network = network || NETWORKS.bitcoin; - if (version !== network.wif) throw new Error('Invalid network version'); - } - return fromPrivateKey(decoded.privateKey, { - compressed: decoded.compressed, - network: network, - }); -} -exports.fromWIF = fromWIF; -function makeRandom(options) { - typeforce(isOptions, options); - if (options === undefined) options = {}; - const rng = options.rng || randomBytes; - let d; - do { - d = rng(32); - typeforce(types.Buffer256bit, d); - } while (!ecc.isPrivate(d)); - return fromPrivateKey(d, options); -} -exports.makeRandom = makeRandom; diff --git a/types/index.d.ts b/src/index.d.ts similarity index 63% rename from types/index.d.ts rename to src/index.d.ts index f63a986..1086e4b 100644 --- a/types/index.d.ts +++ b/src/index.d.ts @@ -1,18 +1,15 @@ import * as bip32 from 'bip32'; import * as address from './address'; import * as crypto from './crypto'; -import * as ECPair from './ecpair'; import * as networks from './networks'; import * as payments from './payments'; import * as script from './script'; -export { ECPair, address, bip32, crypto, networks, payments, script }; +export { address, bip32, crypto, networks, payments, script }; export { Block } from './block'; -export { Psbt, PsbtTxInput, PsbtTxOutput } from './psbt'; -export { OPS as opcodes } from './script'; +export { Psbt, PsbtTxInput, PsbtTxOutput, Signer, SignerAsync, HDSigner, HDSignerAsync, } from './psbt'; +export { OPS as opcodes } from './ops'; export { Transaction } from './transaction'; export { BIP32Interface } from 'bip32'; -export { ECPairInterface, Signer, SignerAsync } from './ecpair'; export { Network } from './networks'; export { Payment, PaymentCreator, PaymentOpts, Stack, StackElement, } from './payments'; -export { OpCode } from './script'; export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/src/index.js b/src/index.js index e27b98a..a643df8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,12 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.Transaction = exports.opcodes = exports.Psbt = exports.Block = exports.script = exports.payments = exports.networks = exports.crypto = exports.bip32 = exports.address = void 0; const bip32 = require('bip32'); exports.bip32 = bip32; const address = require('./address'); exports.address = address; const crypto = require('./crypto'); exports.crypto = crypto; -const ECPair = require('./ecpair'); -exports.ECPair = ECPair; const networks = require('./networks'); exports.networks = networks; const payments = require('./payments'); @@ -15,10 +14,30 @@ exports.payments = payments; const script = require('./script'); exports.script = script; var block_1 = require('./block'); -exports.Block = block_1.Block; +Object.defineProperty(exports, 'Block', { + enumerable: true, + get: function() { + return block_1.Block; + }, +}); var psbt_1 = require('./psbt'); -exports.Psbt = psbt_1.Psbt; -var script_1 = require('./script'); -exports.opcodes = script_1.OPS; +Object.defineProperty(exports, 'Psbt', { + enumerable: true, + get: function() { + return psbt_1.Psbt; + }, +}); +var ops_1 = require('./ops'); +Object.defineProperty(exports, 'opcodes', { + enumerable: true, + get: function() { + return ops_1.OPS; + }, +}); var transaction_1 = require('./transaction'); -exports.Transaction = transaction_1.Transaction; +Object.defineProperty(exports, 'Transaction', { + enumerable: true, + get: function() { + return transaction_1.Transaction; + }, +}); diff --git a/src/merkle.d.ts b/src/merkle.d.ts new file mode 100644 index 0000000..d602201 --- /dev/null +++ b/src/merkle.d.ts @@ -0,0 +1,2 @@ +/// <reference types="node" /> +export declare function fastMerkleRoot(values: Buffer[], digestFn: (b: Buffer) => Buffer): Buffer; diff --git a/src/merkle.js b/src/merkle.js new file mode 100644 index 0000000..e93f9ca --- /dev/null +++ b/src/merkle.js @@ -0,0 +1,22 @@ +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.fastMerkleRoot = void 0; +function fastMerkleRoot(values, digestFn) { + if (!Array.isArray(values)) throw TypeError('Expected values Array'); + if (typeof digestFn !== 'function') + throw TypeError('Expected digest Function'); + let length = values.length; + const results = values.concat(); + while (length > 1) { + let j = 0; + for (let i = 0; i < length; i += 2, ++j) { + const left = results[i]; + const right = i + 1 === length ? left : results[i + 1]; + const data = Buffer.concat([left, right]); + results[j] = digestFn(data); + } + length = j; + } + return results[0]; +} +exports.fastMerkleRoot = fastMerkleRoot; diff --git a/types/networks.d.ts b/src/networks.d.ts similarity index 100% rename from types/networks.d.ts rename to src/networks.d.ts diff --git a/src/networks.js b/src/networks.js index 0c31fe1..ea710f8 100644 --- a/src/networks.js +++ b/src/networks.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.testnet = exports.regtest = exports.bitcoin = void 0; exports.bitcoin = { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'bc', diff --git a/src/ops.d.ts b/src/ops.d.ts new file mode 100644 index 0000000..cda7a84 --- /dev/null +++ b/src/ops.d.ts @@ -0,0 +1,7 @@ +declare const OPS: { + [key: string]: number; +}; +declare const REVERSE_OPS: { + [key: number]: string; +}; +export { OPS, REVERSE_OPS }; diff --git a/src/ops.js b/src/ops.js new file mode 100644 index 0000000..9d629cd --- /dev/null +++ b/src/ops.js @@ -0,0 +1,130 @@ +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.REVERSE_OPS = exports.OPS = void 0; +const OPS = { + OP_FALSE: 0, + OP_0: 0, + OP_PUSHDATA1: 76, + OP_PUSHDATA2: 77, + OP_PUSHDATA4: 78, + OP_1NEGATE: 79, + OP_RESERVED: 80, + OP_TRUE: 81, + OP_1: 81, + OP_2: 82, + OP_3: 83, + OP_4: 84, + OP_5: 85, + OP_6: 86, + OP_7: 87, + OP_8: 88, + OP_9: 89, + OP_10: 90, + OP_11: 91, + OP_12: 92, + OP_13: 93, + OP_14: 94, + OP_15: 95, + OP_16: 96, + OP_NOP: 97, + OP_VER: 98, + OP_IF: 99, + OP_NOTIF: 100, + OP_VERIF: 101, + OP_VERNOTIF: 102, + OP_ELSE: 103, + OP_ENDIF: 104, + OP_VERIFY: 105, + OP_RETURN: 106, + OP_TOALTSTACK: 107, + OP_FROMALTSTACK: 108, + OP_2DROP: 109, + OP_2DUP: 110, + OP_3DUP: 111, + OP_2OVER: 112, + OP_2ROT: 113, + OP_2SWAP: 114, + OP_IFDUP: 115, + OP_DEPTH: 116, + OP_DROP: 117, + OP_DUP: 118, + OP_NIP: 119, + OP_OVER: 120, + OP_PICK: 121, + OP_ROLL: 122, + OP_ROT: 123, + OP_SWAP: 124, + OP_TUCK: 125, + OP_CAT: 126, + OP_SUBSTR: 127, + OP_LEFT: 128, + OP_RIGHT: 129, + OP_SIZE: 130, + OP_INVERT: 131, + OP_AND: 132, + OP_OR: 133, + OP_XOR: 134, + OP_EQUAL: 135, + OP_EQUALVERIFY: 136, + OP_RESERVED1: 137, + OP_RESERVED2: 138, + OP_1ADD: 139, + OP_1SUB: 140, + OP_2MUL: 141, + OP_2DIV: 142, + OP_NEGATE: 143, + OP_ABS: 144, + OP_NOT: 145, + OP_0NOTEQUAL: 146, + OP_ADD: 147, + OP_SUB: 148, + OP_MUL: 149, + OP_DIV: 150, + OP_MOD: 151, + OP_LSHIFT: 152, + OP_RSHIFT: 153, + OP_BOOLAND: 154, + OP_BOOLOR: 155, + OP_NUMEQUAL: 156, + OP_NUMEQUALVERIFY: 157, + OP_NUMNOTEQUAL: 158, + OP_LESSTHAN: 159, + OP_GREATERTHAN: 160, + OP_LESSTHANOREQUAL: 161, + OP_GREATERTHANOREQUAL: 162, + OP_MIN: 163, + OP_MAX: 164, + OP_WITHIN: 165, + OP_RIPEMD160: 166, + OP_SHA1: 167, + OP_SHA256: 168, + OP_HASH160: 169, + OP_HASH256: 170, + OP_CODESEPARATOR: 171, + OP_CHECKSIG: 172, + OP_CHECKSIGVERIFY: 173, + OP_CHECKMULTISIG: 174, + OP_CHECKMULTISIGVERIFY: 175, + OP_NOP1: 176, + OP_NOP2: 177, + OP_CHECKLOCKTIMEVERIFY: 177, + OP_NOP3: 178, + OP_CHECKSEQUENCEVERIFY: 178, + OP_NOP4: 179, + OP_NOP5: 180, + OP_NOP6: 181, + OP_NOP7: 182, + OP_NOP8: 183, + OP_NOP9: 184, + OP_NOP10: 185, + OP_PUBKEYHASH: 253, + OP_PUBKEY: 254, + OP_INVALIDOPCODE: 255, +}; +exports.OPS = OPS; +const REVERSE_OPS = {}; +exports.REVERSE_OPS = REVERSE_OPS; +for (const op of Object.keys(OPS)) { + const code = OPS[op]; + REVERSE_OPS[code] = op; +} diff --git a/types/payments/embed.d.ts b/src/payments/embed.d.ts similarity index 100% rename from types/payments/embed.d.ts rename to src/payments/embed.d.ts diff --git a/src/payments/embed.js b/src/payments/embed.js index 19df35d..4b7218f 100644 --- a/src/payments/embed.js +++ b/src/payments/embed.js @@ -1,9 +1,10 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.p2data = void 0; const networks_1 = require('../networks'); const bscript = require('../script'); +const types_1 = require('../types'); const lazy = require('./lazy'); -const typef = require('typeforce'); const OPS = bscript.OPS; function stacksEqual(a, b) { if (a.length !== b.length) return false; @@ -15,11 +16,13 @@ function stacksEqual(a, b) { function p2data(a, opts) { if (!a.data && !a.output) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); - typef( + (0, types_1.typeforce)( { - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - data: typef.maybe(typef.arrayOf(typef.Buffer)), + network: types_1.typeforce.maybe(types_1.typeforce.Object), + output: types_1.typeforce.maybe(types_1.typeforce.Buffer), + data: types_1.typeforce.maybe( + types_1.typeforce.arrayOf(types_1.typeforce.Buffer), + ), }, a, ); @@ -38,7 +41,7 @@ function p2data(a, opts) { if (a.output) { const chunks = bscript.decompile(a.output); if (chunks[0] !== OPS.OP_RETURN) throw new TypeError('Output is invalid'); - if (!chunks.slice(1).every(typef.Buffer)) + if (!chunks.slice(1).every(types_1.typeforce.Buffer)) throw new TypeError('Output is invalid'); if (a.data && !stacksEqual(a.data, o.data)) throw new TypeError('Data mismatch'); diff --git a/types/payments/index.d.ts b/src/payments/index.d.ts similarity index 97% rename from types/payments/index.d.ts rename to src/payments/index.d.ts index 922e0bf..1edf071 100644 --- a/types/payments/index.d.ts +++ b/src/payments/index.d.ts @@ -1,3 +1,4 @@ +/// <reference types="node" /> import { Network } from '../networks'; import { p2data as embed } from './embed'; import { p2ms } from './p2ms'; diff --git a/src/payments/index.js b/src/payments/index.js index ddab977..c23c529 100644 --- a/src/payments/index.js +++ b/src/payments/index.js @@ -1,18 +1,54 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.p2wsh = exports.p2wpkh = exports.p2sh = exports.p2pkh = exports.p2pk = exports.p2ms = exports.embed = void 0; const embed_1 = require('./embed'); -exports.embed = embed_1.p2data; +Object.defineProperty(exports, 'embed', { + enumerable: true, + get: function() { + return embed_1.p2data; + }, +}); const p2ms_1 = require('./p2ms'); -exports.p2ms = p2ms_1.p2ms; +Object.defineProperty(exports, 'p2ms', { + enumerable: true, + get: function() { + return p2ms_1.p2ms; + }, +}); const p2pk_1 = require('./p2pk'); -exports.p2pk = p2pk_1.p2pk; +Object.defineProperty(exports, 'p2pk', { + enumerable: true, + get: function() { + return p2pk_1.p2pk; + }, +}); const p2pkh_1 = require('./p2pkh'); -exports.p2pkh = p2pkh_1.p2pkh; +Object.defineProperty(exports, 'p2pkh', { + enumerable: true, + get: function() { + return p2pkh_1.p2pkh; + }, +}); const p2sh_1 = require('./p2sh'); -exports.p2sh = p2sh_1.p2sh; +Object.defineProperty(exports, 'p2sh', { + enumerable: true, + get: function() { + return p2sh_1.p2sh; + }, +}); const p2wpkh_1 = require('./p2wpkh'); -exports.p2wpkh = p2wpkh_1.p2wpkh; +Object.defineProperty(exports, 'p2wpkh', { + enumerable: true, + get: function() { + return p2wpkh_1.p2wpkh; + }, +}); const p2wsh_1 = require('./p2wsh'); -exports.p2wsh = p2wsh_1.p2wsh; +Object.defineProperty(exports, 'p2wsh', { + enumerable: true, + get: function() { + return p2wsh_1.p2wsh; + }, +}); // TODO // witness commitment diff --git a/types/payments/lazy.d.ts b/src/payments/lazy.d.ts similarity index 100% rename from types/payments/lazy.d.ts rename to src/payments/lazy.d.ts diff --git a/src/payments/lazy.js b/src/payments/lazy.js index 1a71521..e620c72 100644 --- a/src/payments/lazy.js +++ b/src/payments/lazy.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.value = exports.prop = void 0; function prop(object, name, f) { Object.defineProperty(object, name, { configurable: true, diff --git a/types/payments/p2ms.d.ts b/src/payments/p2ms.d.ts similarity index 100% rename from types/payments/p2ms.d.ts rename to src/payments/p2ms.d.ts diff --git a/src/payments/p2ms.js b/src/payments/p2ms.js index 9fed788..0b7e72d 100644 --- a/src/payments/p2ms.js +++ b/src/payments/p2ms.js @@ -1,11 +1,11 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.p2ms = void 0; const networks_1 = require('../networks'); const bscript = require('../script'); +const types_1 = require('../types'); const lazy = require('./lazy'); const OPS = bscript.OPS; -const typef = require('typeforce'); -const ecc = require('tiny-secp256k1'); const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 function stacksEqual(a, b) { if (a.length !== b.length) return false; @@ -30,15 +30,19 @@ function p2ms(a, opts) { (opts.allowIncomplete && x === OPS.OP_0) !== undefined ); } - typef( + (0, types_1.typeforce)( { - network: typef.maybe(typef.Object), - m: typef.maybe(typef.Number), - n: typef.maybe(typef.Number), - output: typef.maybe(typef.Buffer), - pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), - signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), - input: typef.maybe(typef.Buffer), + network: types_1.typeforce.maybe(types_1.typeforce.Object), + m: types_1.typeforce.maybe(types_1.typeforce.Number), + n: types_1.typeforce.maybe(types_1.typeforce.Number), + output: types_1.typeforce.maybe(types_1.typeforce.Buffer), + pubkeys: types_1.typeforce.maybe( + types_1.typeforce.arrayOf(types_1.isPoint), + ), + signatures: types_1.typeforce.maybe( + types_1.typeforce.arrayOf(isAcceptableSignature), + ), + input: types_1.typeforce.maybe(types_1.typeforce.Buffer), }, a, ); @@ -101,14 +105,15 @@ function p2ms(a, opts) { if (opts.validate) { if (a.output) { decode(a.output); - if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid'); - if (!typef.Number(chunks[chunks.length - 2])) + if (!types_1.typeforce.Number(chunks[0])) + throw new TypeError('Output is invalid'); + if (!types_1.typeforce.Number(chunks[chunks.length - 2])) throw new TypeError('Output is invalid'); if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) throw new TypeError('Output is invalid'); if (o.m <= 0 || o.n > 16 || o.m > o.n || o.n !== chunks.length - 3) throw new TypeError('Output is invalid'); - if (!o.pubkeys.every(x => ecc.isPoint(x))) + if (!o.pubkeys.every(x => (0, types_1.isPoint)(x))) throw new TypeError('Output is invalid'); if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch'); if (a.n !== undefined && a.n !== o.n) throw new TypeError('n mismatch'); diff --git a/types/payments/p2pk.d.ts b/src/payments/p2pk.d.ts similarity index 100% rename from types/payments/p2pk.d.ts rename to src/payments/p2pk.d.ts diff --git a/src/payments/p2pk.js b/src/payments/p2pk.js index 702669e..2849530 100644 --- a/src/payments/p2pk.js +++ b/src/payments/p2pk.js @@ -1,24 +1,24 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.p2pk = void 0; const networks_1 = require('../networks'); const bscript = require('../script'); +const types_1 = require('../types'); const lazy = require('./lazy'); -const typef = require('typeforce'); const OPS = bscript.OPS; -const ecc = require('tiny-secp256k1'); // input: {signature} // output: {pubKey} OP_CHECKSIG function p2pk(a, opts) { if (!a.input && !a.output && !a.pubkey && !a.input && !a.signature) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); - typef( + (0, types_1.typeforce)( { - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - pubkey: typef.maybe(ecc.isPoint), - signature: typef.maybe(bscript.isCanonicalScriptSignature), - input: typef.maybe(typef.Buffer), + network: types_1.typeforce.maybe(types_1.typeforce.Object), + output: types_1.typeforce.maybe(types_1.typeforce.Buffer), + pubkey: types_1.typeforce.maybe(types_1.isPoint), + signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature), + input: types_1.typeforce.maybe(types_1.typeforce.Buffer), }, a, ); @@ -52,7 +52,7 @@ function p2pk(a, opts) { if (a.output) { if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid'); - if (!ecc.isPoint(o.pubkey)) + if (!(0, types_1.isPoint)(o.pubkey)) throw new TypeError('Output pubkey is invalid'); if (a.pubkey && !a.pubkey.equals(o.pubkey)) throw new TypeError('Pubkey mismatch'); diff --git a/types/payments/p2pkh.d.ts b/src/payments/p2pkh.d.ts similarity index 100% rename from types/payments/p2pkh.d.ts rename to src/payments/p2pkh.d.ts diff --git a/src/payments/p2pkh.js b/src/payments/p2pkh.js index 94c801f..8edc8ba 100644 --- a/src/payments/p2pkh.js +++ b/src/payments/p2pkh.js @@ -1,28 +1,28 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.p2pkh = void 0; const bcrypto = require('../crypto'); const networks_1 = require('../networks'); const bscript = require('../script'); +const types_1 = require('../types'); const lazy = require('./lazy'); -const typef = require('typeforce'); -const OPS = bscript.OPS; -const ecc = require('tiny-secp256k1'); const bs58check = require('bs58check'); +const OPS = bscript.OPS; // input: {signature} {pubkey} // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG function p2pkh(a, opts) { if (!a.address && !a.hash && !a.output && !a.pubkey && !a.input) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); - typef( + (0, types_1.typeforce)( { - network: typef.maybe(typef.Object), - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(20)), - output: typef.maybe(typef.BufferN(25)), - pubkey: typef.maybe(ecc.isPoint), - signature: typef.maybe(bscript.isCanonicalScriptSignature), - input: typef.maybe(typef.Buffer), + network: types_1.typeforce.maybe(types_1.typeforce.Object), + address: types_1.typeforce.maybe(types_1.typeforce.String), + hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)), + output: types_1.typeforce.maybe(types_1.typeforce.BufferN(25)), + pubkey: types_1.typeforce.maybe(types_1.isPoint), + signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature), + input: types_1.typeforce.maybe(types_1.typeforce.Buffer), }, a, ); @@ -116,7 +116,7 @@ function p2pkh(a, opts) { if (chunks.length !== 2) throw new TypeError('Input is invalid'); if (!bscript.isCanonicalScriptSignature(chunks[0])) throw new TypeError('Input has invalid signature'); - if (!ecc.isPoint(chunks[1])) + if (!(0, types_1.isPoint)(chunks[1])) throw new TypeError('Input has invalid pubkey'); if (a.signature && !a.signature.equals(chunks[0])) throw new TypeError('Signature mismatch'); diff --git a/types/payments/p2sh.d.ts b/src/payments/p2sh.d.ts similarity index 100% rename from types/payments/p2sh.d.ts rename to src/payments/p2sh.d.ts diff --git a/src/payments/p2sh.js b/src/payments/p2sh.js index 0f46403..8710bf1 100644 --- a/src/payments/p2sh.js +++ b/src/payments/p2sh.js @@ -1,12 +1,13 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.p2sh = void 0; const bcrypto = require('../crypto'); const networks_1 = require('../networks'); const bscript = require('../script'); +const types_1 = require('../types'); const lazy = require('./lazy'); -const typef = require('typeforce'); -const OPS = bscript.OPS; const bs58check = require('bs58check'); +const OPS = bscript.OPS; function stacksEqual(a, b) { if (a.length !== b.length) return false; return a.every((x, i) => { @@ -20,20 +21,24 @@ function p2sh(a, opts) { if (!a.address && !a.hash && !a.output && !a.redeem && !a.input) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); - typef( + (0, types_1.typeforce)( { - network: typef.maybe(typef.Object), - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(20)), - output: typef.maybe(typef.BufferN(23)), - redeem: typef.maybe({ - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - input: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)), + network: types_1.typeforce.maybe(types_1.typeforce.Object), + address: types_1.typeforce.maybe(types_1.typeforce.String), + hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)), + output: types_1.typeforce.maybe(types_1.typeforce.BufferN(23)), + redeem: types_1.typeforce.maybe({ + network: types_1.typeforce.maybe(types_1.typeforce.Object), + output: types_1.typeforce.maybe(types_1.typeforce.Buffer), + input: types_1.typeforce.maybe(types_1.typeforce.Buffer), + witness: types_1.typeforce.maybe( + types_1.typeforce.arrayOf(types_1.typeforce.Buffer), + ), }), - input: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)), + input: types_1.typeforce.maybe(types_1.typeforce.Buffer), + witness: types_1.typeforce.maybe( + types_1.typeforce.arrayOf(types_1.typeforce.Buffer), + ), }, a, ); diff --git a/types/payments/p2wpkh.d.ts b/src/payments/p2wpkh.d.ts similarity index 100% rename from types/payments/p2wpkh.d.ts rename to src/payments/p2wpkh.d.ts diff --git a/src/payments/p2wpkh.js b/src/payments/p2wpkh.js index 9e9e685..168e08f 100644 --- a/src/payments/p2wpkh.js +++ b/src/payments/p2wpkh.js @@ -1,13 +1,13 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.p2wpkh = void 0; const bcrypto = require('../crypto'); const networks_1 = require('../networks'); const bscript = require('../script'); +const types_1 = require('../types'); const lazy = require('./lazy'); -const typef = require('typeforce'); +const bech32_1 = require('bech32'); const OPS = bscript.OPS; -const ecc = require('tiny-secp256k1'); -const { bech32 } = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); // witness: {signature} {pubKey} // input: <> @@ -16,23 +16,25 @@ function p2wpkh(a, opts) { if (!a.address && !a.hash && !a.output && !a.pubkey && !a.witness) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); - typef( + (0, types_1.typeforce)( { - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(20)), - input: typef.maybe(typef.BufferN(0)), - network: typef.maybe(typef.Object), - output: typef.maybe(typef.BufferN(22)), - pubkey: typef.maybe(ecc.isPoint), - signature: typef.maybe(bscript.isCanonicalScriptSignature), - witness: typef.maybe(typef.arrayOf(typef.Buffer)), + address: types_1.typeforce.maybe(types_1.typeforce.String), + hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(20)), + input: types_1.typeforce.maybe(types_1.typeforce.BufferN(0)), + network: types_1.typeforce.maybe(types_1.typeforce.Object), + output: types_1.typeforce.maybe(types_1.typeforce.BufferN(22)), + pubkey: types_1.typeforce.maybe(types_1.isPoint), + signature: types_1.typeforce.maybe(bscript.isCanonicalScriptSignature), + witness: types_1.typeforce.maybe( + types_1.typeforce.arrayOf(types_1.typeforce.Buffer), + ), }, a, ); const _address = lazy.value(() => { - const result = bech32.decode(a.address); + const result = bech32_1.bech32.decode(a.address); const version = result.words.shift(); - const data = bech32.fromWords(result.words); + const data = bech32_1.bech32.fromWords(result.words); return { version, prefix: result.prefix, @@ -43,9 +45,9 @@ function p2wpkh(a, opts) { const o = { name: 'p2wpkh', network }; lazy.prop(o, 'address', () => { if (!o.hash) return; - const words = bech32.toWords(o.hash); + const words = bech32_1.bech32.toWords(o.hash); words.unshift(0x00); - return bech32.encode(network.bech32, words); + return bech32_1.bech32.encode(network.bech32, words); }); lazy.prop(o, 'hash', () => { if (a.output) return a.output.slice(2, 22); @@ -107,14 +109,14 @@ function p2wpkh(a, opts) { if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch'); else hash = pkh; - if (!ecc.isPoint(a.pubkey) || a.pubkey.length !== 33) + if (!(0, types_1.isPoint)(a.pubkey) || a.pubkey.length !== 33) throw new TypeError('Invalid pubkey for p2wpkh'); } if (a.witness) { if (a.witness.length !== 2) throw new TypeError('Witness is invalid'); if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Witness has invalid signature'); - if (!ecc.isPoint(a.witness[1]) || a.witness[1].length !== 33) + if (!(0, types_1.isPoint)(a.witness[1]) || a.witness[1].length !== 33) throw new TypeError('Witness has invalid pubkey'); if (a.signature && !a.signature.equals(a.witness[0])) throw new TypeError('Signature mismatch'); diff --git a/types/payments/p2wsh.d.ts b/src/payments/p2wsh.d.ts similarity index 100% rename from types/payments/p2wsh.d.ts rename to src/payments/p2wsh.d.ts diff --git a/src/payments/p2wsh.js b/src/payments/p2wsh.js index 20432a9..66ee1da 100644 --- a/src/payments/p2wsh.js +++ b/src/payments/p2wsh.js @@ -1,13 +1,13 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.p2wsh = void 0; const bcrypto = require('../crypto'); const networks_1 = require('../networks'); const bscript = require('../script'); +const types_1 = require('../types'); const lazy = require('./lazy'); -const typef = require('typeforce'); +const bech32_1 = require('bech32'); const OPS = bscript.OPS; -const ecc = require('tiny-secp256k1'); -const { bech32 } = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); function stacksEqual(a, b) { if (a.length !== b.length) return false; @@ -20,7 +20,7 @@ function chunkHasUncompressedPubkey(chunk) { Buffer.isBuffer(chunk) && chunk.length === 65 && chunk[0] === 0x04 && - ecc.isPoint(chunk) + (0, types_1.isPoint)(chunk) ) { return true; } else { @@ -34,27 +34,31 @@ function p2wsh(a, opts) { if (!a.address && !a.hash && !a.output && !a.redeem && !a.witness) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); - typef( + (0, types_1.typeforce)( { - network: typef.maybe(typef.Object), - address: typef.maybe(typef.String), - hash: typef.maybe(typef.BufferN(32)), - output: typef.maybe(typef.BufferN(34)), - redeem: typef.maybe({ - input: typef.maybe(typef.Buffer), - network: typef.maybe(typef.Object), - output: typef.maybe(typef.Buffer), - witness: typef.maybe(typef.arrayOf(typef.Buffer)), + network: types_1.typeforce.maybe(types_1.typeforce.Object), + address: types_1.typeforce.maybe(types_1.typeforce.String), + hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(32)), + output: types_1.typeforce.maybe(types_1.typeforce.BufferN(34)), + redeem: types_1.typeforce.maybe({ + input: types_1.typeforce.maybe(types_1.typeforce.Buffer), + network: types_1.typeforce.maybe(types_1.typeforce.Object), + output: types_1.typeforce.maybe(types_1.typeforce.Buffer), + witness: types_1.typeforce.maybe( + types_1.typeforce.arrayOf(types_1.typeforce.Buffer), + ), }), - input: typef.maybe(typef.BufferN(0)), - witness: typef.maybe(typef.arrayOf(typef.Buffer)), + input: types_1.typeforce.maybe(types_1.typeforce.BufferN(0)), + witness: types_1.typeforce.maybe( + types_1.typeforce.arrayOf(types_1.typeforce.Buffer), + ), }, a, ); const _address = lazy.value(() => { - const result = bech32.decode(a.address); + const result = bech32_1.bech32.decode(a.address); const version = result.words.shift(); - const data = bech32.fromWords(result.words); + const data = bech32_1.bech32.fromWords(result.words); return { version, prefix: result.prefix, @@ -71,9 +75,9 @@ function p2wsh(a, opts) { const o = { network }; lazy.prop(o, 'address', () => { if (!o.hash) return; - const words = bech32.toWords(o.hash); + const words = bech32_1.bech32.toWords(o.hash); words.unshift(0x00); - return bech32.encode(network.bech32, words); + return bech32_1.bech32.encode(network.bech32, words); }); lazy.prop(o, 'hash', () => { if (a.output) return a.output.slice(2); diff --git a/types/psbt.d.ts b/src/psbt.d.ts similarity index 88% rename from types/psbt.d.ts rename to src/psbt.d.ts index e7a79eb..8603a69 100644 --- a/types/psbt.d.ts +++ b/src/psbt.d.ts @@ -1,6 +1,6 @@ +/// <reference types="node" /> import { Psbt as PsbtBase } from 'bip174'; import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate } from 'bip174/src/lib/interfaces'; -import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; export interface TransactionInput { @@ -18,6 +18,7 @@ export interface TransactionOutput { export interface PsbtTxOutput extends TransactionOutput { address: string | undefined; } +export declare type ValidateSigFunction = (pubkey: Buffer, msghash: Buffer, signature: Buffer) => boolean; /** * Psbt class can parse and generate a PSBT binary based off of the BIP174. * There are 6 roles that this class fulfills. (Explained in BIP174) @@ -58,11 +59,13 @@ export declare class Psbt { private __CACHE; private opts; constructor(opts?: PsbtOptsOptional, data?: PsbtBase); - readonly inputCount: number; - version: number; - locktime: number; - readonly txInputs: PsbtTxInput[]; - readonly txOutputs: PsbtTxOutput[]; + get inputCount(): number; + get version(): number; + set version(version: number); + get locktime(): number; + set locktime(locktime: number); + get txInputs(): PsbtTxInput[]; + get txOutputs(): PsbtTxOutput[]; combine(...those: Psbt[]): this; clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void; @@ -83,8 +86,8 @@ export declare class Psbt { inputHasHDKey(inputIndex: number, root: HDSigner): boolean; outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean; outputHasHDKey(outputIndex: number, root: HDSigner): boolean; - validateSignaturesOfAllInputs(): boolean; - validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean; + validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean; + validateSignaturesOfInput(inputIndex: number, validator: ValidateSigFunction, pubkey?: Buffer): boolean; signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this; signAllInputsHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>; signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this; @@ -129,7 +132,7 @@ interface HDSignerBase { */ fingerprint: Buffer; } -interface HDSigner extends HDSignerBase { +export interface HDSigner extends HDSignerBase { /** * The path string must match /^m(\/\d+'?)+$/ * ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations @@ -144,10 +147,22 @@ interface HDSigner extends HDSignerBase { /** * Same as above but with async sign method */ -interface HDSignerAsync extends HDSignerBase { +export interface HDSignerAsync extends HDSignerBase { derivePath(path: string): HDSignerAsync; sign(hash: Buffer): Promise<Buffer>; } +export interface Signer { + publicKey: Buffer; + network?: any; + sign(hash: Buffer, lowR?: boolean): Buffer; + getPublicKey?(): Buffer; +} +export interface SignerAsync { + publicKey: Buffer; + network?: any; + sign(hash: Buffer, lowR?: boolean): Promise<Buffer>; + getPublicKey?(): Buffer; +} /** * This function must do two things: * 1. Check if the `input` can be finalized. If it can not be finalized, throw. diff --git a/src/psbt.js b/src/psbt.js index 6381669..84aa5b8 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -1,12 +1,12 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.Psbt = void 0; const bip174_1 = require('bip174'); const varuint = require('bip174/src/lib/converter/varint'); const utils_1 = require('bip174/src/lib/utils'); const address_1 = require('./address'); const bufferutils_1 = require('./bufferutils'); const crypto_1 = require('./crypto'); -const ecpair_1 = require('./ecpair'); const networks_1 = require('./networks'); const payments = require('./payments'); const bscript = require('./script'); @@ -25,7 +25,7 @@ const DEFAULT_OPTS = { * THIS IS NOT TO BE RELIED ON. * It is only here as a last ditch effort to prevent sending a 500 BTC fee etc. */ - maximumFeeRate: 5000, + maximumFeeRate: 5000, // satoshi per byte }; /** * Psbt class can parse and generate a PSBT binary based off of the BIP174. @@ -60,6 +60,23 @@ const DEFAULT_OPTS = { * Transaction object. Such as fee rate not being larger than maximumFeeRate etc. */ class Psbt { + data; + static fromBase64(data, opts = {}) { + const buffer = Buffer.from(data, 'base64'); + return this.fromBuffer(buffer, opts); + } + static fromHex(data, opts = {}) { + const buffer = Buffer.from(data, 'hex'); + return this.fromBuffer(buffer, opts); + } + static fromBuffer(buffer, opts = {}) { + const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer); + const psbt = new Psbt(opts, psbtBase); + checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE); + return psbt; + } + __CACHE; + opts; constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) { this.data = data; // set defaults @@ -69,6 +86,8 @@ class Psbt { __NON_WITNESS_UTXO_BUF_CACHE: [], __TX_IN_CACHE: {}, __TX: this.data.globalMap.unsignedTx.tx, + // Psbt's predecesor (TransactionBuilder - now removed) behavior + // was to not confirm input values before signing. // Even though we highly encourage people to get // the full parent transaction to verify values, the ability to // sign non-segwit inputs without the full transaction was often @@ -87,20 +106,6 @@ class Psbt { dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } - static fromBase64(data, opts = {}) { - const buffer = Buffer.from(data, 'base64'); - return this.fromBuffer(buffer, opts); - } - static fromHex(data, opts = {}) { - const buffer = Buffer.from(data, 'hex'); - return this.fromBuffer(buffer, opts); - } - static fromBuffer(buffer, opts = {}) { - const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer); - const psbt = new Psbt(opts, psbtBase); - checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE); - return psbt; - } get inputCount() { return this.data.inputs.length; } @@ -118,7 +123,7 @@ class Psbt { } get txInputs() { return this.__CACHE.__TX.ins.map(input => ({ - hash: bufferutils_1.cloneBuffer(input.hash), + hash: (0, bufferutils_1.cloneBuffer)(input.hash), index: input.index, sequence: input.sequence, })); @@ -127,10 +132,13 @@ class Psbt { return this.__CACHE.__TX.outs.map(output => { let address; try { - address = address_1.fromOutputScript(output.script, this.opts.network); + address = (0, address_1.fromOutputScript)( + output.script, + this.opts.network, + ); } catch (_) {} return { - script: bufferutils_1.cloneBuffer(output.script), + script: (0, bufferutils_1.cloneBuffer)(output.script), value: output.value, address, }; @@ -229,7 +237,7 @@ class Psbt { const { address } = outputData; if (typeof address === 'string') { const { network } = this.opts; - const script = address_1.toOutputScript(address, network); + const script = (0, address_1.toOutputScript)(address, network); outputData = Object.assign(outputData, { script }); } const c = this.__CACHE; @@ -262,12 +270,12 @@ class Psbt { return getTxCacheValue('__FEE', 'fee', this.data.inputs, this.__CACHE); } finalizeAllInputs() { - utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one + (0, utils_1.checkForInput)(this.data.inputs, 0); // making sure we have at least one range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx)); return this; } finalizeInput(inputIndex, finalScriptsFunc = getFinalScripts) { - const input = utils_1.checkForInput(this.data.inputs, inputIndex); + const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, @@ -292,7 +300,7 @@ class Psbt { return this; } getInputType(inputIndex) { - const input = utils_1.checkForInput(this.data.inputs, inputIndex); + const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); const script = getScriptFromUtxo(inputIndex, input, this.__CACHE); const result = getMeaningfulScript( script, @@ -307,39 +315,41 @@ class Psbt { return type + mainType; } inputHasPubkey(inputIndex, pubkey) { - const input = utils_1.checkForInput(this.data.inputs, inputIndex); + const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE); } inputHasHDKey(inputIndex, root) { - const input = utils_1.checkForInput(this.data.inputs, inputIndex); + const input = (0, utils_1.checkForInput)(this.data.inputs, inputIndex); const derivationIsMine = bip32DerivationIsMine(root); return ( !!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine) ); } outputHasPubkey(outputIndex, pubkey) { - const output = utils_1.checkForOutput(this.data.outputs, outputIndex); + const output = (0, utils_1.checkForOutput)(this.data.outputs, outputIndex); return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE); } outputHasHDKey(outputIndex, root) { - const output = utils_1.checkForOutput(this.data.outputs, outputIndex); + const output = (0, utils_1.checkForOutput)(this.data.outputs, outputIndex); const derivationIsMine = bip32DerivationIsMine(root); return ( !!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine) ); } - validateSignaturesOfAllInputs() { - utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one + validateSignaturesOfAllInputs(validator) { + (0, utils_1.checkForInput)(this.data.inputs, 0); // making sure we have at least one const results = range(this.data.inputs.length).map(idx => - this.validateSignaturesOfInput(idx), + this.validateSignaturesOfInput(idx, validator), ); return results.reduce((final, res) => res === true && final, true); } - validateSignaturesOfInput(inputIndex, pubkey) { + validateSignaturesOfInput(inputIndex, validator, pubkey) { const input = this.data.inputs[inputIndex]; const partialSig = (input || {}).partialSig; if (!input || !partialSig || partialSig.length < 1) throw new Error('No signatures to validate'); + if (typeof validator !== 'function') + throw new Error('Need validator function to validate signatures'); const mySigs = pubkey ? partialSig.filter(sig => sig.pubkey.equals(pubkey)) : partialSig; @@ -363,8 +373,7 @@ class Psbt { hashCache = hash; scriptCache = script; checkScriptForPubkey(pSig.pubkey, script, 'verify'); - const keypair = ecpair_1.fromPublicKey(pSig.pubkey); - results.push(keypair.verify(hash, sig.signature)); + results.push(validator(pSig.pubkey, hash, sig.signature)); } return results.every(res => res === true); } @@ -616,6 +625,7 @@ const transactionFromBuffer = buffer => new PsbtTransaction(buffer); * It contains a bitcoinjs-lib Transaction object. */ class PsbtTransaction { + tx; constructor(buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) { this.tx = transaction_1.Transaction.fromBuffer(buffer); checkTxEmpty(this.tx); @@ -641,7 +651,7 @@ class PsbtTransaction { } const hash = typeof input.hash === 'string' - ? bufferutils_1.reverseBuffer(Buffer.from(input.hash, 'hex')) + ? (0, bufferutils_1.reverseBuffer)(Buffer.from(input.hash, 'hex')) : input.hash; this.tx.addInput(hash, input.index, input.sequence); } @@ -684,8 +694,7 @@ function hasSigs(neededSigs, partialSig, pubkeys) { if (pubkeys) { sigs = pubkeys .map(pkey => { - const pubkey = ecpair_1.fromPublicKey(pkey, { compressed: true }) - .publicKey; + const pubkey = compressPubkey(pkey); return partialSig.find(pSig => pSig.pubkey.equals(pubkey)); }) .filter(v => !!v); @@ -816,7 +825,7 @@ function checkTxForDupeIns(tx, cache) { } function checkTxInputCache(cache, input) { const key = - bufferutils_1.reverseBuffer(Buffer.from(input.hash)).toString('hex') + + (0, bufferutils_1.reverseBuffer)(Buffer.from(input.hash)).toString('hex') + ':' + input.index; if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.'); @@ -911,7 +920,7 @@ function getHashAndSighashType( cache, sighashTypes, ) { - const input = utils_1.checkForInput(inputs, inputIndex); + const input = (0, utils_1.checkForInput)(inputs, inputIndex); const { hash, sighashType, script } = getHashForSig( inputIndex, input, @@ -997,7 +1006,8 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) { console.warn( 'Warning: Signing non-segwit inputs without the full parent transaction ' + 'means there is a chance that a miner could feed you incorrect information ' + - 'to trick you into paying large fees. You are not ' + + "to trick you into paying large fees. This behavior is the same as Psbt's predecesor " + + '(TransactionBuilder - now removed) when signing non-segwit scripts. You are not ' + 'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' + 'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' + '*********************', @@ -1094,7 +1104,7 @@ function getScriptFromInput(inputIndex, input, cache) { return res; } function getSignersFromHD(inputIndex, inputs, hdKeyPair) { - const input = utils_1.checkForInput(inputs, inputIndex); + const input = (0, utils_1.checkForInput)(inputs, inputIndex); if (!input.bip32Derivation || input.bip32Derivation.length === 0) { throw new Error('Need bip32Derivation to sign with HD'); } @@ -1321,6 +1331,15 @@ function redeemFromFinalWitnessScript(finalScript) { if (!sDecomp) return; return lastItem; } +function compressPubkey(pubkey) { + if (pubkey.length === 65) { + const parity = pubkey[64] & 1; + const newKey = pubkey.slice(0, 33); + newKey[0] = 2 | parity; + return newKey; + } + return pubkey.slice(); +} function isPubkeyLike(buf) { return buf.length === 33 && bscript.isCanonicalPubKey(buf); } @@ -1376,7 +1395,7 @@ function checkInvalidP2WSH(script) { } } function pubkeyInScript(pubkey, script) { - const pubkeyHash = crypto_1.hash160(pubkey); + const pubkeyHash = (0, crypto_1.hash160)(pubkey); const decompiled = bscript.decompile(script); if (decompiled === null) throw new Error('Unknown script error'); return decompiled.some(element => { diff --git a/src/push_data.d.ts b/src/push_data.d.ts new file mode 100644 index 0000000..07c2f91 --- /dev/null +++ b/src/push_data.d.ts @@ -0,0 +1,8 @@ +/// <reference types="node" /> +export declare function encodingLength(i: number): number; +export declare function encode(buffer: Buffer, num: number, offset: number): number; +export declare function decode(buffer: Buffer, offset: number): { + opcode: number; + number: number; + size: number; +} | null; diff --git a/src/push_data.js b/src/push_data.js new file mode 100644 index 0000000..16b6147 --- /dev/null +++ b/src/push_data.js @@ -0,0 +1,61 @@ +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.decode = exports.encode = exports.encodingLength = void 0; +const ops_1 = require('./ops'); +function encodingLength(i) { + return i < ops_1.OPS.OP_PUSHDATA1 ? 1 : i <= 0xff ? 2 : i <= 0xffff ? 3 : 5; +} +exports.encodingLength = encodingLength; +function encode(buffer, num, offset) { + const size = encodingLength(num); + // ~6 bit + if (size === 1) { + buffer.writeUInt8(num, offset); + // 8 bit + } else if (size === 2) { + buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA1, offset); + buffer.writeUInt8(num, offset + 1); + // 16 bit + } else if (size === 3) { + buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA2, offset); + buffer.writeUInt16LE(num, offset + 1); + // 32 bit + } else { + buffer.writeUInt8(ops_1.OPS.OP_PUSHDATA4, offset); + buffer.writeUInt32LE(num, offset + 1); + } + return size; +} +exports.encode = encode; +function decode(buffer, offset) { + const opcode = buffer.readUInt8(offset); + let num; + let size; + // ~6 bit + if (opcode < ops_1.OPS.OP_PUSHDATA1) { + num = opcode; + size = 1; + // 8 bit + } else if (opcode === ops_1.OPS.OP_PUSHDATA1) { + if (offset + 2 > buffer.length) return null; + num = buffer.readUInt8(offset + 1); + size = 2; + // 16 bit + } else if (opcode === ops_1.OPS.OP_PUSHDATA2) { + if (offset + 3 > buffer.length) return null; + num = buffer.readUInt16LE(offset + 1); + size = 3; + // 32 bit + } else { + if (offset + 5 > buffer.length) return null; + if (opcode !== ops_1.OPS.OP_PUSHDATA4) throw new Error('Unexpected opcode'); + num = buffer.readUInt32LE(offset + 1); + size = 5; + } + return { + opcode, + number: num, + size, + }; +} +exports.decode = decode; diff --git a/types/script.d.ts b/src/script.d.ts similarity index 90% rename from types/script.d.ts rename to src/script.d.ts index 4b04615..261ecf4 100644 --- a/types/script.d.ts +++ b/src/script.d.ts @@ -1,10 +1,9 @@ +/// <reference types="node" /> +import { OPS } from './ops'; import { Stack } from './payments'; import * as scriptNumber from './script_number'; import * as scriptSignature from './script_signature'; -export declare type OpCode = number; -export declare const OPS: { - [index: string]: number; -}; +export { OPS }; export declare function isPushOnly(value: Stack): boolean; export declare function compile(chunks: Buffer | Stack): Buffer; export declare function decompile(buffer: Buffer | Array<number | Buffer>): Array<number | Buffer> | null; diff --git a/src/script.js b/src/script.js index 39859dc..5e5e17b 100644 --- a/src/script.js +++ b/src/script.js @@ -1,21 +1,26 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.signature = exports.number = exports.isCanonicalScriptSignature = exports.isDefinedHashType = exports.isCanonicalPubKey = exports.toStack = exports.fromASM = exports.toASM = exports.decompile = exports.compile = exports.isPushOnly = exports.OPS = void 0; +const bip66 = require('./bip66'); +const ops_1 = require('./ops'); +Object.defineProperty(exports, 'OPS', { + enumerable: true, + get: function() { + return ops_1.OPS; + }, +}); +const pushdata = require('./push_data'); const scriptNumber = require('./script_number'); const scriptSignature = require('./script_signature'); const types = require('./types'); -const bip66 = require('bip66'); -const ecc = require('tiny-secp256k1'); -const pushdata = require('pushdata-bitcoin'); -const typeforce = require('typeforce'); -exports.OPS = require('bitcoin-ops'); -const REVERSE_OPS = require('bitcoin-ops/map'); -const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1 +const { typeforce } = types; +const OP_INT_BASE = ops_1.OPS.OP_RESERVED; // OP_1 - 1 function isOPInt(value) { return ( types.Number(value) && - (value === exports.OPS.OP_0 || - (value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) || - value === exports.OPS.OP_1NEGATE) + (value === ops_1.OPS.OP_0 || + (value >= ops_1.OPS.OP_1 && value <= ops_1.OPS.OP_16) || + value === ops_1.OPS.OP_1NEGATE) ); } function isPushOnlyChunk(value) { @@ -26,10 +31,10 @@ function isPushOnly(value) { } exports.isPushOnly = isPushOnly; function asMinimalOP(buffer) { - if (buffer.length === 0) return exports.OPS.OP_0; + if (buffer.length === 0) return ops_1.OPS.OP_0; if (buffer.length !== 1) return; if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0]; - if (buffer[0] === 0x81) return exports.OPS.OP_1NEGATE; + if (buffer[0] === 0x81) return ops_1.OPS.OP_1NEGATE; } function chunksIsBuffer(buf) { return Buffer.isBuffer(buf); @@ -90,7 +95,7 @@ function decompile(buffer) { while (i < buffer.length) { const opcode = buffer[i]; // data chunk - if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) { + if (opcode > ops_1.OPS.OP_0 && opcode <= ops_1.OPS.OP_PUSHDATA4) { const d = pushdata.decode(buffer, i); // did reading a pushDataInt fail? if (d === null) return null; @@ -128,7 +133,7 @@ function toASM(chunks) { chunk = op; } // opcode! - return REVERSE_OPS[chunk]; + return ops_1.REVERSE_OPS[chunk]; }) .join(' '); } @@ -138,7 +143,7 @@ function fromASM(asm) { return compile( asm.split(' ').map(chunkStr => { // opcode? - if (exports.OPS[chunkStr] !== undefined) return exports.OPS[chunkStr]; + if (ops_1.OPS[chunkStr] !== undefined) return ops_1.OPS[chunkStr]; typeforce(types.Hex, chunkStr); // data! return Buffer.from(chunkStr, 'hex'); @@ -151,13 +156,13 @@ function toStack(chunks) { typeforce(isPushOnly, chunks); return chunks.map(op => { if (singleChunkIsBuffer(op)) return op; - if (op === exports.OPS.OP_0) return Buffer.allocUnsafe(0); + if (op === ops_1.OPS.OP_0) return Buffer.allocUnsafe(0); return scriptNumber.encode(op - OP_INT_BASE); }); } exports.toStack = toStack; function isCanonicalPubKey(buffer) { - return ecc.isPoint(buffer); + return types.isPoint(buffer); } exports.isCanonicalPubKey = isCanonicalPubKey; function isDefinedHashType(hashType) { diff --git a/types/script_number.d.ts b/src/script_number.d.ts similarity index 83% rename from types/script_number.d.ts rename to src/script_number.d.ts index cf535fc..015bb89 100644 --- a/types/script_number.d.ts +++ b/src/script_number.d.ts @@ -1,2 +1,3 @@ +/// <reference types="node" /> export declare function decode(buffer: Buffer, maxLength?: number, minimal?: boolean): number; export declare function encode(_number: number): Buffer; diff --git a/src/script_number.js b/src/script_number.js index 3f313af..8220a10 100644 --- a/src/script_number.js +++ b/src/script_number.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.encode = exports.decode = void 0; function decode(buffer, maxLength, minimal) { maxLength = maxLength || 4; minimal = minimal === undefined ? true : minimal; diff --git a/types/script_signature.d.ts b/src/script_signature.d.ts similarity index 88% rename from types/script_signature.d.ts rename to src/script_signature.d.ts index fbf18d5..2057dd9 100644 --- a/types/script_signature.d.ts +++ b/src/script_signature.d.ts @@ -1,3 +1,4 @@ +/// <reference types="node" /> interface ScriptSignature { signature: Buffer; hashType: number; diff --git a/src/script_signature.js b/src/script_signature.js index fb52fe9..638e5f2 100644 --- a/src/script_signature.js +++ b/src/script_signature.js @@ -1,8 +1,9 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.encode = exports.decode = void 0; +const bip66 = require('./bip66'); const types = require('./types'); -const bip66 = require('bip66'); -const typeforce = require('typeforce'); +const { typeforce } = types; const ZERO = Buffer.alloc(1, 0); function toDER(x) { let i = 0; diff --git a/types/transaction.d.ts b/src/transaction.d.ts similarity index 98% rename from types/transaction.d.ts rename to src/transaction.d.ts index 6846ea5..c4de954 100644 --- a/types/transaction.d.ts +++ b/src/transaction.d.ts @@ -1,3 +1,4 @@ +/// <reference types="node" /> export interface Output { script: Buffer; value: number; diff --git a/src/transaction.js b/src/transaction.js index 8db57c3..e4ebf6f 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,20 +1,20 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); +exports.Transaction = void 0; const bufferutils_1 = require('./bufferutils'); const bcrypto = require('./crypto'); const bscript = require('./script'); const script_1 = require('./script'); const types = require('./types'); -const typeforce = require('typeforce'); -const varuint = require('varuint-bitcoin'); +const { typeforce } = types; function varSliceSize(someScript) { const length = someScript.length; - return varuint.encodingLength(length) + length; + return bufferutils_1.varuint.encodingLength(length) + length; } function vectorSize(someVector) { const length = someVector.length; return ( - varuint.encodingLength(length) + + bufferutils_1.varuint.encodingLength(length) + someVector.reduce((sum, witness) => { return sum + varSliceSize(witness); }, 0) @@ -39,12 +39,13 @@ function isOutput(out) { return out.value !== undefined; } class Transaction { - constructor() { - this.version = 1; - this.locktime = 0; - this.ins = []; - this.outs = []; - } + static DEFAULT_SEQUENCE = 0xffffffff; + static SIGHASH_ALL = 0x01; + static SIGHASH_NONE = 0x02; + static SIGHASH_SINGLE = 0x03; + static SIGHASH_ANYONECANPAY = 0x80; + static ADVANCED_TRANSACTION_MARKER = 0x00; + static ADVANCED_TRANSACTION_FLAG = 0x01; static fromBuffer(buffer, _NO_STRICT) { const bufferReader = new bufferutils_1.BufferReader(buffer); const tx = new Transaction(); @@ -101,6 +102,10 @@ class Transaction { } return true; } + version = 1; + locktime = 0; + ins = []; + outs = []; isCoinbase() { return ( this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) @@ -157,8 +162,8 @@ class Transaction { const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses(); return ( (hasWitnesses ? 10 : 8) + - varuint.encodingLength(this.ins.length) + - varuint.encodingLength(this.outs.length) + + bufferutils_1.varuint.encodingLength(this.ins.length) + + bufferutils_1.varuint.encodingLength(this.outs.length) + this.ins.reduce((sum, input) => { return sum + 40 + varSliceSize(input.script); }, 0) + @@ -336,7 +341,9 @@ class Transaction { } getId() { // transaction hash's are displayed in reverse order - return bufferutils_1.reverseBuffer(this.getHash(false)).toString('hex'); + return (0, bufferutils_1.reverseBuffer)(this.getHash(false)).toString( + 'hex', + ); } toBuffer(buffer, initialOffset) { return this.__toBuffer(buffer, initialOffset, true); @@ -392,11 +399,4 @@ class Transaction { return buffer; } } -Transaction.DEFAULT_SEQUENCE = 0xffffffff; -Transaction.SIGHASH_ALL = 0x01; -Transaction.SIGHASH_NONE = 0x02; -Transaction.SIGHASH_SINGLE = 0x03; -Transaction.SIGHASH_ANYONECANPAY = 0x80; -Transaction.ADVANCED_TRANSACTION_MARKER = 0x00; -Transaction.ADVANCED_TRANSACTION_FLAG = 0x01; exports.Transaction = Transaction; diff --git a/types/types.d.ts b/src/types.d.ts similarity index 86% rename from types/types.d.ts rename to src/types.d.ts index e7c588d..5a8505d 100644 --- a/types/types.d.ts +++ b/src/types.d.ts @@ -1,3 +1,6 @@ +/// <reference types="node" /> +export declare const typeforce: any; +export declare function isPoint(p: Buffer | number | undefined | null): boolean; export declare function UInt31(value: number): boolean; export declare function BIP32Path(value: string): boolean; export declare namespace BIP32Path { diff --git a/src/types.js b/src/types.js index 8bcee2c..a6d1efa 100644 --- a/src/types.js +++ b/src/types.js @@ -1,13 +1,39 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); -const typeforce = require('typeforce'); +exports.oneOf = exports.Null = exports.BufferN = exports.Function = exports.UInt32 = exports.UInt8 = exports.tuple = exports.maybe = exports.Hex = exports.Buffer = exports.String = exports.Boolean = exports.Array = exports.Number = exports.Hash256bit = exports.Hash160bit = exports.Buffer256bit = exports.Network = exports.ECPoint = exports.Satoshi = exports.Signer = exports.BIP32Path = exports.UInt31 = exports.isPoint = exports.typeforce = void 0; +const buffer_1 = require('buffer'); +exports.typeforce = require('typeforce'); +const ZERO32 = buffer_1.Buffer.alloc(32, 0); +const EC_P = buffer_1.Buffer.from( + 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', + 'hex', +); +function isPoint(p) { + if (!buffer_1.Buffer.isBuffer(p)) return false; + if (p.length < 33) return false; + const t = p[0]; + const x = p.slice(1, 33); + if (x.compare(ZERO32) === 0) return false; + if (x.compare(EC_P) >= 0) return false; + if ((t === 0x02 || t === 0x03) && p.length === 33) { + return true; + } + const y = p.slice(33); + if (y.compare(ZERO32) === 0) return false; + if (y.compare(EC_P) >= 0) return false; + if (t === 0x04 && p.length === 65) return true; + return false; +} +exports.isPoint = isPoint; const UINT31_MAX = Math.pow(2, 31) - 1; function UInt31(value) { - return typeforce.UInt32(value) && value <= UINT31_MAX; + return exports.typeforce.UInt32(value) && value <= UINT31_MAX; } exports.UInt31 = UInt31; function BIP32Path(value) { - return typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/); + return ( + exports.typeforce.String(value) && !!value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) + ); } exports.BIP32Path = BIP32Path; BIP32Path.toJSON = () => { @@ -15,7 +41,7 @@ BIP32Path.toJSON = () => { }; function Signer(obj) { return ( - (typeforce.Buffer(obj.publicKey) || + (exports.typeforce.Buffer(obj.publicKey) || typeof obj.getPublicKey === 'function') && typeof obj.sign === 'function' ); @@ -23,36 +49,39 @@ function Signer(obj) { exports.Signer = Signer; const SATOSHI_MAX = 21 * 1e14; function Satoshi(value) { - return typeforce.UInt53(value) && value <= SATOSHI_MAX; + return exports.typeforce.UInt53(value) && value <= SATOSHI_MAX; } exports.Satoshi = Satoshi; // external dependent types -exports.ECPoint = typeforce.quacksLike('Point'); +exports.ECPoint = exports.typeforce.quacksLike('Point'); // exposed, external API -exports.Network = typeforce.compile({ - messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), +exports.Network = exports.typeforce.compile({ + messagePrefix: exports.typeforce.oneOf( + exports.typeforce.Buffer, + exports.typeforce.String, + ), bip32: { - public: typeforce.UInt32, - private: typeforce.UInt32, + public: exports.typeforce.UInt32, + private: exports.typeforce.UInt32, }, - pubKeyHash: typeforce.UInt8, - scriptHash: typeforce.UInt8, - wif: typeforce.UInt8, + pubKeyHash: exports.typeforce.UInt8, + scriptHash: exports.typeforce.UInt8, + wif: exports.typeforce.UInt8, }); -exports.Buffer256bit = typeforce.BufferN(32); -exports.Hash160bit = typeforce.BufferN(20); -exports.Hash256bit = typeforce.BufferN(32); -exports.Number = typeforce.Number; // tslint:disable-line variable-name -exports.Array = typeforce.Array; -exports.Boolean = typeforce.Boolean; // tslint:disable-line variable-name -exports.String = typeforce.String; // tslint:disable-line variable-name -exports.Buffer = typeforce.Buffer; -exports.Hex = typeforce.Hex; -exports.maybe = typeforce.maybe; -exports.tuple = typeforce.tuple; -exports.UInt8 = typeforce.UInt8; -exports.UInt32 = typeforce.UInt32; -exports.Function = typeforce.Function; -exports.BufferN = typeforce.BufferN; -exports.Null = typeforce.Null; -exports.oneOf = typeforce.oneOf; +exports.Buffer256bit = exports.typeforce.BufferN(32); +exports.Hash160bit = exports.typeforce.BufferN(20); +exports.Hash256bit = exports.typeforce.BufferN(32); +exports.Number = exports.typeforce.Number; // tslint:disable-line variable-name +exports.Array = exports.typeforce.Array; +exports.Boolean = exports.typeforce.Boolean; // tslint:disable-line variable-name +exports.String = exports.typeforce.String; // tslint:disable-line variable-name +exports.Buffer = exports.typeforce.Buffer; +exports.Hex = exports.typeforce.Hex; +exports.maybe = exports.typeforce.maybe; +exports.tuple = exports.typeforce.tuple; +exports.UInt8 = exports.typeforce.UInt8; +exports.UInt32 = exports.typeforce.UInt32; +exports.Function = exports.typeforce.Function; +exports.BufferN = exports.typeforce.BufferN; +exports.Null = exports.typeforce.Null; +exports.oneOf = exports.typeforce.oneOf; diff --git a/test/bitcoin.core.spec.ts b/test/bitcoin.core.spec.ts index 0263266..9040416 100644 --- a/test/bitcoin.core.spec.ts +++ b/test/bitcoin.core.spec.ts @@ -88,49 +88,6 @@ describe('Bitcoin-core', () => { }); }); - // base58KeysValid - describe('ECPair', () => { - base58KeysValid.forEach(f => { - const strng = f[0] as string; - const hex = f[1]; - const params = f[2] as any; - - if (!params.isPrivkey) return; - - const network = params.isTestnet - ? bitcoin.networks.testnet - : bitcoin.networks.bitcoin; - const keyPair = bitcoin.ECPair.fromWIF(strng, network); - - it('fromWIF imports ' + strng, () => { - assert.strictEqual(keyPair.privateKey!.toString('hex'), hex); - assert.strictEqual(keyPair.compressed, params.isCompressed); - }); - - it('toWIF exports ' + hex + ' to ' + strng, () => { - assert.strictEqual(keyPair.toWIF(), strng); - }); - }); - }); - - // base58KeysInvalid - describe('ECPair.fromWIF', () => { - const allowedNetworks = [ - bitcoin.networks.bitcoin, - bitcoin.networks.testnet, - ]; - - base58KeysInvalid.forEach(f => { - const strng = f[0]; - - it('throws on ' + strng, () => { - assert.throws(() => { - bitcoin.ECPair.fromWIF(strng, allowedNetworks); - }, /(Invalid|Unknown) (checksum|compression flag|network version|WIF length)/); - }); - }); - }); - describe('Block.fromHex', () => { blocksValid.forEach(f => { it('can parse ' + f.id, () => { diff --git a/test/ecpair.spec.ts b/test/ecpair.spec.ts deleted file mode 100644 index 0b54ecc..0000000 --- a/test/ecpair.spec.ts +++ /dev/null @@ -1,341 +0,0 @@ -import * as assert from 'assert'; -import { beforeEach, describe, it } from 'mocha'; -import * as proxyquire from 'proxyquire'; -import { ECPair, ECPairInterface, networks as NETWORKS } from '..'; -import * as fixtures from './fixtures/ecpair.json'; -const hoodwink = require('hoodwink'); -const tinysecp = require('tiny-secp256k1'); - -const NETWORKS_LIST = Object.values(NETWORKS); -const ZERO = Buffer.alloc(32, 0); -const ONE = Buffer.from( - '0000000000000000000000000000000000000000000000000000000000000001', - 'hex', -); -const GROUP_ORDER = Buffer.from( - 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', - 'hex', -); -const GROUP_ORDER_LESS_1 = Buffer.from( - 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', - 'hex', -); - -describe('ECPair', () => { - describe('getPublicKey', () => { - let keyPair: ECPairInterface; - - beforeEach(() => { - keyPair = ECPair.fromPrivateKey(ONE); - }); - - it( - 'calls pointFromScalar lazily', - hoodwink(() => { - assert.strictEqual((keyPair as any).__Q, undefined); - - // .publicKey forces the memoization - assert.strictEqual( - keyPair.publicKey.toString('hex'), - '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', - ); - assert.strictEqual( - (keyPair as any).__Q.toString('hex'), - '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', - ); - }), - ); - }); - - describe('fromPrivateKey', () => { - it('defaults to compressed', () => { - const keyPair = ECPair.fromPrivateKey(ONE); - - assert.strictEqual(keyPair.compressed, true); - }); - - it('supports the uncompressed option', () => { - const keyPair = ECPair.fromPrivateKey(ONE, { - compressed: false, - }); - - assert.strictEqual(keyPair.compressed, false); - }); - - it('supports the network option', () => { - const keyPair = ECPair.fromPrivateKey(ONE, { - compressed: false, - network: NETWORKS.testnet, - }); - - assert.strictEqual(keyPair.network, NETWORKS.testnet); - }); - - fixtures.valid.forEach(f => { - it('derives public key for ' + f.WIF, () => { - const d = Buffer.from(f.d, 'hex'); - const keyPair = ECPair.fromPrivateKey(d, { - compressed: f.compressed, - }); - - assert.strictEqual(keyPair.publicKey.toString('hex'), f.Q); - }); - }); - - fixtures.invalid.fromPrivateKey.forEach(f => { - it('throws ' + f.exception, () => { - const d = Buffer.from(f.d, 'hex'); - assert.throws(() => { - ECPair.fromPrivateKey(d, (f as any).options); - }, new RegExp(f.exception)); - }); - }); - }); - - describe('fromPublicKey', () => { - fixtures.invalid.fromPublicKey.forEach(f => { - it('throws ' + f.exception, () => { - const Q = Buffer.from(f.Q, 'hex'); - assert.throws(() => { - ECPair.fromPublicKey(Q, (f as any).options); - }, new RegExp(f.exception)); - }); - }); - }); - - describe('fromWIF', () => { - fixtures.valid.forEach(f => { - it('imports ' + f.WIF + ' (' + f.network + ')', () => { - const network = (NETWORKS as any)[f.network]; - const keyPair = ECPair.fromWIF(f.WIF, network); - - assert.strictEqual(keyPair.privateKey!.toString('hex'), f.d); - assert.strictEqual(keyPair.compressed, f.compressed); - assert.strictEqual(keyPair.network, network); - }); - }); - - fixtures.valid.forEach(f => { - it('imports ' + f.WIF + ' (via list of networks)', () => { - const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST); - - assert.strictEqual(keyPair.privateKey!.toString('hex'), f.d); - assert.strictEqual(keyPair.compressed, f.compressed); - assert.strictEqual(keyPair.network, (NETWORKS as any)[f.network]); - }); - }); - - fixtures.invalid.fromWIF.forEach(f => { - it('throws on ' + f.WIF, () => { - assert.throws(() => { - const networks = f.network - ? (NETWORKS as any)[f.network] - : NETWORKS_LIST; - - ECPair.fromWIF(f.WIF, networks); - }, new RegExp(f.exception)); - }); - }); - }); - - describe('toWIF', () => { - fixtures.valid.forEach(f => { - it('exports ' + f.WIF, () => { - const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST); - const result = keyPair.toWIF(); - assert.strictEqual(result, f.WIF); - }); - }); - it('throws if no private key is found', () => { - assert.throws(() => { - const keyPair = ECPair.makeRandom(); - delete (keyPair as any).__D; - keyPair.toWIF(); - }, /Missing private key/); - }); - }); - - describe('makeRandom', () => { - const d = Buffer.alloc(32, 4); - const exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv'; - - describe('uses randombytes RNG', () => { - it('generates a ECPair', () => { - const stub = { - randombytes: (): Buffer => { - return d; - }, - }; - const ProxiedECPair = proxyquire('../src/ecpair', stub); - - const keyPair = ProxiedECPair.makeRandom(); - assert.strictEqual(keyPair.toWIF(), exWIF); - }); - }); - - it('allows a custom RNG to be used', () => { - const keyPair = ECPair.makeRandom({ - rng: (size): Buffer => { - return d.slice(0, size); - }, - }); - - assert.strictEqual(keyPair.toWIF(), exWIF); - }); - - it('retains the same defaults as ECPair constructor', () => { - const keyPair = ECPair.makeRandom(); - - assert.strictEqual(keyPair.compressed, true); - assert.strictEqual(keyPair.network, NETWORKS.bitcoin); - }); - - it('supports the options parameter', () => { - const keyPair = ECPair.makeRandom({ - compressed: false, - network: NETWORKS.testnet, - }); - - assert.strictEqual(keyPair.compressed, false); - assert.strictEqual(keyPair.network, NETWORKS.testnet); - }); - - it('throws if d is bad length', () => { - function rng(): Buffer { - return Buffer.alloc(28); - } - - assert.throws(() => { - ECPair.makeRandom({ rng }); - }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/); - }); - - it( - 'loops until d is within interval [1, n) : 1', - hoodwink(function(this: any): void { - const rng = this.stub(() => { - if (rng.calls === 0) return ZERO; // 0 - return ONE; // >0 - }, 2); - - ECPair.makeRandom({ rng }); - }), - ); - - it( - 'loops until d is within interval [1, n) : n - 1', - hoodwink(function(this: any): void { - const rng = this.stub(() => { - if (rng.calls === 0) return ZERO; // <1 - if (rng.calls === 1) return GROUP_ORDER; // >n-1 - return GROUP_ORDER_LESS_1; // n-1 - }, 3); - - ECPair.makeRandom({ rng }); - }), - ); - }); - - describe('.network', () => { - fixtures.valid.forEach(f => { - it('returns ' + f.network + ' for ' + f.WIF, () => { - const network = (NETWORKS as any)[f.network]; - const keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST); - - assert.strictEqual(keyPair.network, network); - }); - }); - }); - - describe('tinysecp wrappers', () => { - let keyPair: ECPairInterface; - let hash: Buffer; - let signature: Buffer; - - beforeEach(() => { - keyPair = ECPair.makeRandom(); - hash = ZERO; - signature = Buffer.alloc(64, 1); - }); - - describe('signing', () => { - it( - 'wraps tinysecp.sign', - hoodwink(function(this: any): void { - this.mock( - tinysecp, - 'sign', - (h: any, d: any) => { - assert.strictEqual(h, hash); - assert.strictEqual(d, keyPair.privateKey); - return signature; - }, - 1, - ); - - assert.strictEqual(keyPair.sign(hash), signature); - }), - ); - - it('throws if no private key is found', () => { - delete (keyPair as any).__D; - - assert.throws(() => { - keyPair.sign(hash); - }, /Missing private key/); - }); - }); - - describe('verify', () => { - it( - 'wraps tinysecp.verify', - hoodwink(function(this: any): void { - this.mock( - tinysecp, - 'verify', - (h: any, q: any, s: any) => { - assert.strictEqual(h, hash); - assert.strictEqual(q, keyPair.publicKey); - assert.strictEqual(s, signature); - return true; - }, - 1, - ); - - assert.strictEqual(keyPair.verify(hash, signature), true); - }), - ); - }); - }); - describe('optional low R signing', () => { - const sig = Buffer.from( - '95a6619140fca3366f1d3b013b0367c4f86e39508a50fdce' + - 'e5245fbb8bd60aa6086449e28cf15387cf9f85100bfd0838624ca96759e59f65c10a00' + - '16b86f5229', - 'hex', - ); - const sigLowR = Buffer.from( - '6a2660c226e8055afad317eeba918a304be79208d505' + - '3bc5ea4a5e4c5892b4a061c717c5284ae5202d721c0e49b4717b79966280906b1d3b52' + - '95d1fdde963c35', - 'hex', - ); - const lowRKeyPair = ECPair.fromWIF( - 'L3nThUzbAwpUiBAjR5zCu66ybXSPMr2zZ3ikp' + 'ScpTPiYTxBynfZu', - ); - const dataToSign = Buffer.from( - 'b6c5c548a7f6164c8aa7af5350901626ebd69f9ae' + '2c1ecf8871f5088ec204cfe', - 'hex', - ); - - it('signs with normal R by default', () => { - const signed = lowRKeyPair.sign(dataToSign); - assert.deepStrictEqual(sig, signed); - }); - - it('signs with low R when true is passed', () => { - const signed = lowRKeyPair.sign(dataToSign, true); - assert.deepStrictEqual(sigLowR, signed); - }); - }); -}); diff --git a/test/integration/addresses.spec.ts b/test/integration/addresses.spec.ts index d8feb7c..2b24ef5 100644 --- a/test/integration/addresses.spec.ts +++ b/test/integration/addresses.spec.ts @@ -1,4 +1,5 @@ import * as assert from 'assert'; +import { ECPair } from 'ecpair'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; @@ -10,7 +11,7 @@ describe('bitcoinjs-lib (addresses)', () => { 'can generate a random address [and support the retrieval of ' + 'transactions for that address (via 3PBP)]', async () => { - const keyPair = bitcoin.ECPair.makeRandom(); + const keyPair = ECPair.makeRandom(); const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); // bitcoin P2PKH addresses start with a '1' @@ -29,7 +30,7 @@ describe('bitcoinjs-lib (addresses)', () => { ); it('can import an address via WIF', () => { - const keyPair = bitcoin.ECPair.fromWIF( + const keyPair = ECPair.fromWIF( 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', ); const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey }); @@ -51,7 +52,7 @@ describe('bitcoinjs-lib (addresses)', () => { }); it('can generate a SegWit address', () => { - const keyPair = bitcoin.ECPair.fromWIF( + const keyPair = ECPair.fromWIF( 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', ); const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }); @@ -60,7 +61,7 @@ describe('bitcoinjs-lib (addresses)', () => { }); it('can generate a SegWit address (via P2SH)', () => { - const keyPair = bitcoin.ECPair.fromWIF( + const keyPair = ECPair.fromWIF( 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', ); const { address } = bitcoin.payments.p2sh({ @@ -103,7 +104,7 @@ describe('bitcoinjs-lib (addresses)', () => { // examples using other network information it('can generate a Testnet address', () => { - const keyPair = bitcoin.ECPair.makeRandom({ network: TESTNET }); + const keyPair = ECPair.makeRandom({ network: TESTNET }); const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: TESTNET, @@ -130,7 +131,7 @@ describe('bitcoinjs-lib (addresses)', () => { wif: 0xb0, }; - const keyPair = bitcoin.ECPair.makeRandom({ network: LITECOIN }); + const keyPair = ECPair.makeRandom({ network: LITECOIN }); const { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: LITECOIN, diff --git a/test/integration/cltv.spec.ts b/test/integration/cltv.spec.ts index d5141c7..4b2eb66 100644 --- a/test/integration/cltv.spec.ts +++ b/test/integration/cltv.spec.ts @@ -1,4 +1,5 @@ import * as assert from 'assert'; +import { ECPair } from 'ecpair'; import { before, describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; @@ -13,11 +14,11 @@ function idToHash(txid: string): Buffer { return Buffer.from(txid, 'hex').reverse(); } -const alice = bitcoin.ECPair.fromWIF( +const alice = ECPair.fromWIF( 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest, ); -const bob = bitcoin.ECPair.fromWIF( +const bob = ECPair.fromWIF( 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest, ); diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts index 6f11a90..81b0943 100644 --- a/test/integration/csv.spec.ts +++ b/test/integration/csv.spec.ts @@ -1,5 +1,6 @@ import * as assert from 'assert'; import { PsbtInput } from 'bip174/src/lib/interfaces'; +import { ECPair } from 'ecpair'; import { before, describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; @@ -15,19 +16,19 @@ function idToHash(txid: string): Buffer { return Buffer.from(txid, 'hex').reverse(); } -const alice = bitcoin.ECPair.fromWIF( +const alice = ECPair.fromWIF( 'cScfkGjbzzoeewVWmU2hYPUHeVGJRDdFt7WhmrVVGkxpmPP8BHWe', regtest, ); -const bob = bitcoin.ECPair.fromWIF( +const bob = ECPair.fromWIF( 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsLwjHXA9x', regtest, ); -const charles = bitcoin.ECPair.fromWIF( +const charles = ECPair.fromWIF( 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMSb4Ubnf', regtest, ); -const dave = bitcoin.ECPair.fromWIF( +const dave = ECPair.fromWIF( 'cMkopUXKWsEzAjfa1zApksGRwjVpJRB3831qM9W4gKZsMwS4pqnx', regtest, ); diff --git a/test/integration/payments.spec.ts b/test/integration/payments.spec.ts index 58f48f5..ea7294e 100644 --- a/test/integration/payments.spec.ts +++ b/test/integration/payments.spec.ts @@ -1,10 +1,11 @@ +import { ECPair } from 'ecpair'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; const NETWORK = regtestUtils.network; const keyPairs = [ - bitcoin.ECPair.makeRandom({ network: NETWORK }), - bitcoin.ECPair.makeRandom({ network: NETWORK }), + ECPair.makeRandom({ network: NETWORK }), + ECPair.makeRandom({ network: NETWORK }), ]; async function buildAndSign( diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts index 9c98bb4..d4788dc 100644 --- a/test/integration/transactions.spec.ts +++ b/test/integration/transactions.spec.ts @@ -1,16 +1,23 @@ import * as assert from 'assert'; import * as bip32 from 'bip32'; +import { ECPair } from 'ecpair'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; const rng = require('randombytes'); const regtest = regtestUtils.network; +const validator = ( + pubkey: Buffer, + msghash: Buffer, + signature: Buffer, +): boolean => ECPair.fromPublicKey(pubkey).verify(msghash, signature); + // See bottom of file for some helper functions used to make the payment objects needed. describe('bitcoinjs-lib (transactions with psbt)', () => { it('can create a 1-to-1 Transaction', () => { - const alice = bitcoin.ECPair.fromWIF( + const alice = ECPair.fromWIF( 'L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr', ); const psbt = new bitcoin.Psbt(); @@ -59,7 +66,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { value: 80000, }); psbt.signInput(0, alice); - psbt.validateSignaturesOfInput(0); + psbt.validateSignaturesOfInput(0, validator); psbt.finalizeAllInputs(); assert.strictEqual( psbt.extractTransaction().toHex(), @@ -147,8 +154,8 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { // Finalizer wants to check all signatures are valid before finalizing. // If the finalizer wants to check for specific pubkeys, the second arg // can be passed. See the first multisig example below. - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); - assert.strictEqual(psbt.validateSignaturesOfInput(1), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true); + assert.strictEqual(psbt.validateSignaturesOfInput(1, validator), true); // This step it new. Since we separate the signing operation and // the creation of the scriptSig and witness stack, we are able to @@ -183,7 +190,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, alice1.keys[0]); - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true); psbt.finalizeAllInputs(); // build and broadcast to the RegTest network @@ -215,13 +222,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .signInput(0, multisig.keys[0]) .signInput(0, multisig.keys[2]); - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true); assert.strictEqual( - psbt.validateSignaturesOfInput(0, multisig.keys[0].publicKey), + psbt.validateSignaturesOfInput(0, validator, multisig.keys[0].publicKey), true, ); assert.throws(() => { - psbt.validateSignaturesOfInput(0, multisig.keys[3].publicKey); + psbt.validateSignaturesOfInput(0, validator, multisig.keys[3].publicKey); }, new RegExp('No signatures for this pubkey')); psbt.finalizeAllInputs(); @@ -330,7 +337,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, p2wpkh.keys[0]); - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true); psbt.finalizeAllInputs(); const tx = psbt.extractTransaction(); @@ -398,7 +405,7 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInput(0, p2wsh.keys[0]); - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true); psbt.finalizeAllInputs(); const tx = psbt.extractTransaction(); @@ -472,13 +479,13 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { .signInput(0, p2sh.keys[2]) .signInput(0, p2sh.keys[3]); - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true); assert.strictEqual( - psbt.validateSignaturesOfInput(0, p2sh.keys[3].publicKey), + psbt.validateSignaturesOfInput(0, validator, p2sh.keys[3].publicKey), true, ); assert.throws(() => { - psbt.validateSignaturesOfInput(0, p2sh.keys[1].publicKey); + psbt.validateSignaturesOfInput(0, validator, p2sh.keys[1].publicKey); }, new RegExp('No signatures for this pubkey')); psbt.finalizeAllInputs(); @@ -534,10 +541,10 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { 'can create (and broadcast via 3PBP) a Transaction, w/ a ' + 'P2SH(P2MS(2 of 2)) input with nonWitnessUtxo', async () => { - const myKey = bitcoin.ECPair.makeRandom({ network: regtest }); + const myKey = ECPair.makeRandom({ network: regtest }); const myKeys = [ myKey, - bitcoin.ECPair.fromPrivateKey(myKey.privateKey!, { network: regtest }), + ECPair.fromPrivateKey(myKey.privateKey!, { network: regtest }), ]; const p2sh = createPayment('p2sh-p2ms(2 of 2)', myKeys); const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh'); @@ -603,9 +610,9 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { }) .signInputHD(0, hdRoot); // must sign with root!!! - assert.strictEqual(psbt.validateSignaturesOfInput(0), true); + assert.strictEqual(psbt.validateSignaturesOfInput(0, validator), true); assert.strictEqual( - psbt.validateSignaturesOfInput(0, childNode.publicKey), + psbt.validateSignaturesOfInput(0, validator, childNode.publicKey), true, ); psbt.finalizeAllInputs(); @@ -638,11 +645,11 @@ function createPayment(_type: string, myKeys?: any[], network?: any): any { throw new Error('Need n keys for multisig'); } while (!myKeys && n > 1) { - keys.push(bitcoin.ECPair.makeRandom({ network })); + keys.push(ECPair.makeRandom({ network })); n--; } } - if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network })); + if (!myKeys) keys.push(ECPair.makeRandom({ network })); let payment: any; splitType.forEach(type => { diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index f203324..1c6cb09 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,10 +1,10 @@ import * as assert from 'assert'; import * as crypto from 'crypto'; +import { ECPair } from 'ecpair'; import { describe, it } from 'mocha'; import { bip32, - ECPair, networks as NETWORKS, payments, Psbt, @@ -14,6 +14,12 @@ import { import * as preFixtures from './fixtures/psbt.json'; +const validator = ( + pubkey: Buffer, + msghash: Buffer, + signature: Buffer, +): boolean => ECPair.fromPublicKey(pubkey).verify(msghash, signature); + const initBuffers = (object: any): typeof preFixtures => JSON.parse(JSON.stringify(object), (_, value) => { const regex = new RegExp(/^Buffer.from\(['"](.*)['"], ['"](.*)['"]\)$/); @@ -896,7 +902,7 @@ describe(`Psbt`, () => { const notAClone = Object.assign(new Psbt(), psbt); // references still active const clone = psbt.clone(); - assert.strictEqual(psbt.validateSignaturesOfAllInputs(), true); + assert.strictEqual(psbt.validateSignaturesOfAllInputs(validator), true); assert.strictEqual(clone.toBase64(), psbt.toBase64()); assert.strictEqual(clone.toBase64(), notAClone.toBase64()); @@ -923,20 +929,27 @@ describe(`Psbt`, () => { it('Correctly validates a signature', () => { const psbt = Psbt.fromBase64(f.psbt); - assert.strictEqual(psbt.validateSignaturesOfInput(f.index), true); + assert.strictEqual( + psbt.validateSignaturesOfInput(f.index, validator), + true, + ); assert.throws(() => { - psbt.validateSignaturesOfInput(f.nonExistantIndex); + psbt.validateSignaturesOfInput(f.nonExistantIndex, validator); }, new RegExp('No signatures to validate')); }); it('Correctly validates a signature against a pubkey', () => { const psbt = Psbt.fromBase64(f.psbt); assert.strictEqual( - psbt.validateSignaturesOfInput(f.index, f.pubkey as any), + psbt.validateSignaturesOfInput(f.index, validator, f.pubkey as any), true, ); assert.throws(() => { - psbt.validateSignaturesOfInput(f.index, f.incorrectPubkey as any); + psbt.validateSignaturesOfInput( + f.index, + validator, + f.incorrectPubkey as any, + ); }, new RegExp('No signatures for this pubkey')); }); }); @@ -985,7 +998,7 @@ describe(`Psbt`, () => { assert.throws(() => { psbt.setVersion(3); }, new RegExp('Can not modify transaction, signatures exist.')); - psbt.validateSignaturesOfInput(0); + psbt.validateSignaturesOfInput(0, validator); psbt.finalizeAllInputs(); assert.throws(() => { psbt.setVersion(3); diff --git a/test/tsconfig.json b/test/tsconfig.json index e29620d..4e4f529 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES2017", + "target": "ESNEXT", "module": "commonjs", "outDir": "../", "declaration": false, diff --git a/ts_src/address.ts b/ts_src/address.ts index 11be51b..d8111a7 100644 --- a/ts_src/address.ts +++ b/ts_src/address.ts @@ -3,10 +3,9 @@ import * as networks from './networks'; import * as payments from './payments'; import * as bscript from './script'; import * as types from './types'; - -const { bech32, bech32m } = require('bech32'); -const bs58check = require('bs58check'); -const typeforce = require('typeforce'); +import { bech32, bech32m } from 'bech32'; +import * as bs58check from 'bs58check'; +const { typeforce } = types; export interface Base58CheckResult { hash: Buffer; diff --git a/ts_src/bip66.ts b/ts_src/bip66.ts new file mode 100644 index 0000000..ab76a4f --- /dev/null +++ b/ts_src/bip66.ts @@ -0,0 +1,111 @@ +// Reference https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki +// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] +// NOTE: SIGHASH byte ignored AND restricted, truncate before use + +export function check(buffer: Buffer): boolean { + if (buffer.length < 8) return false; + if (buffer.length > 72) return false; + if (buffer[0] !== 0x30) return false; + if (buffer[1] !== buffer.length - 2) return false; + if (buffer[2] !== 0x02) return false; + + const lenR = buffer[3]; + if (lenR === 0) return false; + if (5 + lenR >= buffer.length) return false; + if (buffer[4 + lenR] !== 0x02) return false; + + const lenS = buffer[5 + lenR]; + if (lenS === 0) return false; + if (6 + lenR + lenS !== buffer.length) return false; + + if (buffer[4] & 0x80) return false; + if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) return false; + + if (buffer[lenR + 6] & 0x80) return false; + if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80)) + return false; + return true; +} + +export function decode(buffer: Buffer): { r: Buffer; s: Buffer } { + if (buffer.length < 8) throw new Error('DER sequence length is too short'); + if (buffer.length > 72) throw new Error('DER sequence length is too long'); + if (buffer[0] !== 0x30) throw new Error('Expected DER sequence'); + if (buffer[1] !== buffer.length - 2) + throw new Error('DER sequence length is invalid'); + if (buffer[2] !== 0x02) throw new Error('Expected DER integer'); + + const lenR = buffer[3]; + if (lenR === 0) throw new Error('R length is zero'); + if (5 + lenR >= buffer.length) throw new Error('R length is too long'); + if (buffer[4 + lenR] !== 0x02) throw new Error('Expected DER integer (2)'); + + const lenS = buffer[5 + lenR]; + if (lenS === 0) throw new Error('S length is zero'); + if (6 + lenR + lenS !== buffer.length) throw new Error('S length is invalid'); + + if (buffer[4] & 0x80) throw new Error('R value is negative'); + if (lenR > 1 && buffer[4] === 0x00 && !(buffer[5] & 0x80)) + throw new Error('R value excessively padded'); + + if (buffer[lenR + 6] & 0x80) throw new Error('S value is negative'); + if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80)) + throw new Error('S value excessively padded'); + + // non-BIP66 - extract R, S values + return { + r: buffer.slice(4, 4 + lenR), + s: buffer.slice(6 + lenR), + }; +} + +/* + * Expects r and s to be positive DER integers. + * + * The DER format uses the most significant bit as a sign bit (& 0x80). + * If the significant bit is set AND the integer is positive, a 0x00 is prepended. + * + * Examples: + * + * 0 => 0x00 + * 1 => 0x01 + * -1 => 0xff + * 127 => 0x7f + * -127 => 0x81 + * 128 => 0x0080 + * -128 => 0x80 + * 255 => 0x00ff + * -255 => 0xff01 + * 16300 => 0x3fac + * -16300 => 0xc054 + * 62300 => 0x00f35c + * -62300 => 0xff0ca4 + */ +export function encode(r: Buffer, s: Buffer): Buffer { + const lenR = r.length; + const lenS = s.length; + if (lenR === 0) throw new Error('R length is zero'); + if (lenS === 0) throw new Error('S length is zero'); + if (lenR > 33) throw new Error('R length is too long'); + if (lenS > 33) throw new Error('S length is too long'); + if (r[0] & 0x80) throw new Error('R value is negative'); + if (s[0] & 0x80) throw new Error('S value is negative'); + if (lenR > 1 && r[0] === 0x00 && !(r[1] & 0x80)) + throw new Error('R value excessively padded'); + if (lenS > 1 && s[0] === 0x00 && !(s[1] & 0x80)) + throw new Error('S value excessively padded'); + + const signature = Buffer.allocUnsafe(6 + lenR + lenS); + + // 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] + signature[0] = 0x30; + signature[1] = signature.length - 2; + signature[2] = 0x02; + signature[3] = r.length; + r.copy(signature, 4); + signature[4 + lenR] = 0x02; + signature[5 + lenR] = s.length; + s.copy(signature, 6 + lenR); + + return signature; +} diff --git a/ts_src/block.ts b/ts_src/block.ts index 2760e5e..c73477e 100644 --- a/ts_src/block.ts +++ b/ts_src/block.ts @@ -1,11 +1,14 @@ -import { BufferReader, BufferWriter, reverseBuffer } from './bufferutils'; +import { + BufferReader, + BufferWriter, + reverseBuffer, + varuint, +} from './bufferutils'; import * as bcrypto from './crypto'; +import { fastMerkleRoot } from './merkle'; import { Transaction } from './transaction'; import * as types from './types'; - -const fastMerkleRoot = require('merkle-lib/fastRoot'); -const typeforce = require('typeforce'); -const varuint = require('varuint-bitcoin'); +const { typeforce } = types; const errorMerkleNoTxes = new TypeError( 'Cannot compute merkle root for zero transactions', diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 9005f2a..43171c4 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -1,7 +1,7 @@ import * as types from './types'; - -const typeforce = require('typeforce'); -const varuint = require('varuint-bitcoin'); +const { typeforce } = types; +import * as varuint from 'varuint-bitcoin'; +export { varuint }; // https://github.com/feross/buffer/blob/master/index.js#L1127 function verifuint(value: number, max: number): void { diff --git a/ts_src/crypto.ts b/ts_src/crypto.ts index 1cb5a69..7f69c40 100644 --- a/ts_src/crypto.ts +++ b/ts_src/crypto.ts @@ -1,4 +1,4 @@ -const createHash = require('create-hash'); +import * as createHash from 'create-hash'; export function ripemd160(buffer: Buffer): Buffer { try { diff --git a/ts_src/ecpair.ts b/ts_src/ecpair.ts deleted file mode 100644 index 3c7eb1c..0000000 --- a/ts_src/ecpair.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { Network } from './networks'; -import * as NETWORKS from './networks'; -import * as types from './types'; -const ecc = require('tiny-secp256k1'); -const randomBytes = require('randombytes'); -const typeforce = require('typeforce'); -const wif = require('wif'); - -const isOptions = typeforce.maybe( - typeforce.compile({ - compressed: types.maybe(types.Boolean), - network: types.maybe(types.Network), - }), -); - -interface ECPairOptions { - compressed?: boolean; - network?: Network; - rng?(arg0: number): Buffer; -} - -export interface Signer { - publicKey: Buffer; - network?: any; - sign(hash: Buffer, lowR?: boolean): Buffer; - getPublicKey?(): Buffer; -} - -export interface SignerAsync { - publicKey: Buffer; - network?: any; - sign(hash: Buffer, lowR?: boolean): Promise<Buffer>; - getPublicKey?(): Buffer; -} - -export interface ECPairInterface extends Signer { - compressed: boolean; - network: Network; - lowR: boolean; - privateKey?: Buffer; - toWIF(): string; - verify(hash: Buffer, signature: Buffer): boolean; -} - -class ECPair implements ECPairInterface { - compressed: boolean; - network: Network; - lowR: boolean; - - constructor( - private __D?: Buffer, - private __Q?: Buffer, - options?: ECPairOptions, - ) { - this.lowR = false; - if (options === undefined) options = {}; - this.compressed = - options.compressed === undefined ? true : options.compressed; - this.network = options.network || NETWORKS.bitcoin; - - if (__Q !== undefined) this.__Q = ecc.pointCompress(__Q, this.compressed); - } - - get privateKey(): Buffer | undefined { - return this.__D; - } - - get publicKey(): Buffer { - if (!this.__Q) - this.__Q = ecc.pointFromScalar(this.__D, this.compressed) as Buffer; - return this.__Q; - } - - toWIF(): string { - if (!this.__D) throw new Error('Missing private key'); - return wif.encode(this.network.wif, this.__D, this.compressed); - } - - sign(hash: Buffer, lowR?: boolean): Buffer { - if (!this.__D) throw new Error('Missing private key'); - if (lowR === undefined) lowR = this.lowR; - if (lowR === false) { - return ecc.sign(hash, this.__D); - } else { - let sig = ecc.sign(hash, this.__D); - const extraData = Buffer.alloc(32, 0); - let counter = 0; - // if first try is lowR, skip the loop - // for second try and on, add extra entropy counting up - while (sig[0] > 0x7f) { - counter++; - extraData.writeUIntLE(counter, 0, 6); - sig = ecc.signWithEntropy(hash, this.__D, extraData); - } - return sig; - } - } - - verify(hash: Buffer, signature: Buffer): boolean { - return ecc.verify(hash, this.publicKey, signature); - } -} - -function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair { - typeforce(types.Buffer256bit, buffer); - if (!ecc.isPrivate(buffer)) - throw new TypeError('Private key not in range [1, n)'); - typeforce(isOptions, options); - - return new ECPair(buffer, undefined, options); -} - -function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair { - typeforce(ecc.isPoint, buffer); - typeforce(isOptions, options); - return new ECPair(undefined, buffer, options); -} - -function fromWIF(wifString: string, network?: Network | Network[]): ECPair { - const decoded = wif.decode(wifString); - const version = decoded.version; - - // list of networks? - if (types.Array(network)) { - network = (network as Network[]) - .filter((x: Network) => { - return version === x.wif; - }) - .pop() as Network; - - if (!network) throw new Error('Unknown network version'); - - // otherwise, assume a network object (or default to bitcoin) - } else { - network = network || NETWORKS.bitcoin; - - if (version !== (network as Network).wif) - throw new Error('Invalid network version'); - } - - return fromPrivateKey(decoded.privateKey, { - compressed: decoded.compressed, - network: network as Network, - }); -} - -function makeRandom(options?: ECPairOptions): ECPair { - typeforce(isOptions, options); - if (options === undefined) options = {}; - const rng = options.rng || randomBytes; - - let d; - do { - d = rng(32); - typeforce(types.Buffer256bit, d); - } while (!ecc.isPrivate(d)); - - return fromPrivateKey(d, options); -} - -export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF }; diff --git a/ts_src/index.ts b/ts_src/index.ts index c425d96..66b2f9c 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -1,20 +1,26 @@ import * as bip32 from 'bip32'; import * as address from './address'; import * as crypto from './crypto'; -import * as ECPair from './ecpair'; import * as networks from './networks'; import * as payments from './payments'; import * as script from './script'; -export { ECPair, address, bip32, crypto, networks, payments, script }; +export { address, bip32, crypto, networks, payments, script }; export { Block } from './block'; -export { Psbt, PsbtTxInput, PsbtTxOutput } from './psbt'; -export { OPS as opcodes } from './script'; +export { + Psbt, + PsbtTxInput, + PsbtTxOutput, + Signer, + SignerAsync, + HDSigner, + HDSignerAsync, +} from './psbt'; +export { OPS as opcodes } from './ops'; export { Transaction } from './transaction'; export { BIP32Interface } from 'bip32'; -export { ECPairInterface, Signer, SignerAsync } from './ecpair'; export { Network } from './networks'; export { Payment, @@ -23,5 +29,4 @@ export { Stack, StackElement, } from './payments'; -export { OpCode } from './script'; export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/ts_src/merkle.ts b/ts_src/merkle.ts new file mode 100644 index 0000000..8ff8c3f --- /dev/null +++ b/ts_src/merkle.ts @@ -0,0 +1,27 @@ +export function fastMerkleRoot( + values: Buffer[], + digestFn: (b: Buffer) => Buffer, +): Buffer { + if (!Array.isArray(values)) throw TypeError('Expected values Array'); + if (typeof digestFn !== 'function') + throw TypeError('Expected digest Function'); + + let length = values.length; + const results = values.concat(); + + while (length > 1) { + let j = 0; + + for (let i = 0; i < length; i += 2, ++j) { + const left = results[i]; + const right = i + 1 === length ? left : results[i + 1]; + const data = Buffer.concat([left, right]); + + results[j] = digestFn(data); + } + + length = j; + } + + return results[0]; +} diff --git a/ts_src/ops.ts b/ts_src/ops.ts new file mode 100644 index 0000000..8e2c41c --- /dev/null +++ b/ts_src/ops.ts @@ -0,0 +1,141 @@ +const OPS: { [key: string]: number } = { + OP_FALSE: 0, + OP_0: 0, + OP_PUSHDATA1: 76, + OP_PUSHDATA2: 77, + OP_PUSHDATA4: 78, + OP_1NEGATE: 79, + OP_RESERVED: 80, + OP_TRUE: 81, + OP_1: 81, + OP_2: 82, + OP_3: 83, + OP_4: 84, + OP_5: 85, + OP_6: 86, + OP_7: 87, + OP_8: 88, + OP_9: 89, + OP_10: 90, + OP_11: 91, + OP_12: 92, + OP_13: 93, + OP_14: 94, + OP_15: 95, + OP_16: 96, + + OP_NOP: 97, + OP_VER: 98, + OP_IF: 99, + OP_NOTIF: 100, + OP_VERIF: 101, + OP_VERNOTIF: 102, + OP_ELSE: 103, + OP_ENDIF: 104, + OP_VERIFY: 105, + OP_RETURN: 106, + + OP_TOALTSTACK: 107, + OP_FROMALTSTACK: 108, + OP_2DROP: 109, + OP_2DUP: 110, + OP_3DUP: 111, + OP_2OVER: 112, + OP_2ROT: 113, + OP_2SWAP: 114, + OP_IFDUP: 115, + OP_DEPTH: 116, + OP_DROP: 117, + OP_DUP: 118, + OP_NIP: 119, + OP_OVER: 120, + OP_PICK: 121, + OP_ROLL: 122, + OP_ROT: 123, + OP_SWAP: 124, + OP_TUCK: 125, + + OP_CAT: 126, + OP_SUBSTR: 127, + OP_LEFT: 128, + OP_RIGHT: 129, + OP_SIZE: 130, + + OP_INVERT: 131, + OP_AND: 132, + OP_OR: 133, + OP_XOR: 134, + OP_EQUAL: 135, + OP_EQUALVERIFY: 136, + OP_RESERVED1: 137, + OP_RESERVED2: 138, + + OP_1ADD: 139, + OP_1SUB: 140, + OP_2MUL: 141, + OP_2DIV: 142, + OP_NEGATE: 143, + OP_ABS: 144, + OP_NOT: 145, + OP_0NOTEQUAL: 146, + OP_ADD: 147, + OP_SUB: 148, + OP_MUL: 149, + OP_DIV: 150, + OP_MOD: 151, + OP_LSHIFT: 152, + OP_RSHIFT: 153, + + OP_BOOLAND: 154, + OP_BOOLOR: 155, + OP_NUMEQUAL: 156, + OP_NUMEQUALVERIFY: 157, + OP_NUMNOTEQUAL: 158, + OP_LESSTHAN: 159, + OP_GREATERTHAN: 160, + OP_LESSTHANOREQUAL: 161, + OP_GREATERTHANOREQUAL: 162, + OP_MIN: 163, + OP_MAX: 164, + + OP_WITHIN: 165, + + OP_RIPEMD160: 166, + OP_SHA1: 167, + OP_SHA256: 168, + OP_HASH160: 169, + OP_HASH256: 170, + OP_CODESEPARATOR: 171, + OP_CHECKSIG: 172, + OP_CHECKSIGVERIFY: 173, + OP_CHECKMULTISIG: 174, + OP_CHECKMULTISIGVERIFY: 175, + + OP_NOP1: 176, + + OP_NOP2: 177, + OP_CHECKLOCKTIMEVERIFY: 177, + + OP_NOP3: 178, + OP_CHECKSEQUENCEVERIFY: 178, + + OP_NOP4: 179, + OP_NOP5: 180, + OP_NOP6: 181, + OP_NOP7: 182, + OP_NOP8: 183, + OP_NOP9: 184, + OP_NOP10: 185, + + OP_PUBKEYHASH: 253, + OP_PUBKEY: 254, + OP_INVALIDOPCODE: 255, +}; + +const REVERSE_OPS: { [key: number]: string } = {}; +for (const op of Object.keys(OPS)) { + const code = OPS[op]; + REVERSE_OPS[code] = op; +} + +export { OPS, REVERSE_OPS }; diff --git a/ts_src/payments/embed.ts b/ts_src/payments/embed.ts index c54e279..c479b89 100644 --- a/ts_src/payments/embed.ts +++ b/ts_src/payments/embed.ts @@ -1,9 +1,9 @@ import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; +import { typeforce as typef } from '../types'; import { Payment, PaymentOpts, Stack } from './index'; import * as lazy from './lazy'; -const typef = require('typeforce'); const OPS = bscript.OPS; function stacksEqual(a: Buffer[], b: Buffer[]): boolean { diff --git a/ts_src/payments/p2ms.ts b/ts_src/payments/p2ms.ts index 7cd6f10..eaa1440 100644 --- a/ts_src/payments/p2ms.ts +++ b/ts_src/payments/p2ms.ts @@ -1,10 +1,9 @@ import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; +import { isPoint, typeforce as typef } from '../types'; import { Payment, PaymentOpts, Stack } from './index'; import * as lazy from './lazy'; const OPS = bscript.OPS; -const typef = require('typeforce'); -const ecc = require('tiny-secp256k1'); const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 @@ -41,7 +40,7 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { m: typef.maybe(typef.Number), n: typef.maybe(typef.Number), output: typef.maybe(typef.Buffer), - pubkeys: typef.maybe(typef.arrayOf(ecc.isPoint)), + pubkeys: typef.maybe(typef.arrayOf(isPoint)), signatures: typef.maybe(typef.arrayOf(isAcceptableSignature)), input: typef.maybe(typef.Buffer), @@ -119,7 +118,7 @@ export function p2ms(a: Payment, opts?: PaymentOpts): Payment { if (o.m! <= 0 || o.n! > 16 || o.m! > o.n! || o.n !== chunks.length - 3) throw new TypeError('Output is invalid'); - if (!o.pubkeys!.every(x => ecc.isPoint(x))) + if (!o.pubkeys!.every(x => isPoint(x))) throw new TypeError('Output is invalid'); if (a.m !== undefined && a.m !== o.m) throw new TypeError('m mismatch'); diff --git a/ts_src/payments/p2pk.ts b/ts_src/payments/p2pk.ts index b4fdbc5..7273f53 100644 --- a/ts_src/payments/p2pk.ts +++ b/ts_src/payments/p2pk.ts @@ -1,10 +1,9 @@ import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; +import { isPoint, typeforce as typef } from '../types'; import { Payment, PaymentOpts, StackFunction } from './index'; import * as lazy from './lazy'; -const typef = require('typeforce'); const OPS = bscript.OPS; -const ecc = require('tiny-secp256k1'); // input: {signature} // output: {pubKey} OP_CHECKSIG @@ -17,7 +16,7 @@ export function p2pk(a: Payment, opts?: PaymentOpts): Payment { { network: typef.maybe(typef.Object), output: typef.maybe(typef.Buffer), - pubkey: typef.maybe(ecc.isPoint), + pubkey: typef.maybe(isPoint), signature: typef.maybe(bscript.isCanonicalScriptSignature), input: typef.maybe(typef.Buffer), @@ -58,8 +57,7 @@ export function p2pk(a: Payment, opts?: PaymentOpts): Payment { if (a.output) { if (a.output[a.output.length - 1] !== OPS.OP_CHECKSIG) throw new TypeError('Output is invalid'); - if (!ecc.isPoint(o.pubkey)) - throw new TypeError('Output pubkey is invalid'); + if (!isPoint(o.pubkey)) throw new TypeError('Output pubkey is invalid'); if (a.pubkey && !a.pubkey.equals(o.pubkey!)) throw new TypeError('Pubkey mismatch'); } diff --git a/ts_src/payments/p2pkh.ts b/ts_src/payments/p2pkh.ts index 6503093..3b5bd6f 100644 --- a/ts_src/payments/p2pkh.ts +++ b/ts_src/payments/p2pkh.ts @@ -1,13 +1,11 @@ import * as bcrypto from '../crypto'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; +import { isPoint, typeforce as typef } from '../types'; import { Payment, PaymentOpts, StackFunction } from './index'; import * as lazy from './lazy'; -const typef = require('typeforce'); +import * as bs58check from 'bs58check'; const OPS = bscript.OPS; -const ecc = require('tiny-secp256k1'); - -const bs58check = require('bs58check'); // input: {signature} {pubkey} // output: OP_DUP OP_HASH160 {hash160(pubkey)} OP_EQUALVERIFY OP_CHECKSIG @@ -23,7 +21,7 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { hash: typef.maybe(typef.BufferN(20)), output: typef.maybe(typef.BufferN(25)), - pubkey: typef.maybe(ecc.isPoint), + pubkey: typef.maybe(isPoint), signature: typef.maybe(bscript.isCanonicalScriptSignature), input: typef.maybe(typef.Buffer), }, @@ -31,7 +29,7 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { ); const _address = lazy.value(() => { - const payload = bs58check.decode(a.address); + const payload = bs58check.decode(a.address!); const version = payload.readUInt8(0); const hash = payload.slice(1); return { version, hash }; @@ -129,8 +127,7 @@ export function p2pkh(a: Payment, opts?: PaymentOpts): Payment { if (chunks.length !== 2) throw new TypeError('Input is invalid'); if (!bscript.isCanonicalScriptSignature(chunks[0] as Buffer)) throw new TypeError('Input has invalid signature'); - if (!ecc.isPoint(chunks[1])) - throw new TypeError('Input has invalid pubkey'); + if (!isPoint(chunks[1])) throw new TypeError('Input has invalid pubkey'); if (a.signature && !a.signature.equals(chunks[0] as Buffer)) throw new TypeError('Signature mismatch'); diff --git a/ts_src/payments/p2sh.ts b/ts_src/payments/p2sh.ts index 3b53fdc..9be5a8c 100644 --- a/ts_src/payments/p2sh.ts +++ b/ts_src/payments/p2sh.ts @@ -1,6 +1,7 @@ import * as bcrypto from '../crypto'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; +import { typeforce as typef } from '../types'; import { Payment, PaymentFunction, @@ -9,11 +10,9 @@ import { StackFunction, } from './index'; import * as lazy from './lazy'; -const typef = require('typeforce'); +import * as bs58check from 'bs58check'; const OPS = bscript.OPS; -const bs58check = require('bs58check'); - function stacksEqual(a: Buffer[], b: Buffer[]): boolean { if (a.length !== b.length) return false; @@ -58,7 +57,7 @@ export function p2sh(a: Payment, opts?: PaymentOpts): Payment { const o: Payment = { network }; const _address = lazy.value(() => { - const payload = bs58check.decode(a.address); + const payload = bs58check.decode(a.address!); const version = payload.readUInt8(0); const hash = payload.slice(1); return { version, hash }; diff --git a/ts_src/payments/p2wpkh.ts b/ts_src/payments/p2wpkh.ts index 3d8f86f..a4497fe 100644 --- a/ts_src/payments/p2wpkh.ts +++ b/ts_src/payments/p2wpkh.ts @@ -1,13 +1,11 @@ import * as bcrypto from '../crypto'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; +import { isPoint, typeforce as typef } from '../types'; import { Payment, PaymentOpts } from './index'; import * as lazy from './lazy'; -const typef = require('typeforce'); +import { bech32 } from 'bech32'; const OPS = bscript.OPS; -const ecc = require('tiny-secp256k1'); - -const { bech32 } = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); @@ -26,7 +24,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { input: typef.maybe(typef.BufferN(0)), network: typef.maybe(typef.Object), output: typef.maybe(typef.BufferN(22)), - pubkey: typef.maybe(ecc.isPoint), + pubkey: typef.maybe(isPoint), signature: typef.maybe(bscript.isCanonicalScriptSignature), witness: typef.maybe(typef.arrayOf(typef.Buffer)), }, @@ -34,7 +32,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { ); const _address = lazy.value(() => { - const result = bech32.decode(a.address); + const result = bech32.decode(a.address!); const version = result.words.shift(); const data = bech32.fromWords(result.words); return { @@ -118,7 +116,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { if (hash.length > 0 && !hash.equals(pkh)) throw new TypeError('Hash mismatch'); else hash = pkh; - if (!ecc.isPoint(a.pubkey) || a.pubkey.length !== 33) + if (!isPoint(a.pubkey) || a.pubkey.length !== 33) throw new TypeError('Invalid pubkey for p2wpkh'); } @@ -126,7 +124,7 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment { if (a.witness.length !== 2) throw new TypeError('Witness is invalid'); if (!bscript.isCanonicalScriptSignature(a.witness[0])) throw new TypeError('Witness has invalid signature'); - if (!ecc.isPoint(a.witness[1]) || a.witness[1].length !== 33) + if (!isPoint(a.witness[1]) || a.witness[1].length !== 33) throw new TypeError('Witness has invalid pubkey'); if (a.signature && !a.signature.equals(a.witness[0])) diff --git a/ts_src/payments/p2wsh.ts b/ts_src/payments/p2wsh.ts index e28cd57..00860e0 100644 --- a/ts_src/payments/p2wsh.ts +++ b/ts_src/payments/p2wsh.ts @@ -1,13 +1,11 @@ import * as bcrypto from '../crypto'; import { bitcoin as BITCOIN_NETWORK } from '../networks'; import * as bscript from '../script'; +import { isPoint, typeforce as typef } from '../types'; import { Payment, PaymentOpts, StackElement, StackFunction } from './index'; import * as lazy from './lazy'; -const typef = require('typeforce'); +import { bech32 } from 'bech32'; const OPS = bscript.OPS; -const ecc = require('tiny-secp256k1'); - -const { bech32 } = require('bech32'); const EMPTY_BUFFER = Buffer.alloc(0); @@ -24,7 +22,7 @@ function chunkHasUncompressedPubkey(chunk: StackElement): boolean { Buffer.isBuffer(chunk) && chunk.length === 65 && chunk[0] === 0x04 && - ecc.isPoint(chunk) + isPoint(chunk) ) { return true; } else { @@ -61,7 +59,7 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment { ); const _address = lazy.value(() => { - const result = bech32.decode(a.address); + const result = bech32.decode(a.address!); const version = result.words.shift(); const data = bech32.fromWords(result.words); return { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index c23fa1f..b9af10f 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -16,11 +16,6 @@ import { checkForInput, checkForOutput } from 'bip174/src/lib/utils'; import { fromOutputScript, toOutputScript } from './address'; import { cloneBuffer, reverseBuffer } from './bufferutils'; import { hash160 } from './crypto'; -import { - fromPublicKey as ecPairFromPublicKey, - Signer, - SignerAsync, -} from './ecpair'; import { bitcoin as btcNetwork, Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; @@ -45,6 +40,13 @@ export interface PsbtTxOutput extends TransactionOutput { address: string | undefined; } +// msghash is 32 byte hash of preimage, signature is 64 byte compact signature (r,s 32 bytes each) +export type ValidateSigFunction = ( + pubkey: Buffer, + msghash: Buffer, + signature: Buffer, +) => boolean; + /** * These are the default arguments for a Psbt instance. */ @@ -416,19 +418,25 @@ export class Psbt { ); } - validateSignaturesOfAllInputs(): boolean { + validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean { checkForInput(this.data.inputs, 0); // making sure we have at least one const results = range(this.data.inputs.length).map(idx => - this.validateSignaturesOfInput(idx), + this.validateSignaturesOfInput(idx, validator), ); return results.reduce((final, res) => res === true && final, true); } - validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean { + validateSignaturesOfInput( + inputIndex: number, + validator: ValidateSigFunction, + pubkey?: Buffer, + ): boolean { const input = this.data.inputs[inputIndex]; const partialSig = (input || {}).partialSig; if (!input || !partialSig || partialSig.length < 1) throw new Error('No signatures to validate'); + if (typeof validator !== 'function') + throw new Error('Need validator function to validate signatures'); const mySigs = pubkey ? partialSig.filter(sig => sig.pubkey.equals(pubkey)) : partialSig; @@ -452,8 +460,7 @@ export class Psbt { hashCache = hash; scriptCache = script; checkScriptForPubkey(pSig.pubkey, script, 'verify'); - const keypair = ecPairFromPublicKey(pSig.pubkey); - results.push(keypair.verify(hash, sig.signature)); + results.push(validator(pSig.pubkey, hash, sig.signature)); } return results.every(res => res === true); } @@ -780,7 +787,7 @@ interface HDSignerBase { fingerprint: Buffer; } -interface HDSigner extends HDSignerBase { +export interface HDSigner extends HDSignerBase { /** * The path string must match /^m(\/\d+'?)+$/ * ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations @@ -796,11 +803,25 @@ interface HDSigner extends HDSignerBase { /** * Same as above but with async sign method */ -interface HDSignerAsync extends HDSignerBase { +export interface HDSignerAsync extends HDSignerBase { derivePath(path: string): HDSignerAsync; sign(hash: Buffer): Promise<Buffer>; } +export interface Signer { + publicKey: Buffer; + network?: any; + sign(hash: Buffer, lowR?: boolean): Buffer; + getPublicKey?(): Buffer; +} + +export interface SignerAsync { + publicKey: Buffer; + network?: any; + sign(hash: Buffer, lowR?: boolean): Promise<Buffer>; + getPublicKey?(): Buffer; +} + /** * This function is needed to pass to the bip174 base class's fromBuffer. * It takes the "transaction buffer" portion of the psbt buffer and returns a @@ -903,8 +924,7 @@ function hasSigs( if (pubkeys) { sigs = pubkeys .map(pkey => { - const pubkey = ecPairFromPublicKey(pkey, { compressed: true }) - .publicKey; + const pubkey = compressPubkey(pkey); return partialSig.find(pSig => pSig.pubkey.equals(pubkey)); }) .filter(v => !!v); @@ -1305,7 +1325,7 @@ function getHashForSig( console.warn( 'Warning: Signing non-segwit inputs without the full parent transaction ' + 'means there is a chance that a miner could feed you incorrect information ' + - 'to trick you into paying large fees. This behavior is the same as Psbt\'s predecesor ' + + "to trick you into paying large fees. This behavior is the same as Psbt's predecesor " + '(TransactionBuilder - now removed) when signing non-segwit scripts. You are not ' + 'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' + 'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' + @@ -1714,6 +1734,16 @@ function redeemFromFinalWitnessScript( return lastItem; } +function compressPubkey(pubkey: Buffer): Buffer { + if (pubkey.length === 65) { + const parity = pubkey[64] & 1; + const newKey = pubkey.slice(0, 33); + newKey[0] = 2 | parity; + return newKey; + } + return pubkey.slice(); +} + function isPubkeyLike(buf: Buffer): boolean { return buf.length === 33 && bscript.isCanonicalPubKey(buf); } diff --git a/ts_src/push_data.ts b/ts_src/push_data.ts new file mode 100644 index 0000000..56bb02a --- /dev/null +++ b/ts_src/push_data.ts @@ -0,0 +1,76 @@ +import { OPS } from './ops'; + +export function encodingLength(i: number): number { + return i < OPS.OP_PUSHDATA1 ? 1 : i <= 0xff ? 2 : i <= 0xffff ? 3 : 5; +} + +export function encode(buffer: Buffer, num: number, offset: number): number { + const size = encodingLength(num); + + // ~6 bit + if (size === 1) { + buffer.writeUInt8(num, offset); + + // 8 bit + } else if (size === 2) { + buffer.writeUInt8(OPS.OP_PUSHDATA1, offset); + buffer.writeUInt8(num, offset + 1); + + // 16 bit + } else if (size === 3) { + buffer.writeUInt8(OPS.OP_PUSHDATA2, offset); + buffer.writeUInt16LE(num, offset + 1); + + // 32 bit + } else { + buffer.writeUInt8(OPS.OP_PUSHDATA4, offset); + buffer.writeUInt32LE(num, offset + 1); + } + + return size; +} + +export function decode( + buffer: Buffer, + offset: number, +): { + opcode: number; + number: number; + size: number; +} | null { + const opcode = buffer.readUInt8(offset); + let num: number; + let size: number; + + // ~6 bit + if (opcode < OPS.OP_PUSHDATA1) { + num = opcode; + size = 1; + + // 8 bit + } else if (opcode === OPS.OP_PUSHDATA1) { + if (offset + 2 > buffer.length) return null; + num = buffer.readUInt8(offset + 1); + size = 2; + + // 16 bit + } else if (opcode === OPS.OP_PUSHDATA2) { + if (offset + 3 > buffer.length) return null; + num = buffer.readUInt16LE(offset + 1); + size = 3; + + // 32 bit + } else { + if (offset + 5 > buffer.length) return null; + if (opcode !== OPS.OP_PUSHDATA4) throw new Error('Unexpected opcode'); + + num = buffer.readUInt32LE(offset + 1); + size = 5; + } + + return { + opcode, + number: num, + size, + }; +} diff --git a/ts_src/script.ts b/ts_src/script.ts index 951f48b..5d20ebc 100644 --- a/ts_src/script.ts +++ b/ts_src/script.ts @@ -1,17 +1,14 @@ +import * as bip66 from './bip66'; +import { OPS, REVERSE_OPS } from './ops'; import { Stack } from './payments'; +import * as pushdata from './push_data'; import * as scriptNumber from './script_number'; import * as scriptSignature from './script_signature'; import * as types from './types'; -const bip66 = require('bip66'); -const ecc = require('tiny-secp256k1'); -const pushdata = require('pushdata-bitcoin'); -const typeforce = require('typeforce'); +const { typeforce } = types; -export type OpCode = number; -export const OPS = require('bitcoin-ops') as { [index: string]: OpCode }; - -const REVERSE_OPS = require('bitcoin-ops/map') as { [index: number]: string }; const OP_INT_BASE = OPS.OP_RESERVED; // OP_1 - 1 +export { OPS }; function isOPInt(value: number): boolean { return ( @@ -194,7 +191,7 @@ export function toStack(chunks: Buffer | Array<number | Buffer>): Buffer[] { } export function isCanonicalPubKey(buffer: Buffer): boolean { - return ecc.isPoint(buffer); + return types.isPoint(buffer); } export function isDefinedHashType(hashType: number): boolean { diff --git a/ts_src/script_signature.ts b/ts_src/script_signature.ts index af9930e..df206e8 100644 --- a/ts_src/script_signature.ts +++ b/ts_src/script_signature.ts @@ -1,7 +1,6 @@ +import * as bip66 from './bip66'; import * as types from './types'; -const bip66 = require('bip66'); - -const typeforce = require('typeforce'); +const { typeforce } = types; const ZERO = Buffer.alloc(1, 0); function toDER(x: Buffer): Buffer { diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index b1ac302..c5dde9a 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -1,11 +1,14 @@ -import { BufferReader, BufferWriter, reverseBuffer } from './bufferutils'; +import { + BufferReader, + BufferWriter, + reverseBuffer, + varuint, +} from './bufferutils'; import * as bcrypto from './crypto'; import * as bscript from './script'; import { OPS as opcodes } from './script'; import * as types from './types'; - -const typeforce = require('typeforce'); -const varuint = require('varuint-bitcoin'); +const { typeforce } = types; function varSliceSize(someScript: Buffer): number { const length = someScript.length; diff --git a/ts_src/types.ts b/ts_src/types.ts index 2e41267..c035b40 100644 --- a/ts_src/types.ts +++ b/ts_src/types.ts @@ -1,4 +1,29 @@ -const typeforce = require('typeforce'); +import { Buffer as NBuffer } from 'buffer'; +export const typeforce = require('typeforce'); + +const ZERO32 = NBuffer.alloc(32, 0); +const EC_P = NBuffer.from( + 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', + 'hex', +); +export function isPoint(p: Buffer | number | undefined | null): boolean { + if (!NBuffer.isBuffer(p)) return false; + if (p.length < 33) return false; + + const t = p[0]; + const x = p.slice(1, 33); + if (x.compare(ZERO32) === 0) return false; + if (x.compare(EC_P) >= 0) return false; + if ((t === 0x02 || t === 0x03) && p.length === 33) { + return true; + } + + const y = p.slice(33); + if (y.compare(ZERO32) === 0) return false; + if (y.compare(EC_P) >= 0) return false; + if (t === 0x04 && p.length === 65) return true; + return false; +} const UINT31_MAX: number = Math.pow(2, 31) - 1; export function UInt31(value: number): boolean { diff --git a/tsconfig.json b/tsconfig.json index f770a45..3c74061 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,9 @@ { "compilerOptions": { - "target": "ES2015", + "target": "ESNEXT", "module": "commonjs", "outDir": "./src", "declaration": true, - "declarationDir": "./types", "rootDir": "./ts_src", "types": [ "node" diff --git a/tslint.json b/tslint.json index d42da60..90e513d 100644 --- a/tslint.json +++ b/tslint.json @@ -2,7 +2,8 @@ "defaultSeverity": "error", "extends": ["tslint:recommended"], "rules": { - "arrow-parens": [true, "ban-single-arg-parens"], + "array-type": false, + "arrow-parens": false, "curly": false, "indent": [ true, @@ -25,7 +26,6 @@ "typedef": [ true, "call-signature", - "arrow-call-signature", "property-declaration" ], "variable-name": [ diff --git a/types/ecpair.d.ts b/types/ecpair.d.ts deleted file mode 100644 index 447c608..0000000 --- a/types/ecpair.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Network } from './networks'; -interface ECPairOptions { - compressed?: boolean; - network?: Network; - rng?(arg0: number): Buffer; -} -export interface Signer { - publicKey: Buffer; - network?: any; - sign(hash: Buffer, lowR?: boolean): Buffer; - getPublicKey?(): Buffer; -} -export interface SignerAsync { - publicKey: Buffer; - network?: any; - sign(hash: Buffer, lowR?: boolean): Promise<Buffer>; - getPublicKey?(): Buffer; -} -export interface ECPairInterface extends Signer { - compressed: boolean; - network: Network; - lowR: boolean; - privateKey?: Buffer; - toWIF(): string; - verify(hash: Buffer, signature: Buffer): boolean; -} -declare class ECPair implements ECPairInterface { - private __D?; - private __Q?; - compressed: boolean; - network: Network; - lowR: boolean; - constructor(__D?: Buffer | undefined, __Q?: Buffer | undefined, options?: ECPairOptions); - readonly privateKey: Buffer | undefined; - readonly publicKey: Buffer; - toWIF(): string; - sign(hash: Buffer, lowR?: boolean): Buffer; - verify(hash: Buffer, signature: Buffer): boolean; -} -declare function fromPrivateKey(buffer: Buffer, options?: ECPairOptions): ECPair; -declare function fromPublicKey(buffer: Buffer, options?: ECPairOptions): ECPair; -declare function fromWIF(wifString: string, network?: Network | Network[]): ECPair; -declare function makeRandom(options?: ECPairOptions): ECPair; -export { makeRandom, fromPrivateKey, fromPublicKey, fromWIF }; From 32cc25dad88dc2ee0119fd29102afcd2ed03baec Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 20 Oct 2021 20:36:21 +0900 Subject: [PATCH 555/568] Remove randombytes and create-hmac --- package-lock.json | 10 +--------- package.json | 3 --- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index f932d24..d021ca3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -427,15 +427,6 @@ "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==", "dev": true }, - "@types/randombytes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.0.tgz", - "integrity": "sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/wif": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.2.tgz", @@ -2150,6 +2141,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, "requires": { "safe-buffer": "^5.1.0" } diff --git a/package.json b/package.json index 03df9d5..10b0d76 100644 --- a/package.json +++ b/package.json @@ -54,8 +54,6 @@ "bip32": "^2.0.4", "bs58check": "^2.1.2", "create-hash": "^1.1.0", - "create-hmac": "^1.1.3", - "randombytes": "^2.0.1", "typeforce": "^1.11.3", "varuint-bitcoin": "^1.1.2", "wif": "^2.0.1" @@ -67,7 +65,6 @@ "@types/mocha": "^5.2.7", "@types/node": "^16.11.1", "@types/proxyquire": "^1.3.28", - "@types/randombytes": "^2.0.0", "@types/wif": "^2.0.2", "bip39": "^3.0.2", "bip65": "^1.0.1", From b1ff3cee498f4b3576e8f74fdb2475f23ea3ba04 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 20 Oct 2021 23:55:57 +0900 Subject: [PATCH 556/568] Use ES2020, add dev dep for randombytes --- package-lock.json | 9 +++++++++ package.json | 2 ++ src/block.js | 18 ++++++++++-------- src/bufferutils.js | 4 ---- src/psbt.js | 32 ++++++++++++++------------------ src/transaction.js | 24 +++++++++++++----------- test/tsconfig.json | 2 +- tsconfig.json | 2 +- 8 files changed, 50 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index d021ca3..6512208 100644 --- a/package-lock.json +++ b/package-lock.json @@ -427,6 +427,15 @@ "integrity": "sha512-SQaNzWQ2YZSr7FqAyPPiA3FYpux2Lqh3HWMZQk47x3xbMCqgC/w0dY3dw9rGqlweDDkrySQBcaScXWeR+Yb11Q==", "dev": true }, + "@types/randombytes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.0.tgz", + "integrity": "sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/wif": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.2.tgz", diff --git a/package.json b/package.json index 10b0d76..0b49862 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "@types/mocha": "^5.2.7", "@types/node": "^16.11.1", "@types/proxyquire": "^1.3.28", + "@types/randombytes": "^2.0.0", "@types/wif": "^2.0.2", "bip39": "^3.0.2", "bip65": "^1.0.1", @@ -80,6 +81,7 @@ "nyc": "^15.1.0", "prettier": "1.16.4", "proxyquire": "^2.0.1", + "randombytes": "^2.1.0", "regtest-client": "0.2.0", "rimraf": "^2.6.3", "ts-node": "^8.3.0", diff --git a/src/block.js b/src/block.js index 4e6ca84..2c130c3 100644 --- a/src/block.js +++ b/src/block.js @@ -14,6 +14,16 @@ const errorWitnessNotSegwit = new TypeError( 'Cannot compute witness commit for non-segwit block', ); class Block { + constructor() { + this.version = 1; + this.prevHash = undefined; + this.merkleRoot = undefined; + this.timestamp = 0; + this.witnessCommit = undefined; + this.bits = 0; + this.nonce = 0; + this.transactions = undefined; + } static fromBuffer(buffer) { if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)'); const bufferReader = new bufferutils_1.BufferReader(buffer); @@ -69,14 +79,6 @@ class Block { ) : rootHash; } - version = 1; - prevHash = undefined; - merkleRoot = undefined; - timestamp = 0; - witnessCommit = undefined; - bits = 0; - nonce = 0; - transactions = undefined; getWitnessCommit() { if (!txesHaveWitnessCommit(this.transactions)) return null; // The merkle root for the witness data is in an OP_RETURN output. diff --git a/src/bufferutils.js b/src/bufferutils.js index 933bebc..fcab0b7 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -53,8 +53,6 @@ exports.cloneBuffer = cloneBuffer; * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ class BufferWriter { - buffer; - offset; constructor(buffer, offset = 0) { this.buffer = buffer; this.offset = offset; @@ -96,8 +94,6 @@ exports.BufferWriter = BufferWriter; * Helper class for reading of bitcoin data types from a buffer. */ class BufferReader { - buffer; - offset; constructor(buffer, offset = 0) { this.buffer = buffer; this.offset = offset; diff --git a/src/psbt.js b/src/psbt.js index 84aa5b8..6162195 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -60,23 +60,6 @@ const DEFAULT_OPTS = { * Transaction object. Such as fee rate not being larger than maximumFeeRate etc. */ class Psbt { - data; - static fromBase64(data, opts = {}) { - const buffer = Buffer.from(data, 'base64'); - return this.fromBuffer(buffer, opts); - } - static fromHex(data, opts = {}) { - const buffer = Buffer.from(data, 'hex'); - return this.fromBuffer(buffer, opts); - } - static fromBuffer(buffer, opts = {}) { - const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer); - const psbt = new Psbt(opts, psbtBase); - checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE); - return psbt; - } - __CACHE; - opts; constructor(opts = {}, data = new bip174_1.Psbt(new PsbtTransaction())) { this.data = data; // set defaults @@ -106,6 +89,20 @@ class Psbt { dpew(this, '__CACHE', false, true); dpew(this, 'opts', false, true); } + static fromBase64(data, opts = {}) { + const buffer = Buffer.from(data, 'base64'); + return this.fromBuffer(buffer, opts); + } + static fromHex(data, opts = {}) { + const buffer = Buffer.from(data, 'hex'); + return this.fromBuffer(buffer, opts); + } + static fromBuffer(buffer, opts = {}) { + const psbtBase = bip174_1.Psbt.fromBuffer(buffer, transactionFromBuffer); + const psbt = new Psbt(opts, psbtBase); + checkTxForDupeIns(psbt.__CACHE.__TX, psbt.__CACHE); + return psbt; + } get inputCount() { return this.data.inputs.length; } @@ -625,7 +622,6 @@ const transactionFromBuffer = buffer => new PsbtTransaction(buffer); * It contains a bitcoinjs-lib Transaction object. */ class PsbtTransaction { - tx; constructor(buffer = Buffer.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) { this.tx = transaction_1.Transaction.fromBuffer(buffer); checkTxEmpty(this.tx); diff --git a/src/transaction.js b/src/transaction.js index e4ebf6f..5a29569 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -39,13 +39,12 @@ function isOutput(out) { return out.value !== undefined; } class Transaction { - static DEFAULT_SEQUENCE = 0xffffffff; - static SIGHASH_ALL = 0x01; - static SIGHASH_NONE = 0x02; - static SIGHASH_SINGLE = 0x03; - static SIGHASH_ANYONECANPAY = 0x80; - static ADVANCED_TRANSACTION_MARKER = 0x00; - static ADVANCED_TRANSACTION_FLAG = 0x01; + constructor() { + this.version = 1; + this.locktime = 0; + this.ins = []; + this.outs = []; + } static fromBuffer(buffer, _NO_STRICT) { const bufferReader = new bufferutils_1.BufferReader(buffer); const tx = new Transaction(); @@ -102,10 +101,6 @@ class Transaction { } return true; } - version = 1; - locktime = 0; - ins = []; - outs = []; isCoinbase() { return ( this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) @@ -400,3 +395,10 @@ class Transaction { } } exports.Transaction = Transaction; +Transaction.DEFAULT_SEQUENCE = 0xffffffff; +Transaction.SIGHASH_ALL = 0x01; +Transaction.SIGHASH_NONE = 0x02; +Transaction.SIGHASH_SINGLE = 0x03; +Transaction.SIGHASH_ANYONECANPAY = 0x80; +Transaction.ADVANCED_TRANSACTION_MARKER = 0x00; +Transaction.ADVANCED_TRANSACTION_FLAG = 0x01; diff --git a/test/tsconfig.json b/test/tsconfig.json index 4e4f529..6752ec5 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ESNEXT", + "target": "ES2020", "module": "commonjs", "outDir": "../", "declaration": false, diff --git a/tsconfig.json b/tsconfig.json index 3c74061..25f9d61 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ESNEXT", + "target": "ES2020", "module": "commonjs", "outDir": "./src", "declaration": true, From 84bc2ea6bf62ddb26fa9cb023b765d414f98f982 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 21 Oct 2021 08:58:53 +0900 Subject: [PATCH 557/568] Remove bip32, (User must bring their own) --- README.md | 12 ++++++++++-- package-lock.json | 42 +++++++++++++++++++++++++++++------------- package.json | 2 +- src/index.d.ts | 4 +--- src/index.js | 4 +--- test/psbt.spec.ts | 10 ++-------- ts_src/index.ts | 4 +--- 7 files changed, 45 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index f54dab4..b80283e 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,18 @@ You can find a [Web UI](https://bitcoincore.tech/apps/bitcoinjs-ui/index.html) t ## Installation ``` bash npm install bitcoinjs-lib +# optionally, install a key derivation library as well +npm install ecpair bip32 +# ecpair is the ECPair class for single keys +# bip32 is for generating HD keys ``` -Typically we support the [Node Maintenance LTS version](https://github.com/nodejs/Release). -If in doubt, see the [.travis.yml](.travis.yml) for what versions are used by our continuous integration tests. +Previous versions of the library included classes for key management (ECPair, HDNode(->"bip32")) but now these have been separated into different libraries. This lowers the bundle size significantly if you don't need to perform any crypto functions (converting private to public keys and deriving HD keys). + +Typically we support the [Node Maintenance LTS version](https://github.com/nodejs/Release). TypeScript target will be set +to the ECMAScript version in which all features are fully supported by current Active Node LTS. +However, depending on adoption among other environments (browsers etc.) we may keep the target back a year or two. +If in doubt, see the [main_ci.yml](.github/workflows/main_ci.yml) for what versions are used by our continuous integration tests. **WARNING**: We presently don't provide any tooling to verify that the release on `npm` matches GitHub. As such, you should verify anything downloaded by `npm` against your own verified copy. diff --git a/package-lock.json b/package-lock.json index 6512208..4d00add 100644 --- a/package-lock.json +++ b/package-lock.json @@ -545,6 +545,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, "requires": { "file-uri-to-path": "1.0.0" } @@ -555,15 +556,16 @@ "integrity": "sha512-i3X26uKJOkDTAalYAp0Er+qGMDhrbbh2o93/xiPyAN2s25KrClSpe3VXo/7mNJoqA5qfko8rLS2l3RWZgYmjKQ==" }, "bip32": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.4.tgz", - "integrity": "sha512-ioPytarPDIrWckWMuK4RNUtvwhvWEc2fvuhnO0WEwu732k5OLjUXv4rXi2c/KJHw9ZMNQMkYRJrBw81RujShGQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", + "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "dev": true, "requires": { "@types/node": "10.12.18", "bs58check": "^2.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "tiny-secp256k1": "^1.1.0", + "tiny-secp256k1": "^1.1.3", "typeforce": "^1.11.5", "wif": "^2.0.6" }, @@ -571,7 +573,8 @@ "@types/node": { "version": "10.12.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==" + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", + "dev": true } } }, @@ -616,7 +619,8 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true }, "brace-expansion": { "version": "1.1.11", @@ -640,7 +644,8 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true }, "browser-stdout": { "version": "1.3.1", @@ -846,6 +851,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -941,6 +947,7 @@ "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, "requires": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -954,12 +961,14 @@ "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true } } }, @@ -1020,7 +1029,8 @@ "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true }, "fill-keys": { "version": "1.0.2", @@ -1198,6 +1208,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -1223,6 +1234,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -1617,12 +1629,14 @@ "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true }, "minimatch": { "version": "3.0.4", @@ -1695,7 +1709,8 @@ "nan": { "version": "2.14.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true }, "node-environment-flags": { "version": "1.0.6", @@ -2453,6 +2468,7 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "dev": true, "requires": { "bindings": "^1.3.0", "bn.js": "^4.11.8", diff --git a/package.json b/package.json index 0b49862..9d15766 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "dependencies": { "bech32": "^2.0.0", "bip174": "^2.0.1", - "bip32": "^2.0.4", "bs58check": "^2.1.2", "create-hash": "^1.1.0", "typeforce": "^1.11.3", @@ -67,6 +66,7 @@ "@types/proxyquire": "^1.3.28", "@types/randombytes": "^2.0.0", "@types/wif": "^2.0.2", + "bip32": "^2.0.6", "bip39": "^3.0.2", "bip65": "^1.0.1", "bip68": "^1.0.3", diff --git a/src/index.d.ts b/src/index.d.ts index 1086e4b..5b339ca 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,15 +1,13 @@ -import * as bip32 from 'bip32'; import * as address from './address'; import * as crypto from './crypto'; import * as networks from './networks'; import * as payments from './payments'; import * as script from './script'; -export { address, bip32, crypto, networks, payments, script }; +export { address, crypto, networks, payments, script }; export { Block } from './block'; export { Psbt, PsbtTxInput, PsbtTxOutput, Signer, SignerAsync, HDSigner, HDSignerAsync, } from './psbt'; export { OPS as opcodes } from './ops'; export { Transaction } from './transaction'; -export { BIP32Interface } from 'bip32'; export { Network } from './networks'; export { Payment, PaymentCreator, PaymentOpts, Stack, StackElement, } from './payments'; export { Input as TxInput, Output as TxOutput } from './transaction'; diff --git a/src/index.js b/src/index.js index a643df8..983b0cc 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,6 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); -exports.Transaction = exports.opcodes = exports.Psbt = exports.Block = exports.script = exports.payments = exports.networks = exports.crypto = exports.bip32 = exports.address = void 0; -const bip32 = require('bip32'); -exports.bip32 = bip32; +exports.Transaction = exports.opcodes = exports.Psbt = exports.Block = exports.script = exports.payments = exports.networks = exports.crypto = exports.address = void 0; const address = require('./address'); exports.address = address; const crypto = require('./crypto'); diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 1c6cb09..05d4468 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,16 +1,10 @@ import * as assert from 'assert'; +import * as bip32 from 'bip32'; import * as crypto from 'crypto'; import { ECPair } from 'ecpair'; import { describe, it } from 'mocha'; -import { - bip32, - networks as NETWORKS, - payments, - Psbt, - Signer, - SignerAsync, -} from '..'; +import { networks as NETWORKS, payments, Psbt, Signer, SignerAsync } from '..'; import * as preFixtures from './fixtures/psbt.json'; diff --git a/ts_src/index.ts b/ts_src/index.ts index 66b2f9c..4cc82bf 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -1,11 +1,10 @@ -import * as bip32 from 'bip32'; import * as address from './address'; import * as crypto from './crypto'; import * as networks from './networks'; import * as payments from './payments'; import * as script from './script'; -export { address, bip32, crypto, networks, payments, script }; +export { address, crypto, networks, payments, script }; export { Block } from './block'; export { @@ -20,7 +19,6 @@ export { export { OPS as opcodes } from './ops'; export { Transaction } from './transaction'; -export { BIP32Interface } from 'bip32'; export { Network } from './networks'; export { Payment, From 8f31e8882434a666bd67286b3e824b8c6d97de32 Mon Sep 17 00:00:00 2001 From: "YU-AN, CHEN" <59487andy@gmail.com> Date: Wed, 3 Nov 2021 18:25:59 +0800 Subject: [PATCH 558/568] fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b80283e..d974624 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ If you're familiar with how to use browserify, ignore this and carry on, otherwi **NOTE**: We use Node Maintenance LTS features, if you need strict ES5, use [`--transform babelify`](https://github.com/babel/babelify) in conjunction with your `browserify` step (using an [`es2015`](https://babeljs.io/docs/plugins/preset-es2015/) preset). -**WARNING**: iOS devices have [problems](https://github.com/feross/buffer/issues/136), use atleast [buffer@5.0.5](https://github.com/feross/buffer/pull/155) or greater, and enforce the test suites (for `Buffer`, and any other dependency) pass before use. +**WARNING**: iOS devices have [problems](https://github.com/feross/buffer/issues/136), use at least [buffer@5.0.5](https://github.com/feross/buffer/pull/155) or greater, and enforce the test suites (for `Buffer`, and any other dependency) pass before use. ### Typescript or VSCode users Type declarations for Typescript are included in this library. Normal installation should include all the needed type information. From e0f1620ee35372f94c778235f054964488c30bf5 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Thu, 11 Nov 2021 21:18:41 +0900 Subject: [PATCH 559/568] Fix error message for Bitcoin Core v22.0 --- test/integration/cltv.spec.ts | 2 +- test/integration/csv.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/cltv.spec.ts b/test/integration/cltv.spec.ts index 4b2eb66..c1a52de 100644 --- a/test/integration/cltv.spec.ts +++ b/test/integration/cltv.spec.ts @@ -250,7 +250,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', () => { await regtestUtils.broadcast(tx.toHex()).catch(err => { assert.throws(() => { if (err) throw err; - }, /Error: non-final \(code 64\)/); + }, /Error: non-final/); }); }, ); diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts index 81b0943..9993d5c 100644 --- a/test/integration/csv.spec.ts +++ b/test/integration/csv.spec.ts @@ -219,7 +219,7 @@ describe('bitcoinjs-lib (transactions w/ CSV)', () => { await regtestUtils.broadcast(tx.toHex()).catch(err => { assert.throws(() => { if (err) throw err; - }, /Error: non-BIP68-final \(code 64\)/); + }, /Error: non-BIP68-final/); }); }, ); From 45187a32d06349546ed91e4d813c7bc8eb385433 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 12 Nov 2021 08:33:18 +0900 Subject: [PATCH 560/568] Add taggedHash, sigHash v1 Co-authored-by: Brandon Black <brandonblack@bitgo.com> Co-authored-by: Otto Allmendinger <otto@bitgo.com> Co-authored-by: Tyler Levine <tyler@bitgo.com> Co-authored-by: Daniel McNally <danielmcnally@bitgo.com> --- package-lock.json | 6 +- package.json | 2 +- src/bufferutils.d.ts | 2 + src/bufferutils.js | 9 ++ src/crypto.d.ts | 4 + src/crypto.js | 24 ++++- src/index.d.ts | 1 + src/transaction.d.ts | 4 + src/transaction.js | 146 ++++++++++++++++++++++++++++- test/bufferutils.spec.ts | 17 ++++ test/crypto.spec.ts | 15 ++- test/fixtures/crypto.json | 77 +++++++++------- test/fixtures/transaction.json | 81 ++++++++++++++++ test/transaction.spec.ts | 21 +++++ ts_src/bufferutils.ts | 11 +++ ts_src/crypto.ts | 24 +++++ ts_src/index.ts | 1 + ts_src/transaction.ts | 163 ++++++++++++++++++++++++++++++++- 18 files changed, 559 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4d00add..f07cf3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -416,9 +416,9 @@ "dev": true }, "@types/node": { - "version": "16.11.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", - "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", + "version": "16.11.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", + "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==", "dev": true }, "@types/proxyquire": { diff --git a/package.json b/package.json index 9d15766..0eb793e 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "@types/bs58check": "^2.1.0", "@types/create-hash": "^1.2.2", "@types/mocha": "^5.2.7", - "@types/node": "^16.11.1", + "@types/node": "^16.11.7", "@types/proxyquire": "^1.3.28", "@types/randombytes": "^2.0.0", "@types/wif": "^2.0.2", diff --git a/src/bufferutils.d.ts b/src/bufferutils.d.ts index 40f89b3..b1d8966 100644 --- a/src/bufferutils.d.ts +++ b/src/bufferutils.d.ts @@ -11,6 +11,7 @@ export declare function cloneBuffer(buffer: Buffer): Buffer; export declare class BufferWriter { buffer: Buffer; offset: number; + static withCapacity(size: number): BufferWriter; constructor(buffer: Buffer, offset?: number); writeUInt8(i: number): void; writeInt32(i: number): void; @@ -20,6 +21,7 @@ export declare class BufferWriter { writeSlice(slice: Buffer): void; writeVarSlice(slice: Buffer): void; writeVector(vector: Buffer[]): void; + end(): Buffer; } /** * Helper class for reading of bitcoin data types from a buffer. diff --git a/src/bufferutils.js b/src/bufferutils.js index fcab0b7..83a013b 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -58,6 +58,9 @@ class BufferWriter { this.offset = offset; typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); } + static withCapacity(size) { + return new BufferWriter(Buffer.alloc(size)); + } writeUInt8(i) { this.offset = this.buffer.writeUInt8(i, this.offset); } @@ -88,6 +91,12 @@ class BufferWriter { this.writeVarInt(vector.length); vector.forEach(buf => this.writeVarSlice(buf)); } + end() { + if (this.buffer.length === this.offset) { + return this.buffer; + } + throw new Error(`buffer size ${this.buffer.length}, offset ${this.offset}`); + } } exports.BufferWriter = BufferWriter; /** diff --git a/src/crypto.d.ts b/src/crypto.d.ts index 1743681..ec088f3 100644 --- a/src/crypto.d.ts +++ b/src/crypto.d.ts @@ -4,3 +4,7 @@ export declare function sha1(buffer: Buffer): Buffer; export declare function sha256(buffer: Buffer): Buffer; export declare function hash160(buffer: Buffer): Buffer; export declare function hash256(buffer: Buffer): Buffer; +declare const TAGS: readonly ["BIP0340/challenge", "BIP0340/aux", "BIP0340/nonce", "TapLeaf", "TapBranch", "TapSighash", "TapTweak", "KeyAgg list", "KeyAgg coefficient"]; +export declare type TaggedHashPrefix = typeof TAGS[number]; +export declare function taggedHash(prefix: TaggedHashPrefix, data: Buffer): Buffer; +export {}; diff --git a/src/crypto.js b/src/crypto.js index f53b041..3c308da 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -1,6 +1,6 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); -exports.hash256 = exports.hash160 = exports.sha256 = exports.sha1 = exports.ripemd160 = void 0; +exports.taggedHash = exports.hash256 = exports.hash160 = exports.sha256 = exports.sha1 = exports.ripemd160 = void 0; const createHash = require('create-hash'); function ripemd160(buffer) { try { @@ -34,3 +34,25 @@ function hash256(buffer) { return sha256(sha256(buffer)); } exports.hash256 = hash256; +const TAGS = [ + 'BIP0340/challenge', + 'BIP0340/aux', + 'BIP0340/nonce', + 'TapLeaf', + 'TapBranch', + 'TapSighash', + 'TapTweak', + 'KeyAgg list', + 'KeyAgg coefficient', +]; +/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */ +const TAGGED_HASH_PREFIXES = Object.fromEntries( + TAGS.map(tag => { + const tagHash = sha256(Buffer.from(tag)); + return [tag, Buffer.concat([tagHash, tagHash])]; + }), +); +function taggedHash(prefix, data) { + return sha256(Buffer.concat([TAGGED_HASH_PREFIXES[prefix], data])); +} +exports.taggedHash = taggedHash; diff --git a/src/index.d.ts b/src/index.d.ts index 5b339ca..b93c2aa 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -5,6 +5,7 @@ import * as payments from './payments'; import * as script from './script'; export { address, crypto, networks, payments, script }; export { Block } from './block'; +export { TaggedHashPrefix } from './crypto'; export { Psbt, PsbtTxInput, PsbtTxOutput, Signer, SignerAsync, HDSigner, HDSignerAsync, } from './psbt'; export { OPS as opcodes } from './ops'; export { Transaction } from './transaction'; diff --git a/src/transaction.d.ts b/src/transaction.d.ts index c4de954..613706b 100644 --- a/src/transaction.d.ts +++ b/src/transaction.d.ts @@ -12,10 +12,13 @@ export interface Input { } export declare class Transaction { static readonly DEFAULT_SEQUENCE = 4294967295; + static readonly SIGHASH_DEFAULT = 0; static readonly SIGHASH_ALL = 1; static readonly SIGHASH_NONE = 2; static readonly SIGHASH_SINGLE = 3; static readonly SIGHASH_ANYONECANPAY = 128; + static readonly SIGHASH_OUTPUT_MASK = 3; + static readonly SIGHASH_INPUT_MASK = 128; static readonly ADVANCED_TRANSACTION_MARKER = 0; static readonly ADVANCED_TRANSACTION_FLAG = 1; static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction; @@ -42,6 +45,7 @@ export declare class Transaction { * This hash can then be used to sign the provided transaction input. */ hashForSignature(inIndex: number, prevOutScript: Buffer, hashType: number): Buffer; + hashForWitnessV1(inIndex: number, prevOutScripts: Buffer[], values: number[], hashType: number, leafHash?: Buffer, annex?: Buffer): Buffer; hashForWitnessV0(inIndex: number, prevOutScript: Buffer, value: number, hashType: number): Buffer; getHash(forWitness?: boolean): Buffer; getId(): string; diff --git a/src/transaction.js b/src/transaction.js index 5a29569..6f1382c 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -20,7 +20,7 @@ function vectorSize(someVector) { }, 0) ); } -const EMPTY_SCRIPT = Buffer.allocUnsafe(0); +const EMPTY_BUFFER = Buffer.allocUnsafe(0); const EMPTY_WITNESS = []; const ZERO = Buffer.from( '0000000000000000000000000000000000000000000000000000000000000000', @@ -32,7 +32,7 @@ const ONE = Buffer.from( ); const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex'); const BLANK_OUTPUT = { - script: EMPTY_SCRIPT, + script: EMPTY_BUFFER, valueBuffer: VALUE_UINT64_MAX, }; function isOutput(out) { @@ -124,7 +124,7 @@ class Transaction { this.ins.push({ hash, index, - script: scriptSig || EMPTY_SCRIPT, + script: scriptSig || EMPTY_BUFFER, sequence: sequence, witness: EMPTY_WITNESS, }) - 1 @@ -247,7 +247,7 @@ class Transaction { } else { // "blank" others input scripts txTmp.ins.forEach(input => { - input.script = EMPTY_SCRIPT; + input.script = EMPTY_BUFFER; }); txTmp.ins[inIndex].script = ourScript; } @@ -257,6 +257,141 @@ class Transaction { txTmp.__toBuffer(buffer, 0, false); return bcrypto.hash256(buffer); } + hashForWitnessV1(inIndex, prevOutScripts, values, hashType, leafHash, annex) { + // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#common-signature-message + typeforce( + types.tuple( + types.UInt32, + typeforce.arrayOf(types.Buffer), + typeforce.arrayOf(types.Satoshi), + types.UInt32, + ), + arguments, + ); + if ( + values.length !== this.ins.length || + prevOutScripts.length !== this.ins.length + ) { + throw new Error('Must supply prevout script and value for all inputs'); + } + const outputType = + hashType === Transaction.SIGHASH_DEFAULT + ? Transaction.SIGHASH_ALL + : hashType & Transaction.SIGHASH_OUTPUT_MASK; + const inputType = hashType & Transaction.SIGHASH_INPUT_MASK; + const isAnyoneCanPay = inputType === Transaction.SIGHASH_ANYONECANPAY; + const isNone = outputType === Transaction.SIGHASH_NONE; + const isSingle = outputType === Transaction.SIGHASH_SINGLE; + let hashPrevouts = EMPTY_BUFFER; + let hashAmounts = EMPTY_BUFFER; + let hashScriptPubKeys = EMPTY_BUFFER; + let hashSequences = EMPTY_BUFFER; + let hashOutputs = EMPTY_BUFFER; + if (!isAnyoneCanPay) { + let bufferWriter = bufferutils_1.BufferWriter.withCapacity( + 36 * this.ins.length, + ); + this.ins.forEach(txIn => { + bufferWriter.writeSlice(txIn.hash); + bufferWriter.writeUInt32(txIn.index); + }); + hashPrevouts = bcrypto.sha256(bufferWriter.end()); + bufferWriter = bufferutils_1.BufferWriter.withCapacity( + 8 * this.ins.length, + ); + values.forEach(value => bufferWriter.writeUInt64(value)); + hashAmounts = bcrypto.sha256(bufferWriter.end()); + bufferWriter = bufferutils_1.BufferWriter.withCapacity( + prevOutScripts.map(varSliceSize).reduce((a, b) => a + b), + ); + prevOutScripts.forEach(prevOutScript => + bufferWriter.writeVarSlice(prevOutScript), + ); + hashScriptPubKeys = bcrypto.sha256(bufferWriter.end()); + bufferWriter = bufferutils_1.BufferWriter.withCapacity( + 4 * this.ins.length, + ); + this.ins.forEach(txIn => bufferWriter.writeUInt32(txIn.sequence)); + hashSequences = bcrypto.sha256(bufferWriter.end()); + } + if (!(isNone || isSingle)) { + const txOutsSize = this.outs + .map(output => 8 + varSliceSize(output.script)) + .reduce((a, b) => a + b); + const bufferWriter = bufferutils_1.BufferWriter.withCapacity(txOutsSize); + this.outs.forEach(out => { + bufferWriter.writeUInt64(out.value); + bufferWriter.writeVarSlice(out.script); + }); + hashOutputs = bcrypto.sha256(bufferWriter.end()); + } else if (isSingle && inIndex < this.outs.length) { + const output = this.outs[inIndex]; + const bufferWriter = bufferutils_1.BufferWriter.withCapacity( + 8 + varSliceSize(output.script), + ); + bufferWriter.writeUInt64(output.value); + bufferWriter.writeVarSlice(output.script); + hashOutputs = bcrypto.sha256(bufferWriter.end()); + } + const spendType = (leafHash ? 2 : 0) + (annex ? 1 : 0); + // Length calculation from: + // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-14 + // With extension from: + // https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#signature-validation + const sigMsgSize = + 174 - + (isAnyoneCanPay ? 49 : 0) - + (isNone ? 32 : 0) + + (annex ? 32 : 0) + + (leafHash ? 37 : 0); + const sigMsgWriter = bufferutils_1.BufferWriter.withCapacity(sigMsgSize); + sigMsgWriter.writeUInt8(hashType); + // Transaction + sigMsgWriter.writeInt32(this.version); + sigMsgWriter.writeUInt32(this.locktime); + sigMsgWriter.writeSlice(hashPrevouts); + sigMsgWriter.writeSlice(hashAmounts); + sigMsgWriter.writeSlice(hashScriptPubKeys); + sigMsgWriter.writeSlice(hashSequences); + if (!(isNone || isSingle)) { + sigMsgWriter.writeSlice(hashOutputs); + } + // Input + sigMsgWriter.writeUInt8(spendType); + if (isAnyoneCanPay) { + const input = this.ins[inIndex]; + sigMsgWriter.writeSlice(input.hash); + sigMsgWriter.writeUInt32(input.index); + sigMsgWriter.writeUInt64(values[inIndex]); + sigMsgWriter.writeVarSlice(prevOutScripts[inIndex]); + sigMsgWriter.writeUInt32(input.sequence); + } else { + sigMsgWriter.writeUInt32(inIndex); + } + if (annex) { + const bufferWriter = bufferutils_1.BufferWriter.withCapacity( + varSliceSize(annex), + ); + bufferWriter.writeVarSlice(annex); + sigMsgWriter.writeSlice(bcrypto.sha256(bufferWriter.end())); + } + // Output + if (isSingle) { + sigMsgWriter.writeSlice(hashOutputs); + } + // BIP342 extension + if (leafHash) { + sigMsgWriter.writeSlice(leafHash); + sigMsgWriter.writeUInt8(0); + sigMsgWriter.writeUInt32(0xffffffff); + } + // Extra zero byte because: + // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-19 + return bcrypto.taggedHash( + 'TapSighash', + Buffer.concat([Buffer.of(0x00), sigMsgWriter.end()]), + ); + } hashForWitnessV0(inIndex, prevOutScript, value, hashType) { typeforce( types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), @@ -396,9 +531,12 @@ class Transaction { } exports.Transaction = Transaction; Transaction.DEFAULT_SEQUENCE = 0xffffffff; +Transaction.SIGHASH_DEFAULT = 0x00; Transaction.SIGHASH_ALL = 0x01; Transaction.SIGHASH_NONE = 0x02; Transaction.SIGHASH_SINGLE = 0x03; Transaction.SIGHASH_ANYONECANPAY = 0x80; +Transaction.SIGHASH_OUTPUT_MASK = 0x03; +Transaction.SIGHASH_INPUT_MASK = 0x80; Transaction.ADVANCED_TRANSACTION_MARKER = 0x00; Transaction.ADVANCED_TRANSACTION_FLAG = 0x01; diff --git a/test/bufferutils.spec.ts b/test/bufferutils.spec.ts index 213b156..0f1f1a9 100644 --- a/test/bufferutils.spec.ts +++ b/test/bufferutils.spec.ts @@ -66,6 +66,13 @@ describe('bufferutils', () => { ); } + it('withCapacity', () => { + const expectedBuffer = Buffer.from('04030201', 'hex'); + const withCapacity = BufferWriter.withCapacity(4); + withCapacity.writeInt32(0x01020304); + testBuffer(withCapacity, expectedBuffer); + }); + it('writeUint8', () => { const values = [0, 1, 254, 255]; const expectedBuffer = Buffer.from([0, 1, 0xfe, 0xff]); @@ -277,6 +284,16 @@ describe('bufferutils', () => { }); testBuffer(bufferWriter, expectedBuffer); }); + + it('end', () => { + const expected = Buffer.from('0403020108070605', 'hex'); + const bufferWriter = BufferWriter.withCapacity(8); + bufferWriter.writeUInt32(0x01020304); + bufferWriter.writeUInt32(0x05060708); + const result = bufferWriter.end(); + testBuffer(bufferWriter, result); + testBuffer(bufferWriter, expected); + }); }); describe('BufferReader', () => { diff --git a/test/crypto.spec.ts b/test/crypto.spec.ts index 89ffabb..0482ec9 100644 --- a/test/crypto.spec.ts +++ b/test/crypto.spec.ts @@ -1,12 +1,12 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; -import { crypto as bcrypto } from '..'; +import { crypto as bcrypto, TaggedHashPrefix } from '..'; import * as fixtures from './fixtures/crypto.json'; describe('crypto', () => { ['hash160', 'hash256', 'ripemd160', 'sha1', 'sha256'].forEach(algorithm => { describe(algorithm, () => { - fixtures.forEach(f => { + fixtures.hashes.forEach(f => { const fn = (bcrypto as any)[algorithm]; const expected = (f as any)[algorithm]; @@ -19,4 +19,15 @@ describe('crypto', () => { }); }); }); + + describe('taggedHash', () => { + fixtures.taggedHash.forEach(f => { + const bytes = Buffer.from(f.hex, 'hex'); + const expected = Buffer.from(f.result, 'hex'); + it(`returns ${f.result} for taggedHash "${f.tag}" of ${f.hex}`, () => { + const actual = bcrypto.taggedHash(f.tag as TaggedHashPrefix, bytes); + assert.strictEqual(actual.toString('hex'), expected.toString('hex')); + }); + }); + }); }); diff --git a/test/fixtures/crypto.json b/test/fixtures/crypto.json index 102b50a..1d1976b 100644 --- a/test/fixtures/crypto.json +++ b/test/fixtures/crypto.json @@ -1,34 +1,43 @@ -[ - { - "hex": "0000000000000001", - "hash160": "cdb00698f02afd929ffabea308340fa99ac2afa8", - "hash256": "3ae5c198d17634e79059c2cd735491553d22c4e09d1d9fea3ecf214565df2284", - "ripemd160": "8d1a05d1bc08870968eb8a81ad4393fd3aac6633", - "sha1": "cb473678976f425d6ec1339838f11011007ad27d", - "sha256": "cd2662154e6d76b2b2b92e70c0cac3ccf534f9b74eb5b89819ec509083d00a50" - }, - { - "hex": "0101010101010101", - "hash160": "abaf1119f83e384210fe8e222eac76e2f0da39dc", - "hash256": "728338d99f356175c4945ef5cccfa61b7b56143cbbf426ddd0e0fc7cfe8c3c23", - "ripemd160": "5825701b4b9767fd35063b286dca3582853e0630", - "sha1": "c0357a32ed1f6a03be92dd094476f7f1a2e214ec", - "sha256": "04abc8821a06e5a30937967d11ad10221cb5ac3b5273e434f1284ee87129a061" - }, - { - "hex": "ffffffffffffffff", - "hash160": "f86221f5a1fca059a865c0b7d374dfa9d5f3aeb4", - "hash256": "752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad", - "ripemd160": "cb760221600ed34337ca3ab70016b5f58c838120", - "sha1": "be673e8a56eaa9d8c1d35064866701c11ef8e089", - "sha256": "12a3ae445661ce5dee78d0650d33362dec29c4f82af05e7e57fb595bbbacf0ca" - }, - { - "hex": "4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e65632061742066617563696275732073617069656e2c2076656c20666163696c6973697320617263752e20536564207574206d61737361206e6962682e205574206d6f6c6c69732070756c76696e6172206d617373612e20557420756c6c616d636f7270657220646f6c6f7220656e696d2c20696e206d6f6c657374696520656e696d20636f6e64696d656e74756d2061632e20416c697175616d206572617420766f6c75747061742e204e756c6c6120736f64616c657320617420647569206e656320", - "hash160": "9763e6b367c363bd6b88a7b361c98e6beee243a5", - "hash256": "033588797115feb3545052670cac2a46584ab3cb460de63756ee0275e66b5799", - "ripemd160": "cad8593dcdef12ee334c97bab9787f07b3f3a1a5", - "sha1": "10d96fb43aca84e342206887bbeed3065d4e4344", - "sha256": "a7fb8276035057ed6479c5f2305a96da100ac43f0ac10f277e5ab8c5457429da" - } -] +{ + "hashes": [ + { + "hex": "0000000000000001", + "hash160": "cdb00698f02afd929ffabea308340fa99ac2afa8", + "hash256": "3ae5c198d17634e79059c2cd735491553d22c4e09d1d9fea3ecf214565df2284", + "ripemd160": "8d1a05d1bc08870968eb8a81ad4393fd3aac6633", + "sha1": "cb473678976f425d6ec1339838f11011007ad27d", + "sha256": "cd2662154e6d76b2b2b92e70c0cac3ccf534f9b74eb5b89819ec509083d00a50" + }, + { + "hex": "0101010101010101", + "hash160": "abaf1119f83e384210fe8e222eac76e2f0da39dc", + "hash256": "728338d99f356175c4945ef5cccfa61b7b56143cbbf426ddd0e0fc7cfe8c3c23", + "ripemd160": "5825701b4b9767fd35063b286dca3582853e0630", + "sha1": "c0357a32ed1f6a03be92dd094476f7f1a2e214ec", + "sha256": "04abc8821a06e5a30937967d11ad10221cb5ac3b5273e434f1284ee87129a061" + }, + { + "hex": "ffffffffffffffff", + "hash160": "f86221f5a1fca059a865c0b7d374dfa9d5f3aeb4", + "hash256": "752adad0a7b9ceca853768aebb6965eca126a62965f698a0c1bc43d83db632ad", + "ripemd160": "cb760221600ed34337ca3ab70016b5f58c838120", + "sha1": "be673e8a56eaa9d8c1d35064866701c11ef8e089", + "sha256": "12a3ae445661ce5dee78d0650d33362dec29c4f82af05e7e57fb595bbbacf0ca" + }, + { + "hex": "4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e65632061742066617563696275732073617069656e2c2076656c20666163696c6973697320617263752e20536564207574206d61737361206e6962682e205574206d6f6c6c69732070756c76696e6172206d617373612e20557420756c6c616d636f7270657220646f6c6f7220656e696d2c20696e206d6f6c657374696520656e696d20636f6e64696d656e74756d2061632e20416c697175616d206572617420766f6c75747061742e204e756c6c6120736f64616c657320617420647569206e656320", + "hash160": "9763e6b367c363bd6b88a7b361c98e6beee243a5", + "hash256": "033588797115feb3545052670cac2a46584ab3cb460de63756ee0275e66b5799", + "ripemd160": "cad8593dcdef12ee334c97bab9787f07b3f3a1a5", + "sha1": "10d96fb43aca84e342206887bbeed3065d4e4344", + "sha256": "a7fb8276035057ed6479c5f2305a96da100ac43f0ac10f277e5ab8c5457429da" + } + ], + "taggedHash": [ + { + "tag": "TapTweak", + "hex": "0101010101010101", + "result": "71ae15bad52efcecf4c9f672bfbded68a4adb8258f1b95f0d06aefdb5ebd14e9" + } + ] +} \ No newline at end of file diff --git a/test/fixtures/transaction.json b/test/fixtures/transaction.json index 6bf6090..979549a 100644 --- a/test/fixtures/transaction.json +++ b/test/fixtures/transaction.json @@ -808,6 +808,87 @@ "value": 987654321 } ], + "taprootSigning": [ + { + "description": "P2TR key path", + "utxos": [ + { + "scriptHex": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343", + "value": 420000000 + }, + { + "scriptHex": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3", + "value": 462000000 + }, + { + "scriptHex": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac", + "value": 294000000 + }, + { + "scriptHex": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e", + "value": 504000000 + }, + { + "scriptHex": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605", + "value": 630000000 + }, + { + "scriptHex": "00147dd65592d0ab2fe0d0257d571abf032cd9db93dc", + "value": 378000000 + }, + { + "scriptHex": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831", + "value": 672000000 + }, + { + "scriptHex": "51200f63ca2c7639b9bb4be0465cc0aa3ee78a0761ba5f5f7d6ff8eab340f09da561", + "value": 546000000 + }, + { + "scriptHex": "5120053690babeabbb7850c32eead0acf8df990ced79f7a31e358fabf2658b4bc587", + "value": 588000000 + } + ], + "txHex": "02000000097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a418420000000000fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffff0c638ca38362001f5e128a01ae2b379288eb22cfaf903652b2ec1c88588f487a0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd05000000000000000000081efa267f1f0e46e054ecec01773de7c844721e010c2db5d5864a6a6b53e013a010000000000000000a690669c3c4a62507d93609810c6de3f99d1a6e311fe39dd23683d695c07bdee0000000000ffffffff727ab5f877438496f8613ca84002ff38e8292f7bd11f0a9b9b83ebd16779669e0100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0065cd1d", + "cases": [ + { + "vin": 0, + "typeHex": "03", + "hash": "7e584883b084ace0469c6962a9a7d2a9060e1f3c218ab40d32c77651482122bc" + }, + { + "vin": 1, + "typeHex": "83", + "hash": "325a644af47e8a5a2591cda0ab0723978537318f10e6a63d4eed783b96a71a4d" + }, + { + "vin": 3, + "typeHex": "01", + "hash": "6ffd256e108685b41831385f57eebf2fca041bc6b5e607ea11b3e03d4cf9d9ba" + }, + { + "vin": 4, + "typeHex": "00", + "hash": "9f90136737540ccc18707e1fd398ad222a1a7e4dd65cbfd22dbe4660191efa58" + }, + { + "vin": 6, + "typeHex": "02", + "hash": "835c9ab6084ed9a8ae9b7cda21e0aa797aca3b76a54bd1e3c7db093f6c57e23f" + }, + { + "vin": 7, + "typeHex": "82", + "hash": "df1cca638283c667084b8ffe6bf6e116cc5a53cf7ae1202c5fee45a9085f1ba5" + }, + { + "vin": 8, + "typeHex": "81", + "hash": "30319859ca79ea1b7a9782e9daebc46e4ca4ca2bc04c9c53b2ec87fa83a526bd" + } + ] + } + ], "invalid": { "addInput": [ { diff --git a/test/transaction.spec.ts b/test/transaction.spec.ts index 6744545..13d64d1 100644 --- a/test/transaction.spec.ts +++ b/test/transaction.spec.ts @@ -328,6 +328,27 @@ describe('Transaction', () => { }); }); + describe('taprootSigning', () => { + fixtures.taprootSigning.forEach(f => { + const tx = Transaction.fromHex(f.txHex); + const prevOutScripts = f.utxos.map(({ scriptHex }) => + Buffer.from(scriptHex, 'hex'), + ); + const values = f.utxos.map(({ value }) => value); + + f.cases.forEach(c => { + let hash: Buffer; + + it(`should hash to ${c.hash} for ${f.description}:${c.vin}`, () => { + const hashType = Buffer.from(c.typeHex, 'hex').readUInt8(0); + + hash = tx.hashForWitnessV1(c.vin, prevOutScripts, values, hashType); + assert.strictEqual(hash.toString('hex'), c.hash); + }); + }); + }); + }); + describe('setWitness', () => { it('only accepts a a witness stack (Array of Buffers)', () => { assert.throws(() => { diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 43171c4..901d72a 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -58,6 +58,10 @@ export function cloneBuffer(buffer: Buffer): Buffer { * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ export class BufferWriter { + static withCapacity(size: number): BufferWriter { + return new BufferWriter(Buffer.alloc(size)); + } + constructor(public buffer: Buffer, public offset: number = 0) { typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]); } @@ -99,6 +103,13 @@ export class BufferWriter { this.writeVarInt(vector.length); vector.forEach((buf: Buffer) => this.writeVarSlice(buf)); } + + end(): Buffer { + if (this.buffer.length === this.offset) { + return this.buffer; + } + throw new Error(`buffer size ${this.buffer.length}, offset ${this.offset}`); + } } /** diff --git a/ts_src/crypto.ts b/ts_src/crypto.ts index 7f69c40..b7c355a 100644 --- a/ts_src/crypto.ts +++ b/ts_src/crypto.ts @@ -31,3 +31,27 @@ export function hash160(buffer: Buffer): Buffer { export function hash256(buffer: Buffer): Buffer { return sha256(sha256(buffer)); } + +const TAGS = [ + 'BIP0340/challenge', + 'BIP0340/aux', + 'BIP0340/nonce', + 'TapLeaf', + 'TapBranch', + 'TapSighash', + 'TapTweak', + 'KeyAgg list', + 'KeyAgg coefficient', +] as const; +export type TaggedHashPrefix = typeof TAGS[number]; +/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */ +const TAGGED_HASH_PREFIXES = Object.fromEntries( + TAGS.map(tag => { + const tagHash = sha256(Buffer.from(tag)); + return [tag, Buffer.concat([tagHash, tagHash])]; + }), +) as { [k in TaggedHashPrefix]: Buffer }; + +export function taggedHash(prefix: TaggedHashPrefix, data: Buffer): Buffer { + return sha256(Buffer.concat([TAGGED_HASH_PREFIXES[prefix], data])); +} diff --git a/ts_src/index.ts b/ts_src/index.ts index 4cc82bf..d8b8619 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -7,6 +7,7 @@ import * as script from './script'; export { address, crypto, networks, payments, script }; export { Block } from './block'; +export { TaggedHashPrefix } from './crypto'; export { Psbt, PsbtTxInput, diff --git a/ts_src/transaction.ts b/ts_src/transaction.ts index c5dde9a..416f20e 100644 --- a/ts_src/transaction.ts +++ b/ts_src/transaction.ts @@ -27,7 +27,7 @@ function vectorSize(someVector: Buffer[]): number { ); } -const EMPTY_SCRIPT: Buffer = Buffer.allocUnsafe(0); +const EMPTY_BUFFER: Buffer = Buffer.allocUnsafe(0); const EMPTY_WITNESS: Buffer[] = []; const ZERO: Buffer = Buffer.from( '0000000000000000000000000000000000000000000000000000000000000000', @@ -39,7 +39,7 @@ const ONE: Buffer = Buffer.from( ); const VALUE_UINT64_MAX: Buffer = Buffer.from('ffffffffffffffff', 'hex'); const BLANK_OUTPUT = { - script: EMPTY_SCRIPT, + script: EMPTY_BUFFER, valueBuffer: VALUE_UINT64_MAX, }; @@ -62,10 +62,13 @@ export interface Input { export class Transaction { static readonly DEFAULT_SEQUENCE = 0xffffffff; + static readonly SIGHASH_DEFAULT = 0x00; static readonly SIGHASH_ALL = 0x01; static readonly SIGHASH_NONE = 0x02; static readonly SIGHASH_SINGLE = 0x03; static readonly SIGHASH_ANYONECANPAY = 0x80; + static readonly SIGHASH_OUTPUT_MASK = 0x03; + static readonly SIGHASH_INPUT_MASK = 0x80; static readonly ADVANCED_TRANSACTION_MARKER = 0x00; static readonly ADVANCED_TRANSACTION_FLAG = 0x01; @@ -174,7 +177,7 @@ export class Transaction { this.ins.push({ hash, index, - script: scriptSig || EMPTY_SCRIPT, + script: scriptSig || EMPTY_BUFFER, sequence: sequence as number, witness: EMPTY_WITNESS, }) - 1 @@ -326,7 +329,7 @@ export class Transaction { } else { // "blank" others input scripts txTmp.ins.forEach(input => { - input.script = EMPTY_SCRIPT; + input.script = EMPTY_BUFFER; }); txTmp.ins[inIndex].script = ourScript; } @@ -339,6 +342,158 @@ export class Transaction { return bcrypto.hash256(buffer); } + hashForWitnessV1( + inIndex: number, + prevOutScripts: Buffer[], + values: number[], + hashType: number, + leafHash?: Buffer, + annex?: Buffer, + ): Buffer { + // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#common-signature-message + typeforce( + types.tuple( + types.UInt32, + typeforce.arrayOf(types.Buffer), + typeforce.arrayOf(types.Satoshi), + types.UInt32, + ), + arguments, + ); + + if ( + values.length !== this.ins.length || + prevOutScripts.length !== this.ins.length + ) { + throw new Error('Must supply prevout script and value for all inputs'); + } + + const outputType = + hashType === Transaction.SIGHASH_DEFAULT + ? Transaction.SIGHASH_ALL + : hashType & Transaction.SIGHASH_OUTPUT_MASK; + + const inputType = hashType & Transaction.SIGHASH_INPUT_MASK; + + const isAnyoneCanPay = inputType === Transaction.SIGHASH_ANYONECANPAY; + const isNone = outputType === Transaction.SIGHASH_NONE; + const isSingle = outputType === Transaction.SIGHASH_SINGLE; + + let hashPrevouts = EMPTY_BUFFER; + let hashAmounts = EMPTY_BUFFER; + let hashScriptPubKeys = EMPTY_BUFFER; + let hashSequences = EMPTY_BUFFER; + let hashOutputs = EMPTY_BUFFER; + + if (!isAnyoneCanPay) { + let bufferWriter = BufferWriter.withCapacity(36 * this.ins.length); + this.ins.forEach(txIn => { + bufferWriter.writeSlice(txIn.hash); + bufferWriter.writeUInt32(txIn.index); + }); + hashPrevouts = bcrypto.sha256(bufferWriter.end()); + + bufferWriter = BufferWriter.withCapacity(8 * this.ins.length); + values.forEach(value => bufferWriter.writeUInt64(value)); + hashAmounts = bcrypto.sha256(bufferWriter.end()); + + bufferWriter = BufferWriter.withCapacity( + prevOutScripts.map(varSliceSize).reduce((a, b) => a + b), + ); + prevOutScripts.forEach(prevOutScript => + bufferWriter.writeVarSlice(prevOutScript), + ); + hashScriptPubKeys = bcrypto.sha256(bufferWriter.end()); + + bufferWriter = BufferWriter.withCapacity(4 * this.ins.length); + this.ins.forEach(txIn => bufferWriter.writeUInt32(txIn.sequence)); + hashSequences = bcrypto.sha256(bufferWriter.end()); + } + + if (!(isNone || isSingle)) { + const txOutsSize = this.outs + .map(output => 8 + varSliceSize(output.script)) + .reduce((a, b) => a + b); + const bufferWriter = BufferWriter.withCapacity(txOutsSize); + + this.outs.forEach(out => { + bufferWriter.writeUInt64(out.value); + bufferWriter.writeVarSlice(out.script); + }); + + hashOutputs = bcrypto.sha256(bufferWriter.end()); + } else if (isSingle && inIndex < this.outs.length) { + const output = this.outs[inIndex]; + + const bufferWriter = BufferWriter.withCapacity( + 8 + varSliceSize(output.script), + ); + bufferWriter.writeUInt64(output.value); + bufferWriter.writeVarSlice(output.script); + hashOutputs = bcrypto.sha256(bufferWriter.end()); + } + + const spendType = (leafHash ? 2 : 0) + (annex ? 1 : 0); + + // Length calculation from: + // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-14 + // With extension from: + // https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#signature-validation + const sigMsgSize = + 174 - + (isAnyoneCanPay ? 49 : 0) - + (isNone ? 32 : 0) + + (annex ? 32 : 0) + + (leafHash ? 37 : 0); + const sigMsgWriter = BufferWriter.withCapacity(sigMsgSize); + + sigMsgWriter.writeUInt8(hashType); + // Transaction + sigMsgWriter.writeInt32(this.version); + sigMsgWriter.writeUInt32(this.locktime); + sigMsgWriter.writeSlice(hashPrevouts); + sigMsgWriter.writeSlice(hashAmounts); + sigMsgWriter.writeSlice(hashScriptPubKeys); + sigMsgWriter.writeSlice(hashSequences); + if (!(isNone || isSingle)) { + sigMsgWriter.writeSlice(hashOutputs); + } + // Input + sigMsgWriter.writeUInt8(spendType); + if (isAnyoneCanPay) { + const input = this.ins[inIndex]; + sigMsgWriter.writeSlice(input.hash); + sigMsgWriter.writeUInt32(input.index); + sigMsgWriter.writeUInt64(values[inIndex]); + sigMsgWriter.writeVarSlice(prevOutScripts[inIndex]); + sigMsgWriter.writeUInt32(input.sequence); + } else { + sigMsgWriter.writeUInt32(inIndex); + } + if (annex) { + const bufferWriter = BufferWriter.withCapacity(varSliceSize(annex)); + bufferWriter.writeVarSlice(annex); + sigMsgWriter.writeSlice(bcrypto.sha256(bufferWriter.end())); + } + // Output + if (isSingle) { + sigMsgWriter.writeSlice(hashOutputs); + } + // BIP342 extension + if (leafHash) { + sigMsgWriter.writeSlice(leafHash); + sigMsgWriter.writeUInt8(0); + sigMsgWriter.writeUInt32(0xffffffff); + } + + // Extra zero byte because: + // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-19 + return bcrypto.taggedHash( + 'TapSighash', + Buffer.concat([Buffer.of(0x00), sigMsgWriter.end()]), + ); + } + hashForWitnessV0( inIndex: number, prevOutScript: Buffer, From 24e5cc061699cf66e63df7b371d4d1171646d529 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 12 Nov 2021 12:39:56 +0900 Subject: [PATCH 561/568] Add Taproot example --- CHANGELOG.md | 13 +++++ README.md | 7 +-- test/integration/taproot.md | 111 ++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 test/integration/taproot.md diff --git a/CHANGELOG.md b/CHANGELOG.md index a4e1eb7..f7d8f91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 6.0.0 +__removed__ +- bip32: Removed the re-export. Please add as dependency to your app instead. +- ECPair: Please use bip32 moving forward. ecpair package was created for those who need it. +- TransactionBuilder: Any internal files used only in TB (classify, templates, etc.) were also removed. + +__added__ +- taproot segwit v1 address support (bech32m) via address module (#1676) +- hashForWitnessV1 method on Transaction class (#1745) + +__fixed__ +- Transaction version read/write differed. (#1717) + # 5.2.0 __changed__ - Updated PSBT to allow for witnessUtxo and nonWitnessUtxo simultaneously (Re: segwit psbt bug) (#1563) diff --git a/README.md b/README.md index d974624..9389a73 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ # BitcoinJS (bitcoinjs-lib) -[](https://travis-ci.org/bitcoinjs/bitcoinjs-lib) -[](https://www.npmjs.org/package/bitcoinjs-lib) - -[](https://github.com/prettier/prettier) +[](https://github.com/bitcoinjs/bitcoinjs-lib/actions/workflows/main_ci.yml) [](https://www.npmjs.org/package/bitcoinjs-lib) [](https://github.com/prettier/prettier) A javascript Bitcoin library for node.js and browsers. Written in TypeScript, but committing the JS files to verify. @@ -94,6 +91,8 @@ The below examples are implemented as integration tests, they should be very eas Otherwise, pull requests are appreciated. Some examples interact (via HTTPS) with a 3rd Party Blockchain Provider (3PBP). +- [Taproot Key Spend](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/taproot.md) + - [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) - [Generate a 2-of-3 P2SH multisig address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/addresses.spec.ts) diff --git a/test/integration/taproot.md b/test/integration/taproot.md new file mode 100644 index 0000000..2db6eef --- /dev/null +++ b/test/integration/taproot.md @@ -0,0 +1,111 @@ +# Taproot + +A simple keyspend example that is possible with the current API is below. + +## Current state of taproot support + +- [x] segwit v1 address support via bech32m +- [x] segwit v1 sighash calculation on Transaction class + +## TODO + +- [ ] p2tr payment API to make script spends easier +- [ ] Support within the Psbt class + +## Example + +### Requirements +- npm dependencies + - bitcoinjs-lib v6.x.x + - bip32 v3.x.x + - tiny-secp256k1 v2.x.x + - regtest-client vx.x.x +- local regtest-server docker container running + - `docker run -d -p 8080:8080 junderw/bitcoinjs-regtest-server` +- node >= v14 + +```js +const crypto = require('crypto'); + +// bitcoinjs-lib v6 +const bitcoin = require('bitcoinjs-lib'); +// bip32 v3 wraps tiny-secp256k1 +const BIP32Wrapper = require('bip32').default; +const RegtestUtils = require('regtest-client').RegtestUtils; +// tiny-secp256k1 v2 is an ESM module, so we can't "require", and must import async +import('tiny-secp256k1') + .then(async (ecc) => { + // End imports + + // set up dependencies + const APIPASS = process.env.APIPASS || 'satoshi'; + // docker run -d -p 8080:8080 junderw/bitcoinjs-regtest-server + const APIURL = process.env.APIURL || 'http://127.0.0.1:8080/1'; + const regtestUtils = new RegtestUtils({ APIPASS, APIURL }); + + const bip32 = BIP32Wrapper(ecc); + + const myKey = bip32.fromSeed(crypto.randomBytes(64), regtestUtils.network); + // scriptPubkey + const output = Buffer.concat([ + // witness v1, PUSH_DATA 32 bytes + Buffer.from([0x51, 0x20]), + // x-only pubkey (remove 1 byte y parity) + myKey.publicKey.slice(1, 33), + ]); + const address = bitcoin.address.fromOutputScript( + output, + regtestUtils.network + ); + // amount from faucet + const amount = 42e4; + // amount to send + const sendAmount = amount - 1e4; + // get faucet + const unspent = await regtestUtils.faucetComplex(output, amount); + + const tx = createSigned( + myKey, + unspent.txId, + unspent.vout, + sendAmount, + [output], + [amount] + ); + + const hex = tx.toHex(); + console.log('Valid tx sent from:'); + console.log(address); + console.log('tx hex:'); + console.log(hex); + await regtestUtils.broadcast(hex); + await regtestUtils.verify({ + txId: tx.getId(), + address, + vout: 0, + value: sendAmount, + }); + }) + .catch(console.error); + +// Function for creating signed tx +function createSigned(key, txid, vout, amountToSend, scriptPubkeys, values) { + const tx = new bitcoin.Transaction(); + tx.version = 2; + // Add input + tx.addInput(Buffer.from(txid, 'hex').reverse(), vout); + // Add output + tx.addOutput(scriptPubkeys[0], amountToSend); + const sighash = tx.hashForWitnessV1( + 0, // which input + scriptPubkeys, // All previous outputs of all inputs + values, // All previous values of all inputs + bitcoin.Transaction.SIGHASH_DEFAULT // sighash flag, DEFAULT is schnorr-only (DEFAULT == ALL) + ); + const signature = Buffer.from(key.signSchnorr(sighash)); + // witness stack for keypath spend is just the signature. + // If sighash is not SIGHASH_DEFAULT (ALL) then you must add 1 byte with sighash value + tx.ins[0].witness = [signature]; + return tx; +} +``` \ No newline at end of file From 31e512e63f0dac3af4f9cfe72e886baa1b768467 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Fri, 12 Nov 2021 12:40:09 +0900 Subject: [PATCH 562/568] 6.0.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f07cf3c..c66d2ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.2.0", + "version": "6.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0eb793e..e420511 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "5.2.0", + "version": "6.0.0", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./src/index.d.ts", From 424abf2376772bb57b7668bc35b29ed18879fa0a Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Mon, 15 Nov 2021 08:25:22 +0900 Subject: [PATCH 563/568] Fix taproot example to follow the suggestion in BIP341 --- test/integration/taproot.md | 147 +++++++++++++++++++++++------------- 1 file changed, 96 insertions(+), 51 deletions(-) diff --git a/test/integration/taproot.md b/test/integration/taproot.md index 2db6eef..4010340 100644 --- a/test/integration/taproot.md +++ b/test/integration/taproot.md @@ -25,68 +25,108 @@ A simple keyspend example that is possible with the current API is below. - node >= v14 ```js -const crypto = require('crypto'); +// Run this whole file as async +// Catch any errors at the bottom of the file +// and exit the process with 1 error code +(async () => { +// Order of the curve (N) - 1 +const N_LESS_1 = Buffer.from( + 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', + 'hex' +); +// 1 represented as 32 bytes BE +const ONE = Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000001', + 'hex' +); + +const crypto = require('crypto'); // bitcoinjs-lib v6 const bitcoin = require('bitcoinjs-lib'); // bip32 v3 wraps tiny-secp256k1 const BIP32Wrapper = require('bip32').default; const RegtestUtils = require('regtest-client').RegtestUtils; // tiny-secp256k1 v2 is an ESM module, so we can't "require", and must import async -import('tiny-secp256k1') - .then(async (ecc) => { - // End imports +const ecc = await import('tiny-secp256k1'); +// wrap the bip32 library +const bip32 = BIP32Wrapper(ecc); +// set up dependencies +const APIPASS = process.env.APIPASS || 'satoshi'; +// docker run -d -p 8080:8080 junderw/bitcoinjs-regtest-server +const APIURL = process.env.APIURL || 'http://127.0.0.1:8080/1'; +const regtestUtils = new RegtestUtils({ APIPASS, APIURL }); +// End imports - // set up dependencies - const APIPASS = process.env.APIPASS || 'satoshi'; - // docker run -d -p 8080:8080 junderw/bitcoinjs-regtest-server - const APIURL = process.env.APIURL || 'http://127.0.0.1:8080/1'; - const regtestUtils = new RegtestUtils({ APIPASS, APIURL }); +const myKey = bip32.fromSeed(crypto.randomBytes(64), regtestUtils.network); - const bip32 = BIP32Wrapper(ecc); +const output = createKeySpendOutput(myKey.publicKey); +const address = bitcoin.address.fromOutputScript( + output, + regtestUtils.network +); +// amount from faucet +const amount = 42e4; +// amount to send +const sendAmount = amount - 1e4; +// get faucet +const unspent = await regtestUtils.faucetComplex(output, amount); - const myKey = bip32.fromSeed(crypto.randomBytes(64), regtestUtils.network); - // scriptPubkey - const output = Buffer.concat([ - // witness v1, PUSH_DATA 32 bytes - Buffer.from([0x51, 0x20]), - // x-only pubkey (remove 1 byte y parity) - myKey.publicKey.slice(1, 33), - ]); - const address = bitcoin.address.fromOutputScript( - output, - regtestUtils.network - ); - // amount from faucet - const amount = 42e4; - // amount to send - const sendAmount = amount - 1e4; - // get faucet - const unspent = await regtestUtils.faucetComplex(output, amount); +const tx = createSigned( + myKey, + unspent.txId, + unspent.vout, + sendAmount, + [output], + [amount] +); - const tx = createSigned( - myKey, - unspent.txId, - unspent.vout, - sendAmount, - [output], - [amount] - ); +const hex = tx.toHex(); +console.log('Valid tx sent from:'); +console.log(address); +console.log('tx hex:'); +console.log(hex); +await regtestUtils.broadcast(hex); +await regtestUtils.verify({ + txId: tx.getId(), + address, + vout: 0, + value: sendAmount, +}); - const hex = tx.toHex(); - console.log('Valid tx sent from:'); - console.log(address); - console.log('tx hex:'); - console.log(hex); - await regtestUtils.broadcast(hex); - await regtestUtils.verify({ - txId: tx.getId(), - address, - vout: 0, - value: sendAmount, - }); - }) - .catch(console.error); +// Function for creating a tweaked p2tr key-spend only address +// (This is recommended by BIP341) +function createKeySpendOutput(publicKey) { + // x-only pubkey (remove 1 byte y parity) + const myXOnlyPubkey = publicKey.slice(1, 33); + const commitHash = bitcoin.crypto.taggedHash('TapTweak', myXOnlyPubkey); + const tweakResult = ecc.xOnlyPointAddTweak(myXOnlyPubkey, commitHash); + if (tweakResult === null) throw new Error('Invalid Tweak'); + const { xOnlyPubkey: tweaked } = tweakResult; + // scriptPubkey + return Buffer.concat([ + // witness v1, PUSH_DATA 32 bytes + Buffer.from([0x51, 0x20]), + // x-only tweaked pubkey + tweaked, + ]); +} + +// Function for signing for a tweaked p2tr key-spend only address +// (Required for the above address) +function signTweaked(messageHash, key) { + const privateKey = + key.publicKey[0] === 2 + ? key.privateKey + : ecc.privateAdd(ecc.privateSub(N_LESS_1, key.privateKey), ONE); + const tweakHash = bitcoin.crypto.taggedHash( + 'TapTweak', + key.publicKey.slice(1, 33) + ); + const newPrivateKey = ecc.privateAdd(privateKey, tweakHash); + if (newPrivateKey === null) throw new Error('Invalid Tweak'); + return ecc.signSchnorr(messageHash, newPrivateKey, Buffer.alloc(32)); +} // Function for creating signed tx function createSigned(key, txid, vout, amountToSend, scriptPubkeys, values) { @@ -102,10 +142,15 @@ function createSigned(key, txid, vout, amountToSend, scriptPubkeys, values) { values, // All previous values of all inputs bitcoin.Transaction.SIGHASH_DEFAULT // sighash flag, DEFAULT is schnorr-only (DEFAULT == ALL) ); - const signature = Buffer.from(key.signSchnorr(sighash)); + const signature = Buffer.from(signTweaked(sighash, key)); // witness stack for keypath spend is just the signature. // If sighash is not SIGHASH_DEFAULT (ALL) then you must add 1 byte with sighash value tx.ins[0].witness = [signature]; return tx; } + +})().catch((err) => { + console.error(err); + process.exit(1); +}); ``` \ No newline at end of file From 191b9e857341521507b35e21f748bf148ebb5301 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 17 Nov 2021 16:01:08 +0900 Subject: [PATCH 564/568] Add taproot test with new CJS compatible tiny-secp256k1 --- package-lock.json | 46 ++++++---- package.json | 3 +- test/integration/bip32.spec.ts | 5 +- test/integration/taproot.spec.ts | 122 ++++++++++++++++++++++++++ test/integration/transactions.spec.ts | 4 +- test/psbt.spec.ts | 5 +- 6 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 test/integration/taproot.spec.ts diff --git a/package-lock.json b/package-lock.json index c66d2ad..d6b9ce8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -556,16 +556,15 @@ "integrity": "sha512-i3X26uKJOkDTAalYAp0Er+qGMDhrbbh2o93/xiPyAN2s25KrClSpe3VXo/7mNJoqA5qfko8rLS2l3RWZgYmjKQ==" }, "bip32": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz", - "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bip32/-/bip32-3.0.1.tgz", + "integrity": "sha512-Uhpp9aEx3iyiO7CpbNGFxv9WcMIVdGoHG04doQ5Ln0u60uwDah7jUSc3QMV/fSZGm/Oo01/OeAmYevXV+Gz5jQ==", "dev": true, "requires": { "@types/node": "10.12.18", "bs58check": "^2.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "tiny-secp256k1": "^1.1.3", "typeforce": "^1.11.5", "wif": "^2.0.6" }, @@ -941,6 +940,21 @@ "tiny-secp256k1": "^1.1.6", "typeforce": "^1.11.3", "wif": "^2.0.1" + }, + "dependencies": { + "tiny-secp256k1": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", + "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "dev": true, + "requires": { + "bindings": "^1.3.0", + "bn.js": "^4.11.8", + "create-hmac": "^1.1.7", + "elliptic": "^6.4.0", + "nan": "^2.13.2" + } + } } }, "elliptic": { @@ -1707,9 +1721,9 @@ "dev": true }, "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", "dev": true }, "node-environment-flags": { @@ -2465,16 +2479,12 @@ } }, "tiny-secp256k1": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", - "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.1.1.tgz", + "integrity": "sha512-pdENPcbI4l3Br6sPVuC5RWONHojcPjBiXljIBvQ5UIN/MD6wPzmJ8mpDnkps3O7FFfT+fLqGXo2MdFdRQaPWUg==", "dev": true, "requires": { - "bindings": "^1.3.0", - "bn.js": "^4.11.8", - "create-hmac": "^1.1.7", - "elliptic": "^6.4.0", - "nan": "^2.13.2" + "uint8array-tools": "0.0.6" } }, "to-fast-properties": { @@ -2583,6 +2593,12 @@ "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true }, + "uint8array-tools": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.6.tgz", + "integrity": "sha512-aIvEHNRX1AlOYAr6qSUjQBn4mCduxx6MtC967aRDTb2UUBPj0Ix2ZFQDcmXUUO/UxRPHcw1f5a5nVbCSKDSOqA==", + "dev": true + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", diff --git a/package.json b/package.json index e420511..36d9003 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@types/proxyquire": "^1.3.28", "@types/randombytes": "^2.0.0", "@types/wif": "^2.0.2", - "bip32": "^2.0.6", + "bip32": "^3.0.1", "bip39": "^3.0.2", "bip65": "^1.0.1", "bip68": "^1.0.3", @@ -84,6 +84,7 @@ "randombytes": "^2.1.0", "regtest-client": "0.2.0", "rimraf": "^2.6.3", + "tiny-secp256k1": "^2.1.1", "ts-node": "^8.3.0", "tslint": "^6.1.3", "typescript": "^4.4.4" diff --git a/test/integration/bip32.spec.ts b/test/integration/bip32.spec.ts index 7cd9e2f..938c281 100644 --- a/test/integration/bip32.spec.ts +++ b/test/integration/bip32.spec.ts @@ -1,9 +1,12 @@ import * as assert from 'assert'; -import * as bip32 from 'bip32'; +import BIP32Factory from 'bip32'; +import * as ecc from 'tiny-secp256k1'; import * as bip39 from 'bip39'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; +const bip32 = BIP32Factory(ecc); + function getAddress(node: any, network?: any): string { return bitcoin.payments.p2pkh({ pubkey: node.publicKey, network }).address!; } diff --git a/test/integration/taproot.spec.ts b/test/integration/taproot.spec.ts new file mode 100644 index 0000000..f7b3733 --- /dev/null +++ b/test/integration/taproot.spec.ts @@ -0,0 +1,122 @@ +import BIP32Factory from 'bip32'; +import * as ecc from 'tiny-secp256k1'; +import { describe, it } from 'mocha'; +import * as bitcoin from '../..'; +import { regtestUtils } from './_regtest'; +const rng = require('randombytes'); +const regtest = regtestUtils.network; +const bip32 = BIP32Factory(ecc); + +describe('bitcoinjs-lib (transaction with taproot)', () => { + it('can create (and broadcast via 3PBP) a taproot keyspend Transaction', async () => { + const myKey = bip32.fromSeed(rng(64), regtest); + + const output = createKeySpendOutput(myKey.publicKey); + const address = bitcoin.address.fromOutputScript(output, regtest); + // amount from faucet + const amount = 42e4; + // amount to send + const sendAmount = amount - 1e4; + // get faucet + const unspent = await regtestUtils.faucetComplex(output, amount); + + const tx = createSigned( + myKey, + unspent.txId, + unspent.vout, + sendAmount, + [output], + [amount], + ); + + const hex = tx.toHex(); + // console.log('Valid tx sent from:'); + // console.log(address); + // console.log('tx hex:'); + // console.log(hex); + await regtestUtils.broadcast(hex); + await regtestUtils.verify({ + txId: tx.getId(), + address, + vout: 0, + value: sendAmount, + }); + }); +}); + +// Order of the curve (N) - 1 +const N_LESS_1 = Buffer.from( + 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', + 'hex', +); +// 1 represented as 32 bytes BE +const ONE = Buffer.from( + '0000000000000000000000000000000000000000000000000000000000000001', + 'hex', +); + +// Function for creating a tweaked p2tr key-spend only address +// (This is recommended by BIP341) +function createKeySpendOutput(publicKey: Buffer): Buffer { + // x-only pubkey (remove 1 byte y parity) + const myXOnlyPubkey = publicKey.slice(1, 33); + const commitHash = bitcoin.crypto.taggedHash('TapTweak', myXOnlyPubkey); + const tweakResult = ecc.xOnlyPointAddTweak(myXOnlyPubkey, commitHash); + if (tweakResult === null) throw new Error('Invalid Tweak'); + const { xOnlyPubkey: tweaked } = tweakResult; + // scriptPubkey + return Buffer.concat([ + // witness v1, PUSH_DATA 32 bytes + Buffer.from([0x51, 0x20]), + // x-only tweaked pubkey + tweaked, + ]); +} + +// Function for signing for a tweaked p2tr key-spend only address +// (Required for the above address) +interface KeyPair { + publicKey: Buffer; + privateKey?: Buffer; +} +function signTweaked(messageHash: Buffer, key: KeyPair): Uint8Array { + const privateKey = + key.publicKey[0] === 2 + ? key.privateKey + : ecc.privateAdd(ecc.privateSub(N_LESS_1, key.privateKey!)!, ONE)!; + const tweakHash = bitcoin.crypto.taggedHash( + 'TapTweak', + key.publicKey.slice(1, 33), + ); + const newPrivateKey = ecc.privateAdd(privateKey!, tweakHash); + if (newPrivateKey === null) throw new Error('Invalid Tweak'); + return ecc.signSchnorr(messageHash, newPrivateKey, Buffer.alloc(32)); +} + +// Function for creating signed tx +function createSigned( + key: KeyPair, + txid: string, + vout: number, + amountToSend: number, + scriptPubkeys: Buffer[], + values: number[], +): bitcoin.Transaction { + const tx = new bitcoin.Transaction(); + tx.version = 2; + // Add input + tx.addInput(Buffer.from(txid, 'hex').reverse(), vout); + // Add output + tx.addOutput(scriptPubkeys[0], amountToSend); + const sighash = tx.hashForWitnessV1( + 0, // which input + scriptPubkeys, // All previous outputs of all inputs + values, // All previous values of all inputs + bitcoin.Transaction.SIGHASH_DEFAULT, // sighash flag, DEFAULT is schnorr-only (DEFAULT == ALL) + ); + const signature = Buffer.from(signTweaked(sighash, key)); + // witness stack for keypath spend is just the signature. + // If sighash is not SIGHASH_DEFAULT (ALL) then you must add 1 byte with sighash value + tx.ins[0].witness = [signature]; + return tx; +} diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts index d4788dc..51e44a0 100644 --- a/test/integration/transactions.spec.ts +++ b/test/integration/transactions.spec.ts @@ -1,11 +1,13 @@ import * as assert from 'assert'; -import * as bip32 from 'bip32'; +import BIP32Factory from 'bip32'; +import * as ecc from 'tiny-secp256k1'; import { ECPair } from 'ecpair'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; const rng = require('randombytes'); const regtest = regtestUtils.network; +const bip32 = BIP32Factory(ecc); const validator = ( pubkey: Buffer, diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 05d4468..32d81ba 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,9 +1,12 @@ import * as assert from 'assert'; -import * as bip32 from 'bip32'; +import BIP32Factory from 'bip32'; +import * as ecc from 'tiny-secp256k1'; import * as crypto from 'crypto'; import { ECPair } from 'ecpair'; import { describe, it } from 'mocha'; +const bip32 = BIP32Factory(ecc); + import { networks as NETWORKS, payments, Psbt, Signer, SignerAsync } from '..'; import * as preFixtures from './fixtures/psbt.json'; From 4674433bb9333cc50858f44aa92b64fb643ed386 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Wed, 17 Nov 2021 16:06:45 +0900 Subject: [PATCH 565/568] Update container --- .github/workflows/main_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml index a9fdc58..7bc62cb 100644 --- a/.github/workflows/main_ci.yml +++ b/.github/workflows/main_ci.yml @@ -41,7 +41,7 @@ jobs: runs-on: ubuntu-latest services: regtest: - image: junderw/bitcoinjs-regtest-server@sha256:a46ec1a651ca5b1a5408f2b2526ea5f435421dd2bc2f28fae3bc33e1fd614552 + image: junderw/bitcoinjs-regtest-server@sha256:5b69cf95d9edf6d5b3a00504665d6b3c382a6aa3728fe8ce897974c519061463 ports: - 8080:8080 steps: From 93af5afe67dbc491e36bdfc8d48a00179093f7d1 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 27 Nov 2021 08:35:19 +0900 Subject: [PATCH 566/568] Add warning to future segwit version address generation/parsing --- src/address.js | 10 +++++++++- ts_src/address.ts | 12 +++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/address.js b/src/address.js index 12938fc..164bf7e 100644 --- a/src/address.js +++ b/src/address.js @@ -13,6 +13,11 @@ const FUTURE_SEGWIT_MIN_SIZE = 2; const FUTURE_SEGWIT_MAX_VERSION = 16; const FUTURE_SEGWIT_MIN_VERSION = 1; const FUTURE_SEGWIT_VERSION_DIFF = 0x50; +const FUTURE_SEGWIT_VERSION_WARNING = + 'WARNING: Sending to a future segwit version address can lead to loss of funds. ' + + 'End users MUST be warned carefully in the GUI and asked if they wish to proceed ' + + 'with caution. Wallets should verify the segwit version from the output of fromBech32, ' + + 'then decide when it is safe to use which version of segwit.'; function _toFutureSegwitAddress(output, network) { const data = output.slice(2); if ( @@ -28,6 +33,7 @@ function _toFutureSegwitAddress(output, network) { throw new TypeError('Invalid version for segwit address'); if (output[1] !== data.length) throw new TypeError('Invalid script for segwit address'); + console.warn(FUTURE_SEGWIT_VERSION_WARNING); return toBech32(data, version, network.bech32); } function fromBase58Check(address) { @@ -128,11 +134,13 @@ function toOutputScript(address, network) { decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION && decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE && decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE - ) + ) { + console.warn(FUTURE_SEGWIT_VERSION_WARNING); return bscript.compile([ decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF, decodeBech32.data, ]); + } } } throw new Error(address + ' has no matching Script'); diff --git a/ts_src/address.ts b/ts_src/address.ts index d8111a7..753589d 100644 --- a/ts_src/address.ts +++ b/ts_src/address.ts @@ -23,6 +23,11 @@ const FUTURE_SEGWIT_MIN_SIZE: number = 2; const FUTURE_SEGWIT_MAX_VERSION: number = 16; const FUTURE_SEGWIT_MIN_VERSION: number = 1; const FUTURE_SEGWIT_VERSION_DIFF: number = 0x50; +const FUTURE_SEGWIT_VERSION_WARNING: string = + 'WARNING: Sending to a future segwit version address can lead to loss of funds. ' + + 'End users MUST be warned carefully in the GUI and asked if they wish to proceed ' + + 'with caution. Wallets should verify the segwit version from the output of fromBech32, ' + + 'then decide when it is safe to use which version of segwit.'; function _toFutureSegwitAddress(output: Buffer, network: Network): string { const data = output.slice(2); @@ -44,6 +49,8 @@ function _toFutureSegwitAddress(output: Buffer, network: Network): string { if (output[1] !== data.length) throw new TypeError('Invalid script for segwit address'); + console.warn(FUTURE_SEGWIT_VERSION_WARNING); + return toBech32(data, version, network.bech32); } @@ -163,11 +170,14 @@ export function toOutputScript(address: string, network?: Network): Buffer { decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION && decodeBech32.data.length >= FUTURE_SEGWIT_MIN_SIZE && decodeBech32.data.length <= FUTURE_SEGWIT_MAX_SIZE - ) + ) { + console.warn(FUTURE_SEGWIT_VERSION_WARNING); + return bscript.compile([ decodeBech32.version + FUTURE_SEGWIT_VERSION_DIFF, decodeBech32.data, ]); + } } } From 11202eb74cc9d9a7338ef4aa04c78061f0544289 Mon Sep 17 00:00:00 2001 From: junderw <junderwood@bitcoinbank.co.jp> Date: Sat, 27 Nov 2021 08:42:12 +0900 Subject: [PATCH 567/568] 6.0.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d6b9ce8..2ea42ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "6.0.0", + "version": "6.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 36d9003..dc93c53 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "6.0.0", + "version": "6.0.1", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "types": "./src/index.d.ts", From 2edfb992fa09761e09490b5efa27ba13a77de374 Mon Sep 17 00:00:00 2001 From: Vlad Stan <stan.v.vlad@gmail.com> Date: Fri, 17 Dec 2021 13:27:35 +0200 Subject: [PATCH 568/568] test: upgrade ecpair lib to version 2.0.1 --- package-lock.json | 123 ++------------------------ package.json | 4 +- test/integration/addresses.spec.ts | 5 +- test/integration/cltv.spec.ts | 5 +- test/integration/csv.spec.ts | 5 +- test/integration/payments.spec.ts | 5 +- test/integration/transactions.spec.ts | 4 +- test/psbt.spec.ts | 3 +- 8 files changed, 32 insertions(+), 122 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ea42ae..3e5bd51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -541,15 +541,6 @@ "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", "dev": true }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, "bip174": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.0.1.tgz", @@ -640,12 +631,6 @@ "fill-range": "^7.0.1" } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", @@ -931,59 +916,14 @@ "dev": true }, "ecpair": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ecpair/-/ecpair-1.0.0.tgz", - "integrity": "sha512-1L+P/ivLC3eKHgqcX1M9tFYQWXDoqwJ3zQnN7zDaTtLpiCQKpFTaAZvnsPC5PkWB4q3EPFAHffCLvjfCqRjuwQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ecpair/-/ecpair-2.0.1.tgz", + "integrity": "sha512-iT3wztQMeE/nDTlfnAg8dAFUfBS7Tq2BXzq3ae6L+pWgFU0fQ3l0woTzdTBrJV3OxBjxbzjq8EQhAbEmJNWFSw==", "dev": true, "requires": { - "randombytes": "^2.0.1", - "tiny-secp256k1": "^1.1.6", - "typeforce": "^1.11.3", - "wif": "^2.0.1" - }, - "dependencies": { - "tiny-secp256k1": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", - "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", - "dev": true, - "requires": { - "bindings": "^1.3.0", - "bn.js": "^4.11.8", - "create-hmac": "^1.1.7", - "elliptic": "^6.4.0", - "nan": "^2.13.2" - } - } - } - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - } + "randombytes": "^2.1.0", + "typeforce": "^1.18.0", + "wif": "^2.0.6" } }, "emoji-regex": { @@ -1040,12 +980,6 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true - }, "fill-keys": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", @@ -1218,16 +1152,6 @@ "safe-buffer": "^5.0.1" } }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, "hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -1244,17 +1168,6 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, "hoodwink": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hoodwink/-/hoodwink-2.0.0.tgz", @@ -1640,18 +1553,6 @@ "pushdata-bitcoin": "^1.0.1" } }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1720,12 +1621,6 @@ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, - "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "dev": true - }, "node-environment-flags": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", @@ -2479,9 +2374,9 @@ } }, "tiny-secp256k1": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.1.1.tgz", - "integrity": "sha512-pdENPcbI4l3Br6sPVuC5RWONHojcPjBiXljIBvQ5UIN/MD6wPzmJ8mpDnkps3O7FFfT+fLqGXo2MdFdRQaPWUg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.1.2.tgz", + "integrity": "sha512-8qPw7zDK6Hco2tVGYGQeOmOPp/hZnREwy2iIkcq0ygAuqc9WHo29vKN94lNymh1QbB3nthtAMF6KTIrdbsIotA==", "dev": true, "requires": { "uint8array-tools": "0.0.6" diff --git a/package.json b/package.json index dc93c53..c5553d5 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "bn.js": "^4.11.8", "bs58": "^4.0.0", "dhttp": "^3.0.0", - "ecpair": "^1.0.0", + "ecpair": "^2.0.1", "hoodwink": "^2.0.0", "minimaldata": "^1.0.2", "mocha": "^7.1.1", @@ -84,7 +84,7 @@ "randombytes": "^2.1.0", "regtest-client": "0.2.0", "rimraf": "^2.6.3", - "tiny-secp256k1": "^2.1.1", + "tiny-secp256k1": "^2.1.2", "ts-node": "^8.3.0", "tslint": "^6.1.3", "typescript": "^4.4.4" diff --git a/test/integration/addresses.spec.ts b/test/integration/addresses.spec.ts index 2b24ef5..d6e758b 100644 --- a/test/integration/addresses.spec.ts +++ b/test/integration/addresses.spec.ts @@ -1,8 +1,11 @@ import * as assert from 'assert'; -import { ECPair } from 'ecpair'; +import ECPairFactory from 'ecpair'; +import * as ecc from 'tiny-secp256k1'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; + +const ECPair = ECPairFactory(ecc); const dhttp = regtestUtils.dhttp; const TESTNET = bitcoin.networks.testnet; diff --git a/test/integration/cltv.spec.ts b/test/integration/cltv.spec.ts index c1a52de..04944eb 100644 --- a/test/integration/cltv.spec.ts +++ b/test/integration/cltv.spec.ts @@ -1,8 +1,11 @@ import * as assert from 'assert'; -import { ECPair } from 'ecpair'; +import ECPairFactory from 'ecpair'; +import * as ecc from 'tiny-secp256k1'; import { before, describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; + +const ECPair = ECPairFactory(ecc); const regtest = regtestUtils.network; const bip65 = require('bip65'); diff --git a/test/integration/csv.spec.ts b/test/integration/csv.spec.ts index 9993d5c..742d68f 100644 --- a/test/integration/csv.spec.ts +++ b/test/integration/csv.spec.ts @@ -1,9 +1,12 @@ import * as assert from 'assert'; import { PsbtInput } from 'bip174/src/lib/interfaces'; -import { ECPair } from 'ecpair'; +import ECPairFactory from 'ecpair'; +import * as ecc from 'tiny-secp256k1'; import { before, describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; + +const ECPair = ECPairFactory(ecc); const regtest = regtestUtils.network; const bip68 = require('bip68'); const varuint = require('varuint-bitcoin'); diff --git a/test/integration/payments.spec.ts b/test/integration/payments.spec.ts index ea7294e..d9d7fde 100644 --- a/test/integration/payments.spec.ts +++ b/test/integration/payments.spec.ts @@ -1,7 +1,10 @@ -import { ECPair } from 'ecpair'; +import ECPairFactory from 'ecpair'; +import * as ecc from 'tiny-secp256k1'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; + +const ECPair = ECPairFactory(ecc); const NETWORK = regtestUtils.network; const keyPairs = [ ECPair.makeRandom({ network: NETWORK }), diff --git a/test/integration/transactions.spec.ts b/test/integration/transactions.spec.ts index 51e44a0..ba7f2fe 100644 --- a/test/integration/transactions.spec.ts +++ b/test/integration/transactions.spec.ts @@ -1,10 +1,12 @@ import * as assert from 'assert'; import BIP32Factory from 'bip32'; import * as ecc from 'tiny-secp256k1'; -import { ECPair } from 'ecpair'; +import ECPairFactory from 'ecpair'; import { describe, it } from 'mocha'; import * as bitcoin from '../..'; import { regtestUtils } from './_regtest'; + +const ECPair = ECPairFactory(ecc); const rng = require('randombytes'); const regtest = regtestUtils.network; const bip32 = BIP32Factory(ecc); diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index 32d81ba..f583e80 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -2,10 +2,11 @@ import * as assert from 'assert'; import BIP32Factory from 'bip32'; import * as ecc from 'tiny-secp256k1'; import * as crypto from 'crypto'; -import { ECPair } from 'ecpair'; +import ECPairFactory from 'ecpair'; import { describe, it } from 'mocha'; const bip32 = BIP32Factory(ecc); +const ECPair = ECPairFactory(ecc); import { networks as NETWORKS, payments, Psbt, Signer, SignerAsync } from '..';