diff --git a/.travis.yml b/.travis.yml index 26f05da87..7422a8bff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,7 @@ language: node_js before_install: - "npm install npm -g" node_js: - - "0.11" - - "0.10" + - "0.12" env: - TEST_SUITE=coveralls - TEST_SUITE=integration diff --git a/README.md b/README.md index 045332e77..40d68e090 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,9 @@ You will then be able to load `foobar.js` into your browser, with each of the de **NOTE**: See our package.json for the currently supported version of browserify used by this repository. +**NOTE**: When uglifying the javascript, you must exclude the following variable names from being mangled: `Array`, `BigInteger`, `Boolean`, `Buffer`, `ECPair`, `Function`, `Number`, `Point` and `Script`. +This is because of the function-name-duck-typing used in [typeforce](https://github.com/dcousens/typeforce). + ## Examples @@ -84,23 +87,25 @@ The below examples are implemented as integration tests, they should be very eas - [Generate a random address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L8) - [Generate a address from a SHA256 hash](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L20) -- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L29) -- [Create a Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L36) +- [Generate a address and WIF for Litecoin](https://github.com/bitcoin/bitcoinjs-lib/blob/master/test/integration/basic.js#L29) +- [Import an address via WIF](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L43) +- [Create a Transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/basic.js#L50) - [Sign a Bitcoin message](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L9) - [Verify a Bitcoin message](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L17) - [Create an OP RETURN transaction](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/advanced.js#L24) - [Create a 2-of-3 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/multisig.js#L8) -- [Spend from a 2-of-2 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/multisig.js#L22) +- [Spend from a 2-of-4 multisig P2SH address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/multisig.js#L22) - [Generate a single-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L7) -- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L42) -- [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#L44) -- [Recover a Private key from duplicate R values in a signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L90) +- [Generate a dual-key stealth address](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L44) +- [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#L46) +- [Recover a Private key from duplicate R values in a signature](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/crypto.js#L92) ## Projects utilizing BitcoinJS - [BitAddress](https://www.bitaddress.org) - [Blockchain.info](https://blockchain.info/wallet) +- [Blocktrail](https://www.blocktrail.com/) - [Brainwallet](https://brainwallet.github.io) - [Coinkite](https://coinkite.com) - [Coinpunk](https://coinpunk.com) @@ -158,5 +163,5 @@ This library is free and open-source software released under the MIT license. ## Copyright -BitcoinJS (c) 2011-2014 Bitcoinjs-lib contributors +BitcoinJS (c) 2011-2015 Bitcoinjs-lib contributors Released under MIT license diff --git a/package.json b/package.json index fd4259be2..6afea75f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitcoinjs-lib", - "version": "1.5.0", + "version": "2.0.0-pre", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "keywords": [ @@ -46,20 +46,23 @@ }, "dependencies": { "bigi": "^1.4.0", - "bs58check": "^1.0.4", + "bs58check": "^1.0.5", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.3", "ecurve": "^1.0.0", - "typeforce": "^0.1.0" + "randombytes": "^2.0.1", + "typeforce": "^1.0.0" }, "devDependencies": { "async": "^0.9.0", "browserify": "^9.0.3", "bs58": "^2.0.1", - "cb-helloblock": "^0.4.10", + "cb-helloblock": "^0.4.13", "coveralls": "^2.11.2", "istanbul": "^0.3.5", - "mocha": "^2.1.0", - "mocha-lcov-reporter": "0.0.1", + "mocha": "^2.2.0", + "proxyquire": "^1.4.0", "sinon": "^1.12.2", - "standard": "^2.7.3" + "standard": "^2.11.0" } } diff --git a/src/address.js b/src/address.js index 4d8c67a26..e10c675a5 100644 --- a/src/address.js +++ b/src/address.js @@ -1,62 +1,67 @@ var assert = require('assert') -var base58check = require('bs58check') -var typeForce = require('typeforce') +var bs58check = require('bs58check') var networks = require('./networks') var scripts = require('./scripts') -function findScriptTypeByVersion (version) { - for (var networkName in networks) { - var network = networks[networkName] +function fromOutputScript(script, network) { + var payload = new Buffer(21) + var hash, version - if (version === network.pubKeyHash) return 'pubkeyhash' - if (version === network.scriptHash) return 'scripthash' - } -} + if (scripts.isPubKeyHashOutput(script)) { + version = network.pubKeyHash + hash = script.chunks[2] -function Address (hash, version) { - typeForce('Buffer', hash) + } else if (scripts.isScriptHashOutput(script)) { + version = network.scriptHash + hash = script.chunks[1] - assert.strictEqual(hash.length, 20, 'Invalid hash length') - assert.strictEqual(version & 0xff, version, 'Invalid version byte') + } else { + assert(false, script.toASM() + ' has no matching Address') + } + + payload.writeUInt8(version, 0) + hash.copy(payload, 1) - this.hash = hash - this.version = version + return bs58check.encode(payload) } -Address.fromBase58Check = function (string) { - var payload = base58check.decode(string) +// FIXME: remove network search +function toOutputScript(address) { + var payload = bs58check.decode(address) var version = payload.readUInt8(0) var hash = payload.slice(1) - return new Address(hash, version) -} + for (var networkStr in networks) { + var network = networks[networkStr] -Address.fromOutputScript = function (script, network) { - network = network || networks.bitcoin + if (version === network.pubKeyHash) { + return scripts.pubKeyHashOutput(hash) - if (scripts.isPubKeyHashOutput(script)) return new Address(script.chunks[2], network.pubKeyHash) - if (scripts.isScriptHashOutput(script)) return new Address(script.chunks[1], network.scriptHash) + } else if (version === network.scriptHash) { + return scripts.scriptHashOutput(hash) + } + } - assert(false, script.toASM() + ' has no matching Address') + assert(false, address + ' has no matching Script') } -Address.prototype.toBase58Check = function () { - var payload = new Buffer(21) - payload.writeUInt8(this.version, 0) - this.hash.copy(payload, 1) +function validate(address, network) { + if (typeof network === 'string') network = networks[network] - return base58check.encode(payload) -} + try { var payload = bs58check.decode(address) + assert.equal(payload.length, 21) -Address.prototype.toOutputScript = function () { - var scriptType = findScriptTypeByVersion(this.version) + var version = payload.readUInt8(0) - if (scriptType === 'pubkeyhash') return scripts.pubKeyHashOutput(this.hash) - if (scriptType === 'scripthash') return scripts.scriptHashOutput(this.hash) + assert(version === network.pubKeyHash || version === network.scriptHash) - assert(false, this.toString() + ' has no matching Script') + } catch (e) { + throw new Error(address + ' is not a valid ' + network + ' address') + } } -Address.prototype.toString = Address.prototype.toBase58Check - -module.exports = Address +module.exports = { + fromOutputScript: fromOutputScript, + toOutputScript: toOutputScript, + validate: validate +} diff --git a/src/bufferutils.js b/src/bufferutils.js index afe03e622..9f1c85edc 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -27,16 +27,19 @@ function readPushDataInt (buffer, offset) { // 8 bit } else if (opcode === opcodes.OP_PUSHDATA1) { + if (offset + 2 > buffer.length) return null number = buffer.readUInt8(offset + 1) size = 2 // 16 bit } else if (opcode === opcodes.OP_PUSHDATA2) { + if (offset + 3 > buffer.length) return null number = buffer.readUInt16LE(offset + 1) size = 3 // 32 bit } else { + if (offset + 5 > buffer.length) return null assert.equal(opcode, opcodes.OP_PUSHDATA4, 'Unexpected opcode') number = buffer.readUInt32LE(offset + 1) @@ -165,16 +168,6 @@ function varIntBuffer (i) { return buffer } -function equal (a, b) { - if (a.length !== b.length) return false - - for (var i = 0; i < a.length; ++i) { - if (a[i] !== b[i]) return false - } - - return true -} - function reverse (buffer) { var buffer2 = new Buffer(buffer) Array.prototype.reverse.call(buffer2) @@ -182,7 +175,6 @@ function reverse (buffer) { } module.exports = { - equal: equal, pushDataSize: pushDataSize, readPushDataInt: readPushDataInt, readUInt64LE: readUInt64LE, diff --git a/src/crypto.js b/src/crypto.js index dec8a79bf..3c1cb7386 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -1,4 +1,4 @@ -var crypto = require('crypto') +var createHash = require('create-hash') function hash160 (buffer) { return ripemd160(sha256(buffer)) @@ -9,15 +9,15 @@ function hash256 (buffer) { } function ripemd160 (buffer) { - return crypto.createHash('rmd160').update(buffer).digest() + return createHash('rmd160').update(buffer).digest() } function sha1 (buffer) { - return crypto.createHash('sha1').update(buffer).digest() + return createHash('sha1').update(buffer).digest() } function sha256 (buffer) { - return crypto.createHash('sha256').update(buffer).digest() + return createHash('sha256').update(buffer).digest() } module.exports = { diff --git a/src/ecdsa.js b/src/ecdsa.js index 0455d198b..c88add3f5 100644 --- a/src/ecdsa.js +++ b/src/ecdsa.js @@ -1,5 +1,5 @@ var assert = require('assert') -var crypto = require('crypto') +var createHmac = require('create-hmac') var typeForce = require('typeforce') var BigInteger = require('bigi') @@ -29,7 +29,7 @@ function deterministicGenerateK (curve, hash, d, checkSig) { k.fill(0) // Step D - k = crypto.createHmac('sha256', k) + k = createHmac('sha256', k) .update(v) .update(ZERO) .update(x) @@ -37,10 +37,10 @@ function deterministicGenerateK (curve, hash, d, checkSig) { .digest() // Step E - v = crypto.createHmac('sha256', k).update(v).digest() + v = createHmac('sha256', k).update(v).digest() // Step F - k = crypto.createHmac('sha256', k) + k = createHmac('sha256', k) .update(v) .update(ONE) .update(x) @@ -48,26 +48,26 @@ function deterministicGenerateK (curve, hash, d, checkSig) { .digest() // Step G - v = crypto.createHmac('sha256', k).update(v).digest() + v = createHmac('sha256', k).update(v).digest() // Step H1/H2a, ignored as tlen === qlen (256 bit) // Step H2b - v = crypto.createHmac('sha256', k).update(v).digest() + 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(curve.n) >= 0) || !checkSig(T)) { - k = crypto.createHmac('sha256', k) + k = createHmac('sha256', k) .update(v) .update(ZERO) .digest() - v = crypto.createHmac('sha256', k).update(v).digest() + v = createHmac('sha256', k).update(v).digest() // Step H1/H2a, again, ignored as tlen === qlen (256 bit) // Step H2b again - v = crypto.createHmac('sha256', k).update(v).digest() + v = createHmac('sha256', k).update(v).digest() T = BigInteger.fromBuffer(v) } diff --git a/src/eckey.js b/src/eckey.js deleted file mode 100644 index af7fda20b..000000000 --- a/src/eckey.js +++ /dev/null @@ -1,84 +0,0 @@ -var assert = require('assert') -var base58check = require('bs58check') -var crypto = require('crypto') -var ecdsa = require('./ecdsa') -var typeForce = require('typeforce') -var networks = require('./networks') - -var BigInteger = require('bigi') -var ECPubKey = require('./ecpubkey') - -var ecurve = require('ecurve') -var secp256k1 = ecurve.getCurveByName('secp256k1') - -function ECKey (d, compressed) { - assert(d.signum() > 0, 'Private key must be greater than 0') - assert(d.compareTo(ECKey.curve.n) < 0, 'Private key must be less than the curve order') - - var Q = ECKey.curve.G.multiply(d) - - this.d = d - this.pub = new ECPubKey(Q, compressed) -} - -// Constants -ECKey.curve = secp256k1 - -// Static constructors -ECKey.fromWIF = function (string) { - var payload = base58check.decode(string) - var compressed = false - - // Ignore the version byte - payload = payload.slice(1) - - if (payload.length === 33) { - assert.strictEqual(payload[32], 0x01, 'Invalid compression flag') - - // Truncate the compression flag - payload = payload.slice(0, -1) - compressed = true - } - - assert.equal(payload.length, 32, 'Invalid WIF payload length') - - var d = BigInteger.fromBuffer(payload) - return new ECKey(d, compressed) -} - -ECKey.makeRandom = function (compressed, rng) { - rng = rng || crypto.randomBytes - - var buffer = rng(32) - typeForce('Buffer', buffer) - assert.equal(buffer.length, 32, 'Expected 256-bit Buffer from RNG') - - var d = BigInteger.fromBuffer(buffer) - d = d.mod(ECKey.curve.n) - - return new ECKey(d, compressed) -} - -// Export functions -ECKey.prototype.toWIF = function (network) { - network = network || networks.bitcoin - - var bufferLen = this.pub.compressed ? 34 : 33 - var buffer = new Buffer(bufferLen) - - buffer.writeUInt8(network.wif, 0) - this.d.toBuffer(32).copy(buffer, 1) - - if (this.pub.compressed) { - buffer.writeUInt8(0x01, 33) - } - - return base58check.encode(buffer) -} - -// Operations -ECKey.prototype.sign = function (hash) { - return ecdsa.sign(ECKey.curve, hash, this.d) -} - -module.exports = ECKey diff --git a/src/ecpair.js b/src/ecpair.js new file mode 100644 index 000000000..990c2dd6a --- /dev/null +++ b/src/ecpair.js @@ -0,0 +1,151 @@ +var assert = require('assert') +var base58check = require('bs58check') +var bcrypto = require('./crypto') +var ecdsa = require('./ecdsa') +var ecurve = require('ecurve') +var networks = require('./networks') +var randomBytes = require('randombytes') +var typeForce = require('typeforce') + +var Address = require('./address') +var BigInteger = require('bigi') + +function findNetworkByWIFVersion (version) { + for (var networkName in networks) { + var network = networks[networkName] + + if (network.wif === version) return network + } + + assert(false, 'Unknown network') +} + +function ECPair (d, Q, options) { + options = options || {} + + var compressed = options.compressed === undefined ? true : options.compressed + var network = options.network === undefined ? networks.bitcoin : options.network + + typeForce('Boolean', compressed) + assert('pubKeyHash' in network, 'Unknown pubKeyHash constants for network') + + if (d) { + assert(d.signum() > 0, 'Private key must be greater than 0') + assert(d.compareTo(ECPair.curve.n) < 0, 'Private key must be less than the curve order') + + assert(!Q, 'Unexpected publicKey parameter') + this.d = d + + // enforce Q is a public key if no private key given + } else { + typeForce('Point', Q) + this.__Q = Q + } + + this.compressed = compressed + this.network = network +} + +Object.defineProperty(ECPair.prototype, 'Q', { + get: function () { + if (!this.__Q && this.d) { + this.__Q = ECPair.curve.G.multiply(this.d) + } + + return this.__Q + } +}) + +// Public access to secp256k1 curve +ECPair.curve = ecurve.getCurveByName('secp256k1') + +ECPair.fromPublicKeyBuffer = function (buffer, network) { + var Q = ecurve.Point.decodeFrom(ECPair.curve, buffer) + + return new ECPair(null, Q, { + compressed: Q.compressed, + network: network + }) +} + +ECPair.fromWIF = function (string) { + var payload = base58check.decode(string) + var version = payload.readUInt8(0) + var compressed + + if (payload.length === 34) { + assert.strictEqual(payload[33], 0x01, 'Invalid compression flag') + + // truncate the version/compression bytes + payload = payload.slice(1, -1) + compressed = true + + // no compression flag + } else { + assert.equal(payload.length, 33, 'Invalid WIF payload length') + + // Truncate the version byte + payload = payload.slice(1) + compressed = false + } + + var network = findNetworkByWIFVersion(version) + var d = BigInteger.fromBuffer(payload) + + return new ECPair(d, null, { + compressed: compressed, + network: network + }) +} + +ECPair.makeRandom = function (options) { + options = options || {} + + var rng = options.rng || randomBytes + var buffer = rng(32) + typeForce('Buffer', buffer) + assert.equal(buffer.length, 32, 'Expected 256-bit Buffer from RNG') + + var d = BigInteger.fromBuffer(buffer) + d = d.mod(ECPair.curve.n) + + return new ECPair(d, null, options) +} + +ECPair.prototype.toWIF = function () { + assert(this.d, 'Missing private key') + + var bufferLen = this.compressed ? 34 : 33 + var buffer = new Buffer(bufferLen) + + buffer.writeUInt8(this.network.wif, 0) + this.d.toBuffer(32).copy(buffer, 1) + + if (this.compressed) { + buffer.writeUInt8(0x01, 33) + } + + return base58check.encode(buffer) +} + +ECPair.prototype.getAddress = function () { + var pubKey = this.getPublicKeyBuffer() + + return new Address(bcrypto.hash160(pubKey), this.network.pubKeyHash) +} + +ECPair.prototype.getPublicKeyBuffer = function () { + return this.Q.getEncoded(this.compressed) +} + +ECPair.prototype.sign = function (hash) { + assert(this.d, 'Missing private key') + + return ecdsa.sign(ECPair.curve, hash, this.d) +} + +ECPair.prototype.verify = function (hash, signature) { + return ecdsa.verify(ECPair.curve, hash, signature, this.Q) +} + +module.exports = ECPair diff --git a/src/ecpubkey.js b/src/ecpubkey.js deleted file mode 100644 index ecc37bc51..000000000 --- a/src/ecpubkey.js +++ /dev/null @@ -1,56 +0,0 @@ -var crypto = require('./crypto') -var ecdsa = require('./ecdsa') -var typeForce = require('typeforce') -var networks = require('./networks') - -var Address = require('./address') - -var ecurve = require('ecurve') -var secp256k1 = ecurve.getCurveByName('secp256k1') - -function ECPubKey (Q, compressed) { - if (compressed === undefined) { - compressed = true - } - - typeForce('Point', Q) - typeForce('Boolean', compressed) - - this.compressed = compressed - this.Q = Q -} - -// Constants -ECPubKey.curve = secp256k1 - -// Static constructors -ECPubKey.fromBuffer = function (buffer) { - var Q = ecurve.Point.decodeFrom(ECPubKey.curve, buffer) - return new ECPubKey(Q, Q.compressed) -} - -ECPubKey.fromHex = function (hex) { - return ECPubKey.fromBuffer(new Buffer(hex, 'hex')) -} - -// Operations -ECPubKey.prototype.getAddress = function (network) { - network = network || networks.bitcoin - - return new Address(crypto.hash160(this.toBuffer()), network.pubKeyHash) -} - -ECPubKey.prototype.verify = function (hash, signature) { - return ecdsa.verify(ECPubKey.curve, hash, signature, this.Q) -} - -// Export functions -ECPubKey.prototype.toBuffer = function () { - return this.Q.getEncoded(this.compressed) -} - -ECPubKey.prototype.toHex = function () { - return this.toBuffer().toString('hex') -} - -module.exports = ECPubKey diff --git a/src/hdnode.js b/src/hdnode.js index f0aa61399..6989b3eaa 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -1,13 +1,12 @@ var assert = require('assert') var base58check = require('bs58check') var bcrypto = require('./crypto') -var crypto = require('crypto') +var createHmac = require('create-hmac') var typeForce = require('typeforce') var networks = require('./networks') var BigInteger = require('bigi') -var ECKey = require('./eckey') -var ECPubKey = require('./ecpubkey') +var ECPair = require('./ecpair') var ecurve = require('ecurve') var curve = ecurve.getCurveByName('secp256k1') @@ -24,32 +23,19 @@ function findBIP32NetworkByVersion (version) { assert(false, 'Could not find network for ' + version.toString(16)) } -function HDNode (K, chainCode, network) { - network = network || networks.bitcoin - +function HDNode (keyPair, chainCode) { + typeForce('ECPair', keyPair) typeForce('Buffer', chainCode) assert.equal(chainCode.length, 32, 'Expected chainCode length of 32, got ' + chainCode.length) - assert(network.bip32, 'Unknown BIP32 constants for network') + assert('bip32' in keyPair.network, 'Unknown BIP32 constants for network') + assert.equal(keyPair.compressed, true, 'BIP32 only allows compressed keyPairs') + this.keyPair = keyPair this.chainCode = chainCode this.depth = 0 this.index = 0 this.parentFingerprint = 0x00000000 - this.network = network - - if (K instanceof BigInteger) { - this.privKey = new ECKey(K, true) - this.pubKey = this.privKey.pub - } else if (K instanceof ECKey) { - assert(K.pub.compressed, 'ECKey must be compressed') - this.privKey = K - } else if (K instanceof ECPubKey) { - assert(K.compressed, 'ECPubKey must be compressed') - this.pubKey = K - } else { - this.pubKey = new ECPubKey(K, true) - } } HDNode.MASTER_SECRET = new Buffer('Bitcoin seed') @@ -62,15 +48,18 @@ HDNode.fromSeedBuffer = function (seed, network) { assert(seed.length >= 16, 'Seed should be at least 128 bits') assert(seed.length <= 64, 'Seed should be at most 512 bits') - var I = crypto.createHmac('sha512', HDNode.MASTER_SECRET).update(seed).digest() + 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 `new ECKey` in the HDNode constructor + // This is handled by the ECPair constructor var pIL = BigInteger.fromBuffer(IL) + var keyPair = new ECPair(pIL, null, { + network: network + }) - return new HDNode(pIL, IR, network) + return new HDNode(keyPair, IR) } HDNode.fromSeedHex = function (hex, network) { @@ -108,14 +97,17 @@ HDNode.fromBase58 = function (string, network) { // 32 bytes: the chain code var chainCode = buffer.slice(13, 45) - var data, hd + var data, keyPair // 33 bytes: private key data (0x00 + k) if (version === network.bip32.private) { assert.strictEqual(buffer.readUInt8(45), 0x00, 'Invalid private key') data = buffer.slice(46, 78) var d = BigInteger.fromBuffer(data) - hd = new HDNode(d, chainCode, network) + + keyPair = new ECPair(d, null, { + network: network + }) // 33 bytes: public key data (0x02 + X or 0x03 + X) } else { @@ -127,9 +119,12 @@ HDNode.fromBase58 = function (string, network) { // If not, the extended public key is invalid. curve.validate(Q) - hd = new HDNode(Q, chainCode, network) + keyPair = new ECPair(null, Q, { + network: network + }) } + var hd = new HDNode(keyPair, chainCode) hd.depth = depth hd.index = index hd.parentFingerprint = parentFingerprint @@ -138,7 +133,7 @@ HDNode.fromBase58 = function (string, network) { } HDNode.prototype.getIdentifier = function () { - return bcrypto.hash160(this.pubKey.toBuffer()) + return bcrypto.hash160(this.keyPair.getPublicKeyBuffer()) } HDNode.prototype.getFingerprint = function () { @@ -146,11 +141,15 @@ HDNode.prototype.getFingerprint = function () { } HDNode.prototype.getAddress = function () { - return this.pubKey.getAddress(this.network) + return this.keyPair.getAddress() } HDNode.prototype.neutered = function () { - var neutered = new HDNode(this.pubKey.Q, this.chainCode, this.network) + 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 @@ -162,7 +161,8 @@ HDNode.prototype.toBase58 = function (__isPrivate) { assert.strictEqual(__isPrivate, undefined, 'Unsupported argument in 2.0.0') // Version - var version = this.privKey ? this.network.bip32.private : this.network.bip32.public + var network = this.keyPair.network + var version = this.keyPair.d ? network.bip32.private : network.bip32.public var buffer = new Buffer(HDNode.LENGTH) // 4 bytes: version bytes @@ -182,16 +182,16 @@ HDNode.prototype.toBase58 = function (__isPrivate) { // 32 bytes: the chain code this.chainCode.copy(buffer, 13) - // 33 bytes: the private key, or - if (this.privKey) { + // 33 bytes: the public key or private key data + if (this.keyPair.d) { // 0x00 + k for private keys buffer.writeUInt8(0, 45) - this.privKey.d.toBuffer(32).copy(buffer, 46) + this.keyPair.d.toBuffer(32).copy(buffer, 46) // 33 bytes: the public key } else { // X9.62 encoding for public keys - this.pubKey.toBuffer().copy(buffer, 45) + this.keyPair.getPublicKeyBuffer().copy(buffer, 45) } return base58check.encode(buffer) @@ -207,11 +207,11 @@ HDNode.prototype.derive = function (index) { // Hardened child if (isHardened) { - assert(this.privKey, 'Could not derive hardened child key') + assert(this.keyPair.d, 'Could not derive hardened child key') // data = 0x00 || ser256(kpar) || ser32(index) data = Buffer.concat([ - this.privKey.d.toBuffer(33), + this.keyPair.d.toBuffer(33), indexBuffer ]) @@ -220,12 +220,12 @@ HDNode.prototype.derive = function (index) { // data = serP(point(kpar)) || ser32(index) // = serP(Kpar) || ser32(index) data = Buffer.concat([ - this.pubKey.toBuffer(), + this.keyPair.getPublicKeyBuffer(), indexBuffer ]) } - var I = crypto.createHmac('sha512', this.chainCode).update(data).digest() + var I = createHmac('sha512', this.chainCode).update(data).digest() var IL = I.slice(0, 32) var IR = I.slice(32) @@ -237,32 +237,37 @@ HDNode.prototype.derive = function (index) { } // Private parent key -> private child key - var hd - if (this.privKey) { + var derivedKeyPair + if (this.keyPair.d) { // ki = parse256(IL) + kpar (mod n) - var ki = pIL.add(this.privKey.d).mod(curve.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) } - hd = new HDNode(ki, IR, this.network) + 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.pubKey.Q) + 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) } - hd = new HDNode(Ki, IR, this.network) + 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) diff --git a/src/index.js b/src/index.js index 3b5317bce..2819bd299 100644 --- a/src/index.js +++ b/src/index.js @@ -4,8 +4,7 @@ module.exports = { bufferutils: require('./bufferutils'), crypto: require('./crypto'), ecdsa: require('./ecdsa'), - ECKey: require('./eckey'), - ECPubKey: require('./ecpubkey'), + ECPair: require('./ecpair'), ECSignature: require('./ecsignature'), message: require('./message'), opcodes: require('./opcodes'), diff --git a/src/message.js b/src/message.js index ecb1f1858..f3791feac 100644 --- a/src/message.js +++ b/src/message.js @@ -4,30 +4,30 @@ var ecdsa = require('./ecdsa') var networks = require('./networks') var BigInteger = require('bigi') -var ECPubKey = require('./ecpubkey') +var ECPair = require('./ecpair') var ECSignature = require('./ecsignature') var ecurve = require('ecurve') var ecparams = ecurve.getCurveByName('secp256k1') function magicHash (message, network) { - var magicPrefix = new Buffer(network.magicPrefix) + var messagePrefix = new Buffer(network.messagePrefix) var messageBuffer = new Buffer(message) var lengthBuffer = bufferutils.varIntBuffer(messageBuffer.length) - var buffer = Buffer.concat([magicPrefix, lengthBuffer, messageBuffer]) + var buffer = Buffer.concat([messagePrefix, lengthBuffer, messageBuffer]) return crypto.hash256(buffer) } -function sign (privKey, message, network) { +function sign (keyPair, message, network) { network = network || networks.bitcoin var hash = magicHash(message, network) - var signature = privKey.sign(hash) + var signature = keyPair.sign(hash) var e = BigInteger.fromBuffer(hash) - var i = ecdsa.calcPubKeyRecoveryParam(ecparams, e, signature, privKey.pub.Q) + var i = ecdsa.calcPubKeyRecoveryParam(ecparams, e, signature, keyPair.Q) - return signature.toCompact(i, privKey.pub.compressed) + return signature.toCompact(i, keyPair.compressed) } // TODO: network could be implied from address @@ -42,9 +42,12 @@ function verify (address, signature, message, network) { var parsed = ECSignature.parseCompact(signature) var e = BigInteger.fromBuffer(hash) var Q = ecdsa.recoverPubKey(ecparams, e, parsed.signature, parsed.i) + var keyPair = new ECPair(null, Q, { + compressed: parsed.compressed, + network: network + }) - var pubKey = new ECPubKey(Q, parsed.compressed) - return pubKey.getAddress(network).toString() === address.toString() + return keyPair.getAddress().toString() === address.toString() } module.exports = { diff --git a/src/networks.js b/src/networks.js index 7395aa4b3..d694bf767 100644 --- a/src/networks.js +++ b/src/networks.js @@ -1,9 +1,10 @@ // https://en.bitcoin.it/wiki/List_of_address_prefixes // Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731 -var networks = { +module.exports = { bitcoin: { - magicPrefix: '\x18Bitcoin Signed Message:\n', + magic: 0xd9b4bef9, + messagePrefix: '\x18Bitcoin Signed Message:\n', bip32: { public: 0x0488b21e, private: 0x0488ade4 @@ -11,12 +12,11 @@ var networks = { pubKeyHash: 0x00, scriptHash: 0x05, wif: 0x80, - dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162 - feePerKb: 10000, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/main.cpp#L53 - estimateFee: estimateFee('bitcoin') + dustThreshold: 546 // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162 }, testnet: { - magicPrefix: '\x18Bitcoin Signed Message:\n', + magic: 0xd9b4bef9, + messagePrefix: '\x18Bitcoin Signed Message:\n', bip32: { public: 0x043587cf, private: 0x04358394 @@ -24,12 +24,11 @@ var networks = { pubKeyHash: 0x6f, scriptHash: 0xc4, wif: 0xef, - dustThreshold: 546, - feePerKb: 10000, - estimateFee: estimateFee('testnet') + dustThreshold: 546 }, litecoin: { - magicPrefix: '\x19Litecoin Signed Message:\n', + magic: 0xd9b4bef9, + messagePrefix: '\x19Litecoin Signed Message:\n', bip32: { public: 0x019da462, private: 0x019d9cfe @@ -37,13 +36,10 @@ var networks = { pubKeyHash: 0x30, scriptHash: 0x05, wif: 0xb0, - dustThreshold: 0, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365 - dustSoftThreshold: 100000, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.h#L53 - feePerKb: 100000, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L56 - estimateFee: estimateFee('litecoin') + dustThreshold: 0 // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365 }, dogecoin: { - magicPrefix: '\x19Dogecoin Signed Message:\n', + messagePrefix: '\x19Dogecoin Signed Message:\n', bip32: { public: 0x02facafd, private: 0x02fac398 @@ -51,99 +47,6 @@ var networks = { pubKeyHash: 0x1e, scriptHash: 0x16, wif: 0x9e, - dustThreshold: 0, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160 - dustSoftThreshold: 100000000, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/main.h#L62 - feePerKb: 100000000, // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/main.cpp#L58 - estimateFee: estimateFee('dogecoin') - }, - viacoin: { - magicPrefix: '\x18Viacoin Signed Message:\n', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4 - }, - pubKeyHash: 0x47, - scriptHash: 0x21, - wif: 0xc7, - dustThreshold: 560, - dustSoftThreshold: 100000, - feePerKb: 100000, // - estimateFee: estimateFee('viacoin') - }, - viacointestnet: { - magicPrefix: '\x18Viacoin Signed Message:\n', - bip32: { - public: 0x043587cf, - private: 0x04358394 - }, - pubKeyHash: 0x7f, - scriptHash: 0xc4, - wif: 0xff, - dustThreshold: 560, - dustSoftThreshold: 100000, - feePerKb: 100000, - estimateFee: estimateFee('viacointestnet') - }, - gamerscoin: { - magicPrefix: '\x19Gamerscoin Signed Message:\n', - bip32: { - public: 0x019da462, - private: 0x019d9cfe - }, - pubKeyHash: 0x26, - scriptHash: 0x05, - wif: 0xA6, - dustThreshold: 0, // https://github.com/gamers-coin/gamers-coinv3/blob/master/src/main.cpp#L358-L363 - dustSoftThreshold: 100000, // https://github.com/gamers-coin/gamers-coinv3/blob/master/src/main.cpp#L51 - feePerKb: 100000, // https://github.com/gamers-coin/gamers-coinv3/blob/master/src/main.cpp#L54 - estimateFee: estimateFee('gamerscoin') - }, - jumbucks: { - magicPrefix: '\x19Jumbucks Signed Message:\n', - bip32: { - public: 0x037a689a, - private: 0x037a6460 - }, - pubKeyHash: 0x2b, - scriptHash: 0x05, - wif: 0xab, - dustThreshold: 0, - dustSoftThreshold: 10000, - feePerKb: 10000, - estimateFee: estimateFee('jumbucks') - }, - zetacoin: { - magicPrefix: '\x18Zetacoin Signed Message:\n', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4 - }, - pubKeyHash: 0x50, - scriptHash: 0x09, - wif: 0xe0, - dustThreshold: 546, // https://github.com/zetacoin/zetacoin/blob/master/src/core.h#L159 - feePerKb: 10000, // https://github.com/zetacoin/zetacoin/blob/master/src/main.cpp#L54 - estimateFee: estimateFee('zetacoin') - } -} - -function estimateFee (type) { - return function (tx) { - var network = networks[type] - var baseFee = network.feePerKb - var byteSize = tx.toBuffer().length - - var fee = baseFee * Math.ceil(byteSize / 1000) - if (network.dustSoftThreshold === undefined) return fee - - tx.outs.forEach(function (e) { - if (e.value < network.dustSoftThreshold) { - fee += baseFee - } - }) - - return fee + dustThreshold: 0 // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160 } } - -module.exports = networks diff --git a/src/script.js b/src/script.js index b35bfdf14..141fb1fc9 100644 --- a/src/script.js +++ b/src/script.js @@ -38,8 +38,14 @@ Script.fromBuffer = function (buffer) { // data chunk if ((opcode > opcodes.OP_0) && (opcode <= opcodes.OP_PUSHDATA4)) { var d = bufferutils.readPushDataInt(buffer, i) + + // did reading a pushDataInt fail? return non-chunked script + if (d === null) return new Script(buffer, []) i += d.size + // attempt to read too much data? + if (i + d.number > buffer.length) return new Script(buffer, []) + var data = buffer.slice(i, i + d.number) i += d.number diff --git a/src/scripts.js b/src/scripts.js index 387618429..fde409f45 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -73,15 +73,12 @@ function isScriptHashInput (script, allowIncomplete) { if (!Buffer.isBuffer(lastChunk)) return false var scriptSig = Script.fromChunks(script.chunks.slice(0, -1)) - var scriptPubKey + var redeemScript = Script.fromBuffer(lastChunk) - try { - scriptPubKey = Script.fromBuffer(lastChunk) - } catch (e) { - return false - } + // is redeemScript a valid script? + if (redeemScript.chunks.length === 0) return false - return classifyInput(scriptSig, allowIncomplete) === classifyOutput(scriptPubKey) + return classifyInput(scriptSig, allowIncomplete) === classifyOutput(redeemScript) } function isScriptHashOutput (script) { @@ -173,7 +170,7 @@ function classifyInput (script, allowIncomplete) { // {pubKey} OP_CHECKSIG function pubKeyOutput (pubKey) { return Script.fromChunks([ - pubKey.toBuffer(), + pubKey, ops.OP_CHECKSIG ]) } @@ -204,18 +201,14 @@ function scriptHashOutput (hash) { // m [pubKeys ...] n OP_CHECKMULTISIG function multisigOutput (m, pubKeys) { - typeForce(['ECPubKey'], pubKeys) + typeForce(['Buffer'], pubKeys) - assert(pubKeys.length >= m, 'Not enough pubKeys provided') - - var pubKeyBuffers = pubKeys.map(function (pubKey) { - return pubKey.toBuffer() - }) var n = pubKeys.length + assert(n >= m, 'Not enough pubKeys provided') return Script.fromChunks([].concat( (ops.OP_1 - 1) + m, - pubKeyBuffers, + pubKeys, (ops.OP_1 - 1) + n, ops.OP_CHECKMULTISIG )) @@ -231,8 +224,9 @@ function pubKeyInput (signature) { // {signature} {pubKey} function pubKeyHashInput (signature, pubKey) { typeForce('Buffer', signature) + typeForce('Buffer', pubKey) - return Script.fromChunks([signature, pubKey.toBuffer()]) + return Script.fromChunks([signature, pubKey]) } // {serialized scriptPubKey script} @@ -253,13 +247,8 @@ function multisigInput (signatures, scriptPubKey) { var m = mOp - (ops.OP_1 - 1) var n = nOp - (ops.OP_1 - 1) - var count = 0 - signatures.forEach(function (signature) { - count += (signature !== ops.OP_0) - }) - - assert(count >= m, 'Not enough signatures provided') - assert(count <= n, 'Too many signatures provided') + assert(signatures.length >= m, 'Not enough signatures provided') + assert(signatures.length <= n, 'Too many signatures provided') } return Script.fromChunks([].concat(ops.OP_0, signatures)) diff --git a/src/transaction.js b/src/transaction.js index fa1b18a7d..6e8fc6c7b 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -137,6 +137,22 @@ Transaction.prototype.addOutput = function (scriptPubKey, value) { }) - 1) } +Transaction.prototype.byteLength = function () { + function scriptSize (script) { + var length = script.buffer.length + + return bufferutils.varIntSize(length) + length + } + + return ( + 8 + + bufferutils.varIntSize(this.ins.length) + + bufferutils.varIntSize(this.outs.length) + + this.ins.reduce(function (sum, input) { return sum + 40 + scriptSize(input.script) }, 0) + + this.outs.reduce(function (sum, output) { return sum + 8 + scriptSize(output.script) }, 0) + ) +} + Transaction.prototype.clone = function () { var newTx = new Transaction() newTx.version = this.version @@ -215,19 +231,7 @@ Transaction.prototype.getId = function () { } Transaction.prototype.toBuffer = function () { - function scriptSize (script) { - var length = script.buffer.length - - return bufferutils.varIntSize(length) + length - } - - var buffer = new Buffer( - 8 + - bufferutils.varIntSize(this.ins.length) + - bufferutils.varIntSize(this.outs.length) + - this.ins.reduce(function (sum, input) { return sum + 40 + scriptSize(input.script) }, 0) + - this.outs.reduce(function (sum, output) { return sum + 8 + scriptSize(output.script) }, 0) - ) + var buffer = new Buffer(this.byteLength()) var offset = 0 function writeSlice (slice) { diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 924f177e5..cafcfc854 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -3,7 +3,7 @@ var ops = require('./opcodes') var scripts = require('./scripts') var Address = require('./address') -var ECPubKey = require('./ecpubkey') +var ECPair = require('./ecpair') var ECSignature = require('./ecsignature') var Script = require('./script') var Transaction = require('./transaction') @@ -33,9 +33,9 @@ function extractInput (txIn) { case 'pubkeyhash': { parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0]) hashType = parsed.hashType - pubKeys = [ECPubKey.fromBuffer(scriptSig.chunks[1])] + pubKeys = scriptSig.chunks.slice(1) signatures = [parsed.signature] - prevOutScript = pubKeys[0].getAddress().toOutputScript() + prevOutScript = ECPair.fromPublicKeyBuffer(pubKeys[0]).getAddress().toOutputScript() break } @@ -46,7 +46,7 @@ function extractInput (txIn) { signatures = [parsed.signature] if (redeemScript) { - pubKeys = [ECPubKey.fromBuffer(redeemScript.chunks[0])] + pubKeys = redeemScript.chunks.slice(0, 1) } break @@ -63,7 +63,7 @@ function extractInput (txIn) { }) if (redeemScript) { - pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer) + pubKeys = redeemScript.chunks.slice(1, -2) } break @@ -113,7 +113,7 @@ TransactionBuilder.fromTransaction = function (transaction) { assert(!Transaction.isCoinbaseHash(txIn.hash), 'coinbase inputs not supported') // Ignore empty scripts - if (txIn.script.buffer.length === 0) return + if (txIn.script.buffer.length === 0) return {} return extractInput(txIn) }) @@ -141,12 +141,12 @@ TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOu // if we can, extract pubKey information switch (prevOutType) { case 'multisig': { - input.pubKeys = prevOutScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer) + input.pubKeys = prevOutScript.chunks.slice(1, -2) break } case 'pubkey': { - input.pubKeys = prevOutScript.chunks.slice(0, 1).map(ECPubKey.fromBuffer) + input.pubKeys = prevOutScript.chunks.slice(0, 1) break } } @@ -238,14 +238,19 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) { case 'multisig': { // Array.prototype.map is sparse-compatible var msSignatures = input.signatures.map(function (signature) { - return signature.toScriptSignature(input.hashType) + return signature && signature.toScriptSignature(input.hashType) }) // fill in blanks with OP_0 - for (var i = 0; i < msSignatures.length; ++i) { - if (msSignatures[i]) continue - - msSignatures[i] = ops.OP_0 + if (allowIncomplete) { + for (var i = 0; i < msSignatures.length; ++i) { + if (msSignatures[i]) continue + + msSignatures[i] = ops.OP_0 + } + } else { + // Array.prototype.filter returns non-sparse array + msSignatures = msSignatures.filter(function (x) { return x }) } var redeemScript = allowIncomplete ? undefined : input.redeemScript @@ -275,7 +280,7 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) { return tx } -TransactionBuilder.prototype.sign = function (index, privKey, redeemScript, hashType) { +TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hashType) { assert(index in this.inputs, 'No input at index: ' + index) hashType = hashType || Transaction.SIGHASH_ALL @@ -287,6 +292,8 @@ TransactionBuilder.prototype.sign = function (index, privKey, redeemScript, hash input.scriptType && input.signatures + var kpPubKey = keyPair.getPublicKeyBuffer() + // are we almost ready to sign? if (canSign) { // if redeemScript was provided, enforce consistency @@ -314,21 +321,21 @@ TransactionBuilder.prototype.sign = function (index, privKey, redeemScript, hash var pubKeys = [] switch (scriptType) { case 'multisig': { - pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer) + pubKeys = redeemScript.chunks.slice(1, -2) break } case 'pubkeyhash': { var pkh1 = redeemScript.chunks[2] - var pkh2 = privKey.pub.getAddress().hash + var pkh2 = keyPair.getAddress().hash assert.deepEqual(pkh1, pkh2, 'privateKey cannot sign for this input') - pubKeys = [privKey.pub] + pubKeys = [kpPubKey] break } case 'pubkey': { - pubKeys = redeemScript.chunks.slice(0, 1).map(ECPubKey.fromBuffer) + pubKeys = redeemScript.chunks.slice(0, 1) break } } @@ -352,9 +359,9 @@ TransactionBuilder.prototype.sign = function (index, privKey, redeemScript, hash // we know nothin' Jon Snow, assume pubKeyHash } else { - input.prevOutScript = privKey.pub.getAddress().toOutputScript() + input.prevOutScript = keyPair.getAddress().toOutputScript() input.prevOutType = 'pubkeyhash' - input.pubKeys = [privKey.pub] + input.pubKeys = [kpPubKey] input.scriptType = input.prevOutType } } @@ -363,18 +370,44 @@ TransactionBuilder.prototype.sign = function (index, privKey, redeemScript, hash input.signatures = input.signatures || [] } + var signatureScript = input.redeemScript || input.prevOutScript + var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType) + + // enforce signature order matches public keys + if (input.scriptType === 'multisig' && input.redeemScript && input.signatures.length !== input.pubKeys.length) { + // maintain a local copy of unmatched signatures + var unmatched = input.signatures.slice() + + input.signatures = input.pubKeys.map(function (pubKey) { + var match + var keyPair2 = ECPair.fromPublicKeyBuffer(pubKey) + + // check for any matching signatures + unmatched.some(function (signature, i) { + if (!keyPair2.verify(signatureHash, signature)) return false + match = signature + + // remove matched signature from unmatched + unmatched.splice(i, 1) + + return true + }) + + return match || undefined + }) + } + // enforce in order signing of public keys assert(input.pubKeys.some(function (pubKey, i) { - if (!privKey.pub.Q.equals(pubKey.Q)) return false + if (kpPubKey.compare(pubKey) !== 0) return false assert(!input.signatures[i], 'Signature already exists') - var signatureScript = input.redeemScript || input.prevOutScript - var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType) - var signature = privKey.sign(signatureHash) + + var signature = keyPair.sign(signatureHash) input.signatures[i] = signature return true - }, this), 'privateKey cannot sign for this input') + }), 'key pair cannot sign for this input') } module.exports = TransactionBuilder diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index ee7d4e88e..bce7741cb 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -2,19 +2,21 @@ var assert = require('assert') var base58 = require('bs58') -var base58check = require('bs58check') var Bitcoin = require('../') var Address = Bitcoin.Address -var networks = Bitcoin.networks -var ECKey = Bitcoin.ECKey +var Block = Bitcoin.Block +var ECPair = Bitcoin.ECPair var ECSignature = Bitcoin.ECSignature var Transaction = Bitcoin.Transaction var Script = Bitcoin.Script +var networks = Bitcoin.networks + var base58_encode_decode = require('./fixtures/core/base58_encode_decode.json') var base58_keys_invalid = require('./fixtures/core/base58_keys_invalid.json') var base58_keys_valid = require('./fixtures/core/base58_keys_valid.json') +var blocks_valid = require('./fixtures/core/blocks.json') var sig_canonical = require('./fixtures/core/sig_canonical.json') var sig_noncanonical = require('./fixtures/core/sig_noncanonical.json') var sighash = require('./fixtures/core/sighash.json') @@ -92,32 +94,31 @@ describe('Bitcoin-core', function () { }) // base58_keys_valid - describe('ECKey', function () { + describe('ECPair', function () { base58_keys_valid.forEach(function (f) { var string = f[0] var hex = f[1] var params = f[2] - var network = params.isTestnet ? networks.testnet : networks.bitcoin if (!params.isPrivkey) return - var privKey = ECKey.fromWIF(string) + var keyPair = ECPair.fromWIF(string) it('imports ' + string + ' correctly', function () { - assert.equal(privKey.d.toHex(), hex) - assert.equal(privKey.pub.compressed, params.isCompressed) + assert.equal(keyPair.d.toHex(), hex) + assert.equal(keyPair.compressed, params.isCompressed) }) it('exports ' + hex + ' to ' + string, function () { - assert.equal(privKey.toWIF(network), string) + assert.equal(keyPair.toWIF(), string) }) }) }) // base58_keys_invalid - describe('ECKey', function () { + describe('ECPair', function () { var allowedNetworks = [ - networks.bitcoin.wif, - networks.testnet.wif + networks.bitcoin, + networks.testnet ] base58_keys_invalid.forEach(function (f) { @@ -125,11 +126,21 @@ describe('Bitcoin-core', function () { it('throws on ' + string, function () { assert.throws(function () { - ECKey.fromWIF(string) - var version = base58check.decode(string).readUInt8(0) + var keyPair = ECPair.fromWIF(string) + + assert(allowedNetworks.indexOf(keyPair.network) > -1, 'Invalid network') + }, /(Invalid|Unknown) (checksum|compression flag|network|WIF payload)/) + }) + }) + }) + + describe('Block', function () { + blocks_valid.forEach(function (f) { + it('fromHex can parse ' + f.id, function () { + var block = Block.fromHex(f.hex) - assert.notEqual(allowedNetworks.indexOf(version), -1, 'Invalid network') - }, /Invalid (checksum|compression flag|network|WIF payload)/) + assert.equal(block.getId(), f.id) + assert.equal(block.transactions.length, f.transactions) }) }) }) diff --git a/test/bufferutils.js b/test/bufferutils.js index 485447b2e..eaef22a7f 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -32,6 +32,17 @@ describe('bufferutils', function () { assert.equal(d.size, buffer.length) }) }) + + fixtures.invalid.readPushDataInt.forEach(function (f) { + if (!f.hexPD) return + + it('decodes ' + f.hexPD + ' as null', function () { + var buffer = new Buffer(f.hexPD, 'hex') + + var n = bufferutils.readPushDataInt(buffer, 0) + assert.equal(n, null) + }) + }) }) describe('readUInt64LE', function () { @@ -44,7 +55,7 @@ describe('bufferutils', function () { }) }) - fixtures.invalid.forEach(function (f) { + fixtures.invalid.readUInt64LE.forEach(function (f) { it('throws on ' + f.description, function () { var buffer = new Buffer(f.hex64, 'hex') @@ -66,7 +77,7 @@ describe('bufferutils', function () { }) }) - fixtures.invalid.forEach(function (f) { + fixtures.invalid.readUInt64LE.forEach(function (f) { it('throws on ' + f.description, function () { var buffer = new Buffer(f.hexVI, 'hex') @@ -77,22 +88,6 @@ describe('bufferutils', function () { }) }) - describe('equal', function () { - fixtures.valid.forEach(function (f) { - describe('for ' + f.hexVI, function () { - fixtures.valid.forEach(function (f2) { - it('equates the string comparison: ' + f.hexVI + ' === ' + f2.hexVI, function () { - var a = new Buffer(f.hexVI, 'hex') - var b = new Buffer(f2.hexVI, 'hex') - var expected = f.hexVI === f2.hexVI - - assert.equal(bufferutils.equal(a, b), expected) - }) - }) - }) - }) - }) - describe('reverse', function () { fixtures.valid.forEach(function (f) { it('reverses ' + f.hex64 + ' correctly', function () { @@ -151,7 +146,7 @@ describe('bufferutils', function () { }) }) - fixtures.invalid.forEach(function (f) { + fixtures.invalid.readUInt64LE.forEach(function (f) { it('throws on ' + f.description, function () { var buffer = new Buffer(8) buffer.fill(0) @@ -174,7 +169,7 @@ describe('bufferutils', function () { }) }) - fixtures.invalid.forEach(function (f) { + fixtures.invalid.readUInt64LE.forEach(function (f) { it('throws on ' + f.description, function () { var buffer = new Buffer(9) buffer.fill(0) diff --git a/test/eckey.js b/test/eckey.js deleted file mode 100644 index 16ce3d0cb..000000000 --- a/test/eckey.js +++ /dev/null @@ -1,150 +0,0 @@ -/* global describe, it, beforeEach, afterEach */ -/* eslint-disable no-new */ - -var assert = require('assert') -var crypto = require('crypto') -var ecurve = require('ecurve') -var networks = require('../src/networks') -var sinon = require('sinon') - -var BigInteger = require('bigi') -var ECKey = require('../src/eckey') - -var fixtures = require('./fixtures/eckey.json') - -describe('ECKey', function () { - describe('constructor', function () { - it('defaults to compressed', function () { - var privKey = new ECKey(BigInteger.ONE) - - assert.equal(privKey.pub.compressed, true) - }) - - it('supports the uncompressed flag', function () { - var privKey = new ECKey(BigInteger.ONE, false) - - assert.equal(privKey.pub.compressed, false) - }) - - fixtures.valid.forEach(function (f) { - it('calculates the matching pubKey for ' + f.d, function () { - var d = new BigInteger(f.d) - var privKey = new ECKey(d) - - assert.equal(privKey.pub.Q.toString(), f.Q) - }) - }) - - fixtures.invalid.constructor.forEach(function (f) { - it('throws on ' + f.d, function () { - var d = new BigInteger(f.d) - - assert.throws(function () { - new ECKey(d) - }, new RegExp(f.exception)) - }) - }) - }) - - it('uses the secp256k1 curve by default', function () { - var secp256k1 = ecurve.getCurveByName('secp256k1') - - for (var property in secp256k1) { - // FIXME: circular structures in ecurve - if (property === 'G') continue - if (property === 'infinity') continue - - var actual = ECKey.curve[property] - var expected = secp256k1[property] - - assert.deepEqual(actual, expected) - } - }) - - describe('fromWIF', function () { - fixtures.valid.forEach(function (f) { - f.WIFs.forEach(function (wif) { - it('imports ' + wif.string + ' correctly', function () { - var privKey = ECKey.fromWIF(wif.string) - - assert.equal(privKey.d.toString(), f.d) - assert.equal(privKey.pub.compressed, wif.compressed) - }) - }) - }) - - fixtures.invalid.WIF.forEach(function (f) { - it('throws on ' + f.string, function () { - assert.throws(function () { - ECKey.fromWIF(f.string) - }, new RegExp(f.exception)) - }) - }) - }) - - describe('toWIF', function () { - fixtures.valid.forEach(function (f) { - f.WIFs.forEach(function (wif) { - it('exports ' + wif.string + ' correctly', function () { - var privKey = ECKey.fromWIF(wif.string) - var network = networks[wif.network] - var result = privKey.toWIF(network) - - assert.equal(result, wif.string) - }) - }) - }) - }) - - describe('makeRandom', function () { - var exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' - var exPrivKey = ECKey.fromWIF(exWIF) - var exBuffer = exPrivKey.d.toBuffer(32) - - describe('uses default crypto RNG', function () { - beforeEach(function () { - sinon.stub(crypto, 'randomBytes').returns(exBuffer) - }) - - afterEach(function () { - crypto.randomBytes.restore() - }) - - it('generates a ECKey', function () { - var privKey = ECKey.makeRandom() - - assert.equal(privKey.toWIF(), exWIF) - }) - - it('supports compression', function () { - assert.equal(ECKey.makeRandom(true).pub.compressed, true) - assert.equal(ECKey.makeRandom(false).pub.compressed, false) - }) - }) - - it('allows a custom RNG to be used', function () { - function rng (size) { - return exBuffer.slice(0, size) - } - - var privKey = ECKey.makeRandom(undefined, rng) - assert.equal(privKey.toWIF(), exWIF) - }) - }) - - describe('signing', function () { - var hash = crypto.randomBytes(32) - var priv = ECKey.makeRandom() - var signature = priv.sign(hash) - - it('should verify against the public key', function () { - assert(priv.pub.verify(hash, signature)) - }) - - it('should not verify against the wrong public key', function () { - var priv2 = ECKey.makeRandom() - - assert(!priv2.pub.verify(hash, signature)) - }) - }) -}) diff --git a/test/ecpair.js b/test/ecpair.js new file mode 100644 index 000000000..9135284d7 --- /dev/null +++ b/test/ecpair.js @@ -0,0 +1,234 @@ +/* global describe, it, beforeEach */ +/* eslint-disable no-new */ + +var assert = require('assert') +var ecdsa = require('../src/ecdsa') +var ecurve = require('ecurve') +var networks = require('../src/networks') +var proxyquire = require('proxyquire') +var sinon = require('sinon') + +var BigInteger = require('bigi') +var ECPair = require('../src/ecpair') + +var fixtures = require('./fixtures/ecpair.json') + +describe('ECPair', function () { + describe('constructor', function () { + it('defaults to compressed', function () { + var keyPair = new ECPair(BigInteger.ONE) + + assert.equal(keyPair.compressed, true) + }) + + it('supports the uncompressed option', function () { + var keyPair = new ECPair(BigInteger.ONE, null, { + compressed: false + }) + + assert.equal(keyPair.compressed, false) + }) + + it('supports the network option', function () { + var keyPair = new ECPair(BigInteger.ONE, null, { + compressed: false, + network: networks.testnet + }) + + assert.equal(keyPair.network, networks.testnet) + }) + + it('throws if compressed option is not a bool', function () { + assert.throws(function () { + new ECPair(null, null, { + compressed: 2 + }, /Expected Boolean, got 2/) + }) + }) + + it('throws if public and private key given', function () { + var qBuffer = new Buffer('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 'hex') + var Q = ecurve.Point.decodeFrom(ECPair.curve, qBuffer) + + assert.throws(function () { + new ECPair(BigInteger.ONE, Q) + }, /Unexpected publicKey parameter/) + }) + + it('throws if network is missing pubKeyHash constants', function () { + assert.throws(function () { + new ECPair(null, null, { + network: {} + }, /Unknown pubKeyHash constants for network/) + }) + }) + + 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, { + compressed: f.compressed + }) + + assert.equal(keyPair.getPublicKeyBuffer().toString('hex'), f.Q) + }) + }) + + fixtures.invalid.constructor.forEach(function (f) { + it('throws on ' + f.d, function () { + var d = new BigInteger(f.d) + + assert.throws(function () { + new ECPair(d) + }, new RegExp(f.exception)) + }) + }) + }) + + describe('getPublicKeyBuffer', function () { + var keyPair + + beforeEach(function () { + keyPair = new ECPair(BigInteger.ONE) + }) + + it('wraps Q.getEncoded', sinon.test(function () { + this.mock(keyPair.Q).expects('getEncoded') + .once().calledWith(keyPair.compressed) + + keyPair.getPublicKeyBuffer() + })) + }) + + describe('fromWIF', function () { + fixtures.valid.forEach(function (f) { + it('imports ' + f.WIF + ' correctly', function () { + var keyPair = ECPair.fromWIF(f.WIF) + + assert.equal(keyPair.d.toString(), f.d) + assert.equal(keyPair.compressed, f.compressed) + assert.equal(keyPair.network, networks[f.network]) + }) + }) + + fixtures.invalid.fromWIF.forEach(function (f) { + it('throws on ' + f.string, function () { + assert.throws(function () { + ECPair.fromWIF(f.string) + }, new RegExp(f.exception)) + }) + }) + }) + + describe('toWIF', function () { + fixtures.valid.forEach(function (f) { + it('exports ' + f.WIF + ' correctly', function () { + var keyPair = ECPair.fromWIF(f.WIF) + var result = keyPair.toWIF() + + assert.equal(result, f.WIF) + }) + }) + }) + + describe('makeRandom', function () { + var d = new Buffer('0404040404040404040404040404040404040404040404040404040404040404', 'hex') + var exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' + + describe('uses randombytes RNG', function () { + it('generates a ECPair', function () { + var stub = { randombytes: function () { return d } } + var ProxiedECPair = proxyquire('../src/ecpair', stub) + + var keyPair = ProxiedECPair.makeRandom() + assert.equal(keyPair.toWIF(), exWIF) + }) + + it('passes the options param', sinon.test(function () { + var options = { + compressed: true + } + + // FIXME: waiting on https://github.com/cjohansen/Sinon.JS/issues/613 +// this.mock(ECPair).expects('constructor') +// .once().calledWith(options) + + ECPair.makeRandom(options) + })) + }) + + it('allows a custom RNG to be used', function () { + var keyPair = ECPair.makeRandom({ + rng: function (size) { return d.slice(0, size) } + }) + + assert.equal(keyPair.toWIF(), exWIF) + }) + }) + + describe('getAddress', function () { + fixtures.valid.forEach(function (f) { + it('returns ' + f.address + ' for ' + f.WIF, function () { + var keyPair = ECPair.fromWIF(f.WIF) + + assert.equal(keyPair.getAddress().toString(), f.address) + }) + }) + }) + + describe('ecdsa wrappers', function () { + var keyPair, hash + + beforeEach(function () { + keyPair = ECPair.makeRandom() + hash = new Buffer(32) + }) + + it('uses the secp256k1 curve by default', function () { + var secp256k1 = ecurve.getCurveByName('secp256k1') + + for (var property in secp256k1) { + // FIXME: circular structures in ecurve + if (property === 'G') continue + if (property === 'infinity') continue + + var actual = ECPair.curve[property] + var expected = secp256k1[property] + + assert.deepEqual(actual, expected) + } + }) + + describe('signing', function () { + it('wraps ecdsa.sign', sinon.test(function () { + this.mock(ecdsa).expects('sign') + .once().calledWith(ECPair.curve, hash, keyPair.d) + + keyPair.sign(hash) + })) + + it('throws if no private key is found', function () { + keyPair.d = null + + assert.throws(function () { + keyPair.sign(hash) + }, /Missing private key/) + }) + }) + + describe('verify', function () { + var signature + + beforeEach(function () { + signature = keyPair.sign(hash) + }) + + it('wraps ecdsa.verify', sinon.test(function () { + this.mock(ecdsa).expects('verify') + .once().calledWith(ECPair.curve, hash, signature, keyPair.Q) + + keyPair.verify(hash, signature) + })) + }) + }) +}) diff --git a/test/ecpubkey.js b/test/ecpubkey.js deleted file mode 100644 index 76943ed3e..000000000 --- a/test/ecpubkey.js +++ /dev/null @@ -1,120 +0,0 @@ -/* global describe, it, beforeEach */ - -var assert = require('assert') -var crypto = require('../src/crypto') -var networks = require('../src/networks') - -var BigInteger = require('bigi') -var ECPubKey = require('../src/ecpubkey') - -var ecurve = require('ecurve') -var curve = ecurve.getCurveByName('secp256k1') - -var fixtures = require('./fixtures/ecpubkey.json') - -describe('ECPubKey', function () { - var Q - - beforeEach(function () { - Q = ecurve.Point.fromAffine( - curve, - new BigInteger(fixtures.Q.x), - new BigInteger(fixtures.Q.y) - ) - }) - - describe('constructor', function () { - it('defaults to compressed', function () { - var pubKey = new ECPubKey(Q) - - assert.equal(pubKey.compressed, true) - }) - - it('supports the uncompressed flag', function () { - var pubKey = new ECPubKey(Q, false) - - assert.equal(pubKey.compressed, false) - }) - }) - - it('uses the secp256k1 curve by default', function () { - var secp256k1 = ecurve.getCurveByName('secp256k1') - - for (var property in secp256k1) { - // FIXME: circular structures in ecurve - if (property === 'G') continue - if (property === 'infinity') continue - - var actual = ECPubKey.curve[property] - var expected = secp256k1[property] - - assert.deepEqual(actual, expected) - } - }) - - describe('fromHex/toHex', function () { - it('supports compressed points', function () { - var pubKey = ECPubKey.fromHex(fixtures.compressed.hex) - - assert(pubKey.Q.equals(Q)) - assert.equal(pubKey.toHex(), fixtures.compressed.hex) - assert.equal(pubKey.compressed, true) - }) - - it('supports uncompressed points', function () { - var pubKey = ECPubKey.fromHex(fixtures.uncompressed.hex) - - assert(pubKey.Q.equals(Q)) - assert.equal(pubKey.toHex(), fixtures.uncompressed.hex) - assert.equal(pubKey.compressed, false) - }) - }) - - describe('getAddress', function () { - it('calculates the expected hash (compressed)', function () { - var pubKey = new ECPubKey(Q, true) - var address = pubKey.getAddress() - - assert.equal(address.hash.toString('hex'), fixtures.compressed.hash160) - }) - - it('calculates the expected hash (uncompressed)', function () { - var pubKey = new ECPubKey(Q, false) - var address = pubKey.getAddress() - - assert.equal(address.hash.toString('hex'), fixtures.uncompressed.hash160) - }) - - it('supports alternative networks', function () { - var pubKey = new ECPubKey(Q) - var address = pubKey.getAddress(networks.testnet) - - assert.equal(address.version, networks.testnet.pubKeyHash) - assert.equal(address.hash.toString('hex'), fixtures.compressed.hash160) - }) - }) - - describe('verify', function () { - var pubKey, signature - beforeEach(function () { - pubKey = new ECPubKey(Q) - - signature = { - r: new BigInteger(fixtures.signature.r), - s: new BigInteger(fixtures.signature.s) - } - }) - - it('verifies a valid signature', function () { - var hash = crypto.sha256(fixtures.message) - - assert(pubKey.verify(hash, signature)) - }) - - it("doesn't verify the wrong signature", function () { - var hash = crypto.sha256('mushrooms') - - assert(!pubKey.verify(hash, signature)) - }) - }) -}) diff --git a/test/fixtures/bufferutils.json b/test/fixtures/bufferutils.json index 8c6881d1b..baa003fe4 100644 --- a/test/fixtures/bufferutils.json +++ b/test/fixtures/bufferutils.json @@ -82,20 +82,36 @@ "hexVI": "ffffffffffffff1f00" } ], - "invalid": [ - { - "description": "n === 2^53", - "exception": "value is larger than maximum value for type", - "hex64": "0000000000002000", - "hexVI": "ff0000000000000020", - "dec": 9007199254740992 - }, - { - "description": "n > 2^53", - "exception": "value is larger than maximum value for type", - "hex64": "0100000000002000", - "hexVI": "ff0100000000000020", - "dec": 9007199254740993 - } - ] + "invalid": { + "readUInt64LE": [ + { + "description": "n === 2^53", + "exception": "value is larger than maximum value for type", + "hex64": "0000000000002000", + "hexVI": "ff0000000000000020", + "dec": 9007199254740992 + }, + { + "description": "n > 2^53", + "exception": "value is larger than maximum value for type", + "hex64": "0100000000002000", + "hexVI": "ff0100000000000020", + "dec": 9007199254740993 + } + ], + "readPushDataInt": [ + { + "description": "OP_PUSHDATA1, no size", + "hexPD": "4c" + }, + { + "description": "OP_PUSHDATA2, no size", + "hexPD": "4d" + }, + { + "description": "OP_PUSHDATA4, no size", + "hexPD": "4e" + } + ] + } } diff --git a/test/fixtures/core/blocks.json b/test/fixtures/core/blocks.json new file mode 100644 index 000000000..1a769ae62 --- /dev/null +++ b/test/fixtures/core/blocks.json @@ -0,0 +1,27 @@ +[ + { + "id": "0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af", + "transactions": 9, + "hex": "0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000" + }, + { + "id": "000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6", + "transactions": 4, + "hex": "0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000" + }, + { + "id": "000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6", + "transactions": 4, + "hex": "0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000" + }, + { + "id": "000000000000dab0130bbcc991d3d7ae6b81aa6f50a798888dfe62337458dc45", + "transactions": 1, + "hex": "0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020a02ffffffff0100f2052a01000000434104ecd3229b0571c3be876feaac0442a9f13c5a572742927af1dc623353ecf8c202225f64868137a18cdd85cbbb4c74fbccfd4f49639cf1bdc94a5672bb15ad5d4cac00000000" + }, + { + "id": "000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4", + "transactions": 7, + "hex": "0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000" + } +] diff --git a/test/fixtures/eckey.json b/test/fixtures/eckey.json deleted file mode 100644 index 7d1c4a4c0..000000000 --- a/test/fixtures/eckey.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "valid": [ - { - "d": "1", - "Q": "(55066263022277343669578718895168534326250603453777594175500187360389116729240,32670510020758816978083085130507043184471273380659243275938904335757337482424)", - "WIFs": [ - { - "network": "bitcoin", - "string": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "compressed": true - }, - { - "network": "bitcoin", - "string": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf", - "compressed": false - } - ] - }, - { - "d": "19898843618908353587043383062236220484949425084007183071220218307100305431102", - "Q": "(83225686012142088543596389522774768397204444195709443235253141114409346958144,23739058578904784236915560265041168694780215705543362357495033621678991351768)", - "WIFs": [ - { - "network": "bitcoin", - "string": "KxhEDBQyyEFymvfJD96q8stMbJMbZUb6D1PmXqBWZDU2WvbvVs9o", - "compressed": true - } - ] - }, - { - "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", - "Q": "(30095590000961171681152428142595206241714764354580127609094760797518133922356,93521207164355458151597931319591130635754976513751247168472016818884561919702)", - "WIFs": [ - { - "network": "bitcoin", - "string": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", - "compressed": true - }, - { - "network": "bitcoin", - "string": "5JdxzLtFPHNe7CAL8EBC6krdFv9pwPoRo4e3syMZEQT9srmK8hh", - "compressed": false - }, - { - "network": "testnet", - "string": "cRD9b1m3vQxmwmjSoJy7Mj56f4uNFXjcWMCzpQCEmHASS4edEwXv", - "compressed": true - }, - { - "network": "testnet", - "string": "92Qba5hnyWSn5Ffcka56yMQauaWY6ZLd91Vzxbi4a9CCetaHtYj", - "compressed": false - } - ] - }, - { - "d": "115792089237316195423570985008687907852837564279074904382605163141518161494336", - "Q": "(55066263022277343669578718895168534326250603453777594175500187360389116729240,83121579216557378445487899878180864668798711284981320763518679672151497189239)", - "WIFs": [ - { - "network": "bitcoin", - "string": "L5oLkpV3aqBjhki6LmvChTCV6odsp4SXM6FfU2Gppt5kFLaHLuZ9", - "compressed": true - } - ] - } - ], - "invalid": { - "constructor": [ - { - "exception": "Private key must be greater than 0", - "d": "-1" - }, - { - "exception": "Private key must be greater than 0", - "d": "0" - }, - { - "exception": "Private key must be less than the curve order", - "d": "115792089237316195423570985008687907852837564279074904382605163141518161494337" - }, - { - "exception": "Private key must be less than the curve order", - "d": "115792089237316195423570985008687907853269984665640564039457584007913129639935" - } - ], - "WIF": [ - { - "exception": "Invalid compression flag", - "string": "ju9rooVsmagsb4qmNyTysUSFB1GB6MdpD7eoGjUTPmZRAApJxRz" - }, - { - "exception": "Invalid WIF payload length", - "string": "7ZEtRQLhCsDQrd6ZKfmcESdXgas8ggZPN24ByEi5ey6VJW" - }, - { - "exception": "Invalid WIF payload length", - "string": "5qibUKwsnMo1qDiNp3prGaQkD2JfVJa8F8Na87H2CkMHvuVg6uKhw67Rh" - } - ] - } -} diff --git a/test/fixtures/ecpair.json b/test/fixtures/ecpair.json new file mode 100644 index 000000000..bc117e618 --- /dev/null +++ b/test/fixtures/ecpair.json @@ -0,0 +1,102 @@ +{ + "valid": [ + { + "d": "1", + "Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "compressed": true, + "network": "bitcoin", + "address": "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH", + "WIF": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + }, + { + "d": "1", + "Q": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "compressed": false, + "network": "bitcoin", + "address": "1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm", + "WIF": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf" + }, + { + "d": "19898843618908353587043383062236220484949425084007183071220218307100305431102", + "Q": "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", + "compressed": true, + "network": "bitcoin", + "address": "1MasfEKgSiaSeri2C6kgznaqBNtyrZPhNq", + "WIF": "KxhEDBQyyEFymvfJD96q8stMbJMbZUb6D1PmXqBWZDU2WvbvVs9o" + }, + { + "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", + "Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34", + "compressed": true, + "network": "bitcoin", + "address": "1LwwMWdSEMHJ2dMhSvAHZ3g95tG2UBv9jg", + "WIF": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx" + }, + { + "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", + "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", + "compressed": false, + "network": "bitcoin", + "address": "1zXcfvKCLgsFdJDYPuqpu1sF3q92tnnUM", + "WIF": "5JdxzLtFPHNe7CAL8EBC6krdFv9pwPoRo4e3syMZEQT9srmK8hh" + }, + { + "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", + "Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34", + "compressed": true, + "network": "testnet", + "address": "n1TteZiR3NiYojqKAV8fNxtTwsrjM7kVdj", + "WIF": "cRD9b1m3vQxmwmjSoJy7Mj56f4uNFXjcWMCzpQCEmHASS4edEwXv" + }, + { + "d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", + "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", + "compressed": false, + "network": "testnet", + "address": "mgWUuj1J1N882jmqFxtDepEC73Rr22E9GU", + "WIF": "92Qba5hnyWSn5Ffcka56yMQauaWY6ZLd91Vzxbi4a9CCetaHtYj" + }, + { + "d": "115792089237316195423570985008687907852837564279074904382605163141518161494336", + "Q": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "compressed": true, + "network": "bitcoin", + "address": "1GrLCmVQXoyJXaPJQdqssNqwxvha1eUo2E", + "WIF": "L5oLkpV3aqBjhki6LmvChTCV6odsp4SXM6FfU2Gppt5kFLaHLuZ9" + } + ], + "invalid": { + "constructor": [ + { + "exception": "Private key must be greater than 0", + "d": "-1" + }, + { + "exception": "Private key must be greater than 0", + "d": "0" + }, + { + "exception": "Private key must be less than the curve order", + "d": "115792089237316195423570985008687907852837564279074904382605163141518161494337" + }, + { + "exception": "Private key must be less than the curve order", + "d": "115792089237316195423570985008687907853269984665640564039457584007913129639935" + } + ], + "fromWIF": [ + { + "exception": "Invalid compression flag", + "string": "ju9rooVsmagsb4qmNyTysUSFB1GB6MdpD7eoGjUTPmZRAApJxRz" + }, + { + "exception": "Invalid WIF payload length", + "string": "7ZEtRQLhCsDQrd6ZKfmcESdXgas8ggZPN24ByEi5ey6VJW" + }, + { + "exception": "Invalid WIF payload length", + "string": "5qibUKwsnMo1qDiNp3prGaQkD2JfVJa8F8Na87H2CkMHvuVg6uKhw67Rh" + } + ] + } +} diff --git a/test/fixtures/ecpubkey.json b/test/fixtures/ecpubkey.json deleted file mode 100644 index 87b8aa2ed..000000000 --- a/test/fixtures/ecpubkey.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Q": { - "x": "55066263022277343669578718895168534326250603453777594175500187360389116729240", - "y": "32670510020758816978083085130507043184471273380659243275938904335757337482424" - }, - "compressed": { - "hex": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - "hash160": "751e76e8199196d454941c45d1b3a323f1433bd6" - }, - "uncompressed": { - "hex": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - "hash160": "91b24bf9f5288532960ac687abb035127b1d28a5" - }, - "message": "vires in numeris", - "signature": { - "r": "68972263025625296948424563184904289678530916807200550828762374724416876919710", - "s": "43478152510424186005054433052302509227777805602212468112169549534899266476898" - } -} diff --git a/test/fixtures/network.json b/test/fixtures/network.json index b638221c9..e542aadf4 100644 --- a/test/fixtures/network.json +++ b/test/fixtures/network.json @@ -1,151 +1,30 @@ -{ - "valid": { - "constants": [ - { - "network": "bitcoin", - "bip32": { - "private": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - "public": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" - } - }, - { - "network": "testnet", - "bip32": { - "private": "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", - "public": "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp" - } - }, - { - "network": "litecoin", - "bip32": { - "private": "Ltpv71G8qDifUiNetP6nmxPA5STrUVmv2J9YSmXajv8VsYBUyuPhvN9xCaQrfX2wo5xxJNtEazYCFRUu5FmokYMM79pcqz8pcdo4rNXAFPgyB4k", - "public": "Ltub2SSUS19CirucWFod2ZsYA2J4v4U76YiCXHdcQttnoiy5aGanFHCPDBX7utfG6f95u1cUbZJNafmvzNCzZZJTw1EmyFoL8u1gJbGM8ipu491" - } - }, - { - "network": "dogecoin", - "bip32": { - "private": "dgpv51eADS3spNJh9Gjth94XcPwAczvQaDJs9rqx11kvxKs6r3Ek8AgERHhjLs6mzXQFHRzQqGwqdeoDkZmr8jQMBfi43b7sT3sx3cCSk5fGeUR", - "public": "dgub8kXBZ7ymNWy2S8Q3jNgVjFUm5ZJ3QLLaSTdAA89ukSv7Q6MSXwE14b7Nv6eDpE9JJXinTKc8LeLVu19uDPrm5uJuhpKNzV2kAgncwo6bNpP" - } - }, - { - "network": "viacoin", - "bip32": { - "private": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - "public": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" - } - }, - { - "network": "viacointestnet", - "bip32": { - "private": "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", - "public": "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp" - } - }, - { - "network": "gamerscoin", - "bip32": { - "private": "Ltpv71G8qDifUiNetP6nmxPA5STrUVmv2J9YSmXajv8VsYBUyuPhvN9xCaQrfX2wo5xxJNtEazYCFRUu5FmokYMM79pcqz8pcdo4rNXAFPgyB4k", - "public": "Ltub2SSUS19CirucWFod2ZsYA2J4v4U76YiCXHdcQttnoiy5aGanFHCPDBX7utfG6f95u1cUbZJNafmvzNCzZZJTw1EmyFoL8u1gJbGM8ipu491" - } - }, - { - "network": "jumbucks", - "bip32": { - "private": "jprv5eCacBgN4Bz4zYxgVQ7RDt1a3eREhEaj8KjAcJ7YwogxGo2rmBF5kvAQS53JwZpo5wnUmJ9Q7kB6b2gQ1MzC6yaTc188hr6hXZ5t8Ruria1", - "public": "jpub1sBw1hDFtZYND339bReRb1xJbgFj6hJaVYemQgXAW9Dw9bN1JiZLJiUtHLgcTTEs1UgRGFAYm3XQPYsYJbpqj1aYPhrMsNcJHfgdAhvFZBB" - } - }, - { - "network": "zetacoin", - "bip32": { - "private": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", - "public": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" - } - } - ], - "estimateFee": [ - { - "description": "when txSize < 1kb", - "network": "bitcoin", - "txSize": 1, - "fee": 10000 - }, - { - "description": "when txSize >= 1kb", - "network": "bitcoin", - "txSize": 1000, - "fee": 10000 - }, - { - "description": "rounding", - "network": "bitcoin", - "txSize": 2800, - "fee": 30000 - }, - { - "description": "when outputs.value > DUST_SOFT_LIMIT, feePerKb is used", - "network": "dogecoin", - "txSize": 1000, - "outputs": [ - { - "value": 100000000 - } - ], - "fee": 100000000 - }, - { - "description": "when not every outputs.value > DUST_SOFT_LIMIT", - "network": "dogecoin", - "txSize": 1000, - "outputs": [ - { - "value": 99999999 - }, - { - "value": 99999999 - } - ], - "fee": 300000000 - }, - { - "description": "rounding", - "network": "dogecoin", - "txSize": 2800, - "fee": 300000000 - }, - { - "description": "when outputs.value > DUST_SOFT_LIMIT, feePerKb is used", - "network": "litecoin", - "txSize": 1000, - "outputs": [ - { - "value": 100000 - } - ], - "fee": 100000 - }, - { - "description": "when not every outputs.value > DUST_SOFT_LIMIT", - "network": "litecoin", - "txSize": 1000, - "outputs": [ - { - "value": 99999 - }, - { - "value": 99999 - } - ], - "fee": 300000 - }, - { - "description": "rounding", - "network": "litecoin", - "txSize": 2800, - "fee": 300000 - } - ] +[ + { + "network": "bitcoin", + "bip32": { + "private": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", + "public": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" + } + }, + { + "network": "testnet", + "bip32": { + "private": "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", + "public": "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp" + } + }, + { + "network": "litecoin", + "bip32": { + "private": "Ltpv71G8qDifUiNetP6nmxPA5STrUVmv2J9YSmXajv8VsYBUyuPhvN9xCaQrfX2wo5xxJNtEazYCFRUu5FmokYMM79pcqz8pcdo4rNXAFPgyB4k", + "public": "Ltub2SSUS19CirucWFod2ZsYA2J4v4U76YiCXHdcQttnoiy5aGanFHCPDBX7utfG6f95u1cUbZJNafmvzNCzZZJTw1EmyFoL8u1gJbGM8ipu491" + } + }, + { + "network": "dogecoin", + "bip32": { + "private": "dgpv51eADS3spNJh9Gjth94XcPwAczvQaDJs9rqx11kvxKs6r3Ek8AgERHhjLs6mzXQFHRzQqGwqdeoDkZmr8jQMBfi43b7sT3sx3cCSk5fGeUR", + "public": "dgub8kXBZ7ymNWy2S8Q3jNgVjFUm5ZJ3QLLaSTdAA89ukSv7Q6MSXwE14b7Nv6eDpE9JJXinTKc8LeLVu19uDPrm5uJuhpKNzV2kAgncwo6bNpP" + } } -} +] diff --git a/test/fixtures/script.json b/test/fixtures/script.json index e06f9f077..714976cb8 100644 --- a/test/fixtures/script.json +++ b/test/fixtures/script.json @@ -1,86 +1,96 @@ { "valid": [ { + "asm": "031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95 OP_CHECKSIG", "description": "pay-to-PubKey", - "hex": "21031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95ac", - "type": "pubkey", "hash": "26e645ab170255f2a0a82d29e48f35b14ae7c826", - "pubKey": "031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95", - "asm": "031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95 OP_CHECKSIG", - "scriptPubKey": true + "hex": "21031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95ac", + "pubKey": "031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95" }, { + "asm": "OP_HASH160 e8c300c87986efa84c37c0519929019ef86eb5b4 OP_EQUAL", "description": "P2SH ScriptPubKey", - "hex": "a914e8c300c87986efa84c37c0519929019ef86eb5b487", - "type": "scripthash", "hash": "0ba47b56a573bab4b430ad6ed3ec79270e04b066", - "asm": "OP_HASH160 e8c300c87986efa84c37c0519929019ef86eb5b4 OP_EQUAL", - "scriptPubKey": true + "hex": "a914e8c300c87986efa84c37c0519929019ef86eb5b487" }, { + "asm": "OP_DUP OP_HASH160 5a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a8 OP_EQUALVERIFY OP_CHECKSIG", "description": "PubKeyHash ScriptPubKey", - "hex": "76a9145a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a888ac", - "type": "pubkeyhash", "hash": "a5313f33d5c7b81674b35f7f3febc3522ef234db", - "asm": "OP_DUP OP_HASH160 5a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a8 OP_EQUALVERIFY OP_CHECKSIG", - "scriptPubKey": true + "hex": "76a9145a3acbc7bbcc97c5ff16f5909c9d7d3fadb293a888ac" }, { + "asm": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301 040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8", "description": "pubKeyHash scriptSig", - "hex": "48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f30141040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8", - "type": "pubkeyhash", "hash": "b9bac2a5c5c29bb27c382d41fa3d179c646c78fd", - "asm": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301 040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8", - "scriptPubKey": false + "hex": "48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f30141040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8" }, { + "asm": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", "description": "pubKey scriptSig", - "hex": "48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", - "type": "pubkey", "hash": "44d9982c3e79452e02ef5816976a0e20a0ec1cba", - "signature": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", - "asm": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", - "scriptPubKey": false + "hex": "48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", + "signature": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301" }, { + "asm": "OP_TRUE 032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca33016 02308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a OP_2 OP_CHECKMULTISIG", "description": "Valid multisig script", - "hex": "5121032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca330162102308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a52ae", - "type": "multisig", "hash": "f1c98f0b74ecabcf78ae20dfa224bb6666051fbe", - "asm": "OP_TRUE 032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca33016 02308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a OP_2 OP_CHECKMULTISIG", - "scriptPubKey": true + "hex": "5121032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca330162102308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a52ae" }, { + "asm": "OP_0 304402202b29881db1b4cc128442d955e906d41c77365ed9a8392b584be12d980b236459022009da4bc60d09280aa26f4f981bfbed94eb7263d92920961e48a7f3f0991895b101 3045022100871708a7597c1dbebff2a5527a56a1f2b49d73e35cd825a07285f5f29f5766d8022003bd7ac25334e9a6d6020cc8ba1be67a8c70dca8e7063ea0547d79c45b9bc12601", "description": "mutisig scriptSig", - "hex": "0047304402202b29881db1b4cc128442d955e906d41c77365ed9a8392b584be12d980b236459022009da4bc60d09280aa26f4f981bfbed94eb7263d92920961e48a7f3f0991895b101483045022100871708a7597c1dbebff2a5527a56a1f2b49d73e35cd825a07285f5f29f5766d8022003bd7ac25334e9a6d6020cc8ba1be67a8c70dca8e7063ea0547d79c45b9bc12601", - "type": "multisig", "hash": "b1ef3ae2c77b356eff81049aad7dfd2eeb34c6f5", - "asm": "OP_0 304402202b29881db1b4cc128442d955e906d41c77365ed9a8392b584be12d980b236459022009da4bc60d09280aa26f4f981bfbed94eb7263d92920961e48a7f3f0991895b101 3045022100871708a7597c1dbebff2a5527a56a1f2b49d73e35cd825a07285f5f29f5766d8022003bd7ac25334e9a6d6020cc8ba1be67a8c70dca8e7063ea0547d79c45b9bc12601", - "scriptPubKey": false + "hex": "0047304402202b29881db1b4cc128442d955e906d41c77365ed9a8392b584be12d980b236459022009da4bc60d09280aa26f4f981bfbed94eb7263d92920961e48a7f3f0991895b101483045022100871708a7597c1dbebff2a5527a56a1f2b49d73e35cd825a07285f5f29f5766d8022003bd7ac25334e9a6d6020cc8ba1be67a8c70dca8e7063ea0547d79c45b9bc12601" }, { + "asm": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", "description": "OP_RETURN script", - "hex": "6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", - "type": "nulldata", "hash": "ec88f016655477663455fe6a8e83508c348ea145", - "asm": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", - "scriptPubKey": true + "hex": "6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474" }, { + "asm": "OP_HASH256 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 OP_EQUAL", "description": "Non standard script", - "hex": "aa206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000087", - "type": "nonstandard", "hash": "3823382e70d1930989813d3459988e0d7c2861d8", - "asm": "OP_HASH256 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 OP_EQUAL", - "scriptPubKey": true + "hex": "aa206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000087" }, { + "asm": "OP_0 OP_0 OP_0 OP_CHECKMULTISIG", "description": "Invalid multisig script", - "hex": "000000ae", - "type": "nonstandard", "hash": "62ede8963f9387544935f168745262f703dab1fb", - "asm": "OP_0 OP_0 OP_0 OP_CHECKMULTISIG", - "scriptPubKey": true + "hex": "000000ae" + }, + { + "asm": "", + "description": "Not enough data: OP_1", + "hash": "c51b66bced5e4491001bd702669770dccf440982", + "hex": "01" + }, + { + "asm": "", + "description": "Not enough data: OP_2", + "hash": "d48ce86c698f246829921ba9fb2a844ae2adba67", + "hex": "0201" + }, + { + "asm": "", + "description": "Not enough data: OP_PUSHDATA1 0x02", + "hash": "b663ef01a96ff65bec84a3fb14688d6ff7fc617c", + "hex": "4c0201" + }, + { + "asm": "", + "description": "Not enough data: OP_PUSHDATA2 0xffff", + "hash": "b4d2fac2836232e59d7b1628f64f24bce3cb4478", + "hex": "4dffff01" + }, + { + "asm": "", + "description": "Not enough data: OP_PUSHDATA4 0xffffffff", + "hash": "941db1ca32faf29e1338fb966bb56d98fbce4823", + "hex": "4effffffff01" } ] } diff --git a/test/fixtures/scripts.json b/test/fixtures/scripts.json index e79a6e76a..2fcd42302 100644 --- a/test/fixtures/scripts.json +++ b/test/fixtures/scripts.json @@ -98,6 +98,12 @@ "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "redeemScriptSig": "OP_0 OP_0 30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801", "scriptSig": "OP_0 OP_0 30450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a801 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" + }, + { + "type": "scripthash", + "redeemScript": "OP_0", + "redeemScriptSig": "OP_0", + "scriptSigHex": "000100" } ], "invalid": { @@ -113,7 +119,7 @@ "scriptSig": "OP_0 304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801 3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501 OP_RESERVED" }, { - "description": "signature forms invalid script", + "description": "redeemScript is a signature, therefore not a valid script", "scriptSig": "OP_0 3045022100e12b17b3a4c80c401a1687487bd2bafee9e5f1f8f1ffc6180ce186672ad7b43a02205e316d1e5e71822f5ef301b694e578fa9c94af4f5f098c952c833f4691307f4e01" } ], @@ -173,31 +179,19 @@ { "description": "Not enough signatures provided", "type": "multisig", - "pubKeys": [ - "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340" - ], - "signatures": [ - null, - null - ] + "scriptPubKey": "OP_2 0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340 OP_2 OP_CHECKMULTISIG", + "signatures": [] }, { "exception": "Not enough signatures provided", - "pubKeys": [ - "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a" - ], + "scriptPubKey": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", "signatures": [ "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801" ] }, { "exception": "Too many signatures provided", - "pubKeys": [ - "02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1", - "0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a" - ], + "scriptPubKey": "OP_2 02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1 0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a OP_2 OP_CHECKMULTISIG", "signatures": [ "304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801", "3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501", diff --git a/test/fixtures/transaction.json b/test/fixtures/transaction.json index 043dfed03..97108181c 100644 --- a/test/fixtures/transaction.json +++ b/test/fixtures/transaction.json @@ -173,6 +173,57 @@ ], "locktime": 0 } + }, + { + "description": "Transaction that ignores script chunking rules - Bug #367", + "id": "ebc9fa1196a59e192352d76c0f6e73167046b9d37b8302b6bb6968dfd279b767", + "hash": "67b779d2df6869bbb602837bd3b9467016736e0f6cd75223199ea59611fac9eb", + "hex": "01000000019ac03d5ae6a875d970128ef9086cef276a1919684a6988023cc7254691d97e6d010000006b4830450221009d41dc793ba24e65f571473d40b299b6459087cea1509f0d381740b1ac863cb6022039c425906fcaf51b2b84d8092569fb3213de43abaff2180e2a799d4fcb4dd0aa012102d5ede09a8ae667d0f855ef90325e27f6ce35bbe60a1e6e87af7f5b3c652140fdffffffff080100000000000000010101000000000000000202010100000000000000014c0100000000000000034c02010100000000000000014d0100000000000000044dffff010100000000000000014e0100000000000000064effffffff0100000000", + "raw": { + "version": 1, + "locktime": 0, + "ins": [ + { + "hash": "9ac03d5ae6a875d970128ef9086cef276a1919684a6988023cc7254691d97e6d", + "index": 1, + "script": "30450221009d41dc793ba24e65f571473d40b299b6459087cea1509f0d381740b1ac863cb6022039c425906fcaf51b2b84d8092569fb3213de43abaff2180e2a799d4fcb4dd0aa01 02d5ede09a8ae667d0f855ef90325e27f6ce35bbe60a1e6e87af7f5b3c652140fd" + } + ], + "outs": [ + { + "data": "01", + "value": 1 + }, + { + "data": "0201", + "value": 1 + }, + { + "data": "4c", + "value": 1 + }, + { + "data": "4c0201", + "value": 1 + }, + { + "data": "4d", + "value": 1 + }, + { + "data": "4dffff01", + "value": 1 + }, + { + "data": "4e", + "value": 1 + }, + { + "data": "4effffffff01", + "value": 1 + } + ] + } } ], "invalid": { diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index 1845a703b..9fd03f3a1 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -10,7 +10,7 @@ "vout": 0, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] } @@ -32,7 +32,7 @@ "prevTxScript": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 OP_CHECKSIG", "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] } @@ -44,6 +44,28 @@ } ] }, + { + "description": "Transaction w/ scriptHash(pubKeyHash) -> pubKeyHash", + "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000085483045022100a3b254e1c10b5d039f36c05f323995d6e5a367d98dd78a13d5bbc3991b35720e022022fccea3897d594de0689601fbd486588d5bfa6915be2386db0397ee9a6e80b601210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817981976a914751e76e8199196d454941c45d1b3a323f1433bd688acffffffff0110270000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "signs": [ + { + "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/ scriptHash(multisig 2-of-2) -> pubKeyHash", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd1b0100483045022100b7a9bab60c4307349de9571ce0bd26ebb9d68d4e9ab3f9173e1f736f1390a04a022020931ff70e87033cdd94bdf434e865993b2258065c5c222a53f29d077bcfa4480147304402206d79ad83f1ab12fc9feee9e66412de842fcbf8de0632beb4433d469f24f0fb4e022079e6df186582f2686a3292bde8e50dac36cb9bec3991995fe331e1daef7df8a4014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0110270000000000001976a914faf1d99bf040ea9c7f8cc9f14ac6733ad75ce24688ac00000000", @@ -53,11 +75,11 @@ "vout": 0, "signs": [ { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG" }, { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" } ] } @@ -79,10 +101,10 @@ "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "signs": [ { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" }, { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" } ] } @@ -104,10 +126,10 @@ "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "signs": [ { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" }, { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" } ] } @@ -119,6 +141,31 @@ } ] }, + { + "description": "Transaction w/ scriptHash(multisig 2-of-3)", + "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000fd5e0100483045022100eec19e061cad41610f9b42d2b06638b6b0fec3da0de9c6858e7f8c06053979900220622936dd47e202b2ad17639cda680e52334d407149252959936bb1f38e4acc52014830450221009aac215157a74a18234fd06be27448dccee809986bbf93be457a9262f0c69a9402203ff41d7c757f0e8951e4471f205087ecff499f986400ab18210eaad9a628e33c014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", + "inputs": [ + { + "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "vout": 0, + "signs": [ + { + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG" + }, + { + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe" + } + ] + } + ], + "outputs": [ + { + "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", + "value": 1000 + } + ] + }, { "description": "Transaction w/ scriptHash(pubKey) -> pubKeyHash", "txHex": "0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000006c47304402201115644b134932c8a7a8e925769d130a801288d477130e2bf6fadda20b33754d02202ecefbf63844d7cb2d5868539c39f973fe019f72e5c31a707836c0d61ef317db012321033e29aea1168a835d5e386c292082db7b7807172a10ec634ad34226f36d79e70facffffffff0100f90295000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000", @@ -129,7 +176,7 @@ "prevTxScript": "OP_HASH160 e89677d91455e541630d62c63718bef738b478b1 OP_EQUAL", "signs": [ { - "privKey": "KxLDMPtVM7sLSu2v5n1LybDibw6P9FFbL4pUwJ51UDm7rp5AmXWW", + "keyPair": "KxLDMPtVM7sLSu2v5n1LybDibw6P9FFbL4pUwJ51UDm7rp5AmXWW", "redeemScript": "033e29aea1168a835d5e386c292082db7b7807172a10ec634ad34226f36d79e70f OP_CHECKSIG" } ] @@ -151,7 +198,7 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] } @@ -175,7 +222,7 @@ "sequence": 2147001, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] } @@ -187,6 +234,242 @@ } ] } + ], + "multisig": [ + { + "description": "P2SH 2of2 multisig, signed in correct order", + "inputs": [ + { + "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", + "vout": 0, + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", + "signs": [ + { + "pubKeyIndex": 0, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" + }, + { + "pubKeyIndex": 1, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" + } + ] + } + ], + "outputs": [ + { + "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", + "value": 1000 + } + ], + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + }, + { + "description": "P2SH 2of2 multisig, signed in shuffled order", + "inputs": [ + { + "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", + "vout": 0, + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", + "signs": [ + { + "pubKeyIndex": 1, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "scriptSig": "OP_0 OP_0 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" + }, + { + "pubKeyIndex": 0, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" + } + ] + } + ], + "outputs": [ + { + "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", + "value": 1000 + } + ], + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + }, + { + "description": "P2SH 2of2 multisig, manually messed up order of signatures", + "inputs": [ + { + "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", + "vout": 0, + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", + "signs": [ + { + "pubKeyIndex": 0, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" + }, + { + "pubKeyIndex": 1, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" + } + ] + } + ], + "outputs": [ + { + "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", + "value": 1000 + } + ], + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c0100483045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001483045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b06576014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + }, + { + "description": "P2SH 2of3 multisig, signed by key 1 and 2", + "inputs": [ + { + "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", + "vout": 0, + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", + "signs": [ + { + "pubKeyIndex": 0, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" + }, + { + "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 + } + ], + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100ae06d8269b79b5cfa0297d1d88261b0061e52fc2814948c3aa05fa78ee76894302206e0c79a5c90569d8c72a542ef9a06471cbbcd2c651b312339983dfba4f8ff463014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + }, + { + "description": "P2SH 2of3 multisig, signed by key 1 and 3", + "inputs": [ + { + "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", + "vout": 0, + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", + "signs": [ + { + "pubKeyIndex": 0, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" + }, + { + "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 + } + ], + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + }, + { + "description": "P2SH 2of3 multisig, signed by key 3 and 1", + "inputs": [ + { + "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", + "vout": 0, + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", + "signs": [ + { + "pubKeyIndex": 2, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", + "scriptSig": "OP_0 OP_0 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" + }, + { + "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 + } + ], + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + }, + { + "description": "P2SH 2of3 multisig, signed by key 1 and 3, manually messed up order of signatures", + "inputs": [ + { + "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", + "vout": 0, + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", + "signs": [ + { + "pubKeyIndex": 0, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" + }, + { + "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 + } + ], + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + }, + { + "description": "P2SH 2of3 multisig, signed by key 3 and 1, manually removing OP_0s", + "inputs": [ + { + "txId": "4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf", + "vout": 0, + "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG", + "signs": [ + { + "pubKeyIndex": 2, + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", + "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" + } + ] + } + ], + "outputs": [ + { + "script": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", + "value": 1000 + } + ], + "txHex": "0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd5e0100483045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01483045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af0014cc952410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253aeffffffff01e8030000000000001976a914aa4d7985c57e011a8b3dd8e0e5a73aaef41629c588ac00000000" + } ] }, "invalid": { @@ -239,7 +522,7 @@ "vout": 0, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] }, @@ -273,10 +556,10 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" }, { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "throws": true } ] @@ -297,7 +580,7 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", "throws": true } @@ -320,7 +603,7 @@ "prevTxScript": "OP_HASH160 7f67f0521934a57d3039f77f9f32cf313f3ac74b OP_EQUAL", "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "throws": true } ] @@ -342,11 +625,11 @@ "signs": [ { "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" }, { "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "throws": true } ] @@ -368,11 +651,11 @@ "signs": [ { "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "hashType": 4 }, { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "hashType": 2, "throws": true } @@ -394,7 +677,7 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_HASH160 7f67f0521934a57d3039f77f9f32cf313f3ac74b OP_EQUAL", "throws": true } @@ -417,7 +700,7 @@ "prevTxScript": "OP_DUP OP_HASH160 aa4d7985c57e011a8b3dd8e0e5a73aaef41629c5 OP_EQUALVERIFY OP_CHECKSIG", "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG", "throws": true } @@ -440,11 +723,11 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf", "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG" }, { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf", "throws": true } ] @@ -458,15 +741,15 @@ ] }, { - "description": "Wrong private key for multisig redeemScript", - "exception": "privateKey cannot sign for this input", + "description": "Wrong key pair for multisig redeemScript", + "exception": "key pair cannot sign for this input", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "vout": 1, "signs": [ { - "privKey": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG", "throws": true } @@ -489,7 +772,7 @@ "prevTxScript": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", "signs": [ { - "privKey": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", + "keyPair": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx", "throws": true } ] diff --git a/test/hdnode.js b/test/hdnode.js index 342f0f5ff..46833e894 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -1,98 +1,54 @@ -/* global describe, it */ +/* global describe, it, beforeEach */ /* eslint-disable no-new */ var assert = require('assert') var networks = require('../src/networks') +var sinon = require('sinon') var BigInteger = require('bigi') -var ECKey = require('../src/eckey') -var ECPubKey = require('../src/ecpubkey') +var ECPair = require('../src/ecpair') var HDNode = require('../src/hdnode') -var ecurve = require('ecurve') -var curve = ecurve.getCurveByName('secp256k1') - var fixtures = require('./fixtures/hdnode.json') describe('HDNode', function () { describe('Constructor', function () { - var d = BigInteger.ONE - var Q = curve.G.multiply(d) - var chainCode = new Buffer(32) - chainCode.fill(1) - - it('calculates the publicKey from a BigInteger', function () { - var hd = new HDNode(d, chainCode) - - assert(hd.pubKey.Q.equals(Q)) - }) - - it('allows initialization directly from an ECKey', function () { - var ek = new ECKey(d) - var hd = new HDNode(ek, chainCode) - - assert.equal(hd.privKey, ek) - }) - - it('allows initialization directly from an ECPubKey', function () { - var ek = new ECPubKey(Q) - var hd = new HDNode(ek, chainCode) - - assert.equal(hd.pubKey, ek) - }) - - it('throws if ECKey is not compressed', function () { - var ek = new ECKey(d, false) - - assert.throws(function () { - new HDNode(ek, chainCode) - }, /ECKey must be compressed/) - }) - - it('throws if ECPubKey is not compressed', function () { - var ek = new ECPubKey(Q, false) - - assert.throws(function () { - new HDNode(ek, chainCode) - }, /ECPubKey must be compressed/) - }) + var keyPair, chainCode - it('only uses compressed points', function () { - var hd = new HDNode(Q, chainCode) - var hdP = new HDNode(d, chainCode) + beforeEach(function () { + var d = BigInteger.ONE - assert.strictEqual(hd.pubKey.compressed, true) - assert.strictEqual(hdP.pubKey.compressed, true) + keyPair = new ECPair(d, null) + chainCode = new Buffer(32) + chainCode.fill(1) }) it('has a default depth/index of 0', function () { - var hd = new HDNode(Q, chainCode) + var hd = new HDNode(keyPair, chainCode) assert.strictEqual(hd.depth, 0) assert.strictEqual(hd.index, 0) }) - it('defaults to the bitcoin network', function () { - var hd = new HDNode(Q, chainCode) - - assert.equal(hd.network, networks.bitcoin) - }) - - it('supports alternative networks', function () { - var hd = new HDNode(Q, chainCode, networks.testnet) + it('throws on uncompressed keyPair', function () { + keyPair.compressed = false - assert.equal(hd.network, networks.testnet) + 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(d, chainCode.slice(0, 20), networks.testnet) + new HDNode(keyPair, chainCode.slice(0, 20)) }, /Expected chainCode length of 32, got 20/) }) it('throws when an unknown network is given', function () { + keyPair.network = {} + assert.throws(function () { - new HDNode(d, chainCode, {}) + new HDNode(keyPair, chainCode) }, /Unknown BIP32 constants for network/) }) }) @@ -103,7 +59,7 @@ describe('HDNode', function () { var network = networks[f.network] var hd = HDNode.fromSeedHex(f.master.seed, network) - assert.equal(hd.privKey.toWIF(network), f.master.wif) + assert.equal(hd.keyPair.toWIF(), f.master.wif) assert.equal(hd.chainCode.toString('hex'), f.master.chainCode) }) }) @@ -144,17 +100,21 @@ describe('HDNode', function () { describe('fromBase58', function () { fixtures.valid.forEach(function (f) { it('imports ' + f.master.base58 + ' (public) correctly', function () { + var network = networks[f.network] var hd = HDNode.fromBase58(f.master.base58) assert.equal(hd.toBase58(), f.master.base58) + assert.equal(hd.keyPair.network, network) }) }) fixtures.valid.forEach(function (f) { it('imports ' + f.master.base58Priv + ' (private) correctly', function () { - var hd = HDNode.fromBase58(f.master.base58Priv) + var network = networks[f.network] + var hd = HDNode.fromBase58(f.master.base58Priv, network) assert.equal(hd.toBase58(), f.master.base58Priv) + assert.equal(hd.keyPair.network, network) }) }) @@ -190,13 +150,20 @@ describe('HDNode', function () { }) describe('getAddress', function () { - fixtures.valid.forEach(function (f) { - it('returns ' + f.master.address + ' for ' + f.master.fingerprint, function () { - var hd = HDNode.fromBase58(f.master.base58) + var hd - assert.equal(hd.getAddress().toString(), f.master.address) - }) + beforeEach(function () { + var f = fixtures.valid[0] + + hd = HDNode.fromBase58(f.master.base58) }) + + it('wraps ECPair.getAddress', sinon.test(function () { + this.mock(hd.keyPair).expects('getAddress') + .once().returns('foobar') + + assert.equal(hd.getAddress(), 'foobar') + })) }) describe('neutered', function () { @@ -206,8 +173,8 @@ describe('HDNode', function () { var hd = HDNode.fromBase58(f.master.base58) var hdn = hd.neutered() - assert.equal(hdn.privKey, undefined) - assert.equal(hdn.pubKey.toHex(), hd.pubKey.toHex()) + assert.equal(hdn.keyPair.d, null) + assert.equal(hdn.keyPair.Q, hd.keyPair.Q) assert.equal(hdn.chainCode, hd.chainCode) assert.equal(hdn.depth, hd.depth) assert.equal(hdn.index, hd.index) @@ -215,9 +182,9 @@ describe('HDNode', function () { }) describe('derive', function () { - function verifyVector (hd, network, v, depth) { - assert.equal(hd.privKey.toWIF(network), v.wif) - assert.equal(hd.pubKey.toHex(), v.pubKey) + function verifyVector (hd, v, depth) { + assert.equal(hd.keyPair.toWIF(), v.wif) + assert.equal(hd.keyPair.getPublicKeyBuffer().toString('hex'), v.pubKey) assert.equal(hd.chainCode.toString('hex'), v.chainCode) assert.equal(hd.depth, depth || 0) @@ -241,7 +208,7 @@ describe('HDNode', function () { hd = hd.derive(c.m) } - verifyVector(hd, network, c, i + 1) + verifyVector(hd, c, i + 1) }) }) }) diff --git a/test/integration/advanced.js b/test/integration/advanced.js index 3d2022851..45e52a742 100644 --- a/test/integration/advanced.js +++ b/test/integration/advanced.js @@ -6,10 +6,10 @@ var blockchain = new (require('cb-helloblock'))('testnet') describe('bitcoinjs-lib (advanced)', function () { it('can sign a Bitcoin message', function () { - var key = bitcoin.ECKey.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') + var keyPair = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') var message = 'This is an example of a signed message.' - var signature = bitcoin.message.sign(key, message) + var signature = bitcoin.message.sign(keyPair, message) assert.equal(signature.toString('base64'), 'G9L5yLFjti0QTHhPyFrZCT1V/MMnBtXKmoiKDZ78NDBjERki6ZTQZdSMCtkgoNmp17By9ItJr8o7ChX0XxY91nk=') }) @@ -24,8 +24,10 @@ describe('bitcoinjs-lib (advanced)', function () { it('can create an OP_RETURN transaction', function (done) { this.timeout(20000) - var key = bitcoin.ECKey.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') - var address = key.pub.getAddress(bitcoin.networks.testnet).toString() + var keyPair = bitcoin.ECPair.makeRandom({ + network: bitcoin.networks.testnet + }) + var address = keyPair.getAddress().toString() blockchain.addresses.__faucetWithdraw(address, 2e4, function (err) { if (err) return done(err) @@ -41,7 +43,7 @@ describe('bitcoinjs-lib (advanced)', function () { tx.addInput(unspent.txId, unspent.vout) tx.addOutput(dataScript, 1000) - tx.sign(0, key) + tx.sign(0, keyPair) var txBuilt = tx.build() diff --git a/test/integration/basic.js b/test/integration/basic.js index 069a535e0..0bb68642f 100644 --- a/test/integration/basic.js +++ b/test/integration/basic.js @@ -3,45 +3,57 @@ var assert = require('assert') var bigi = require('bigi') var bitcoin = require('../../') -var crypto = require('crypto') -var sinon = require('sinon') describe('bitcoinjs-lib (basic)', function () { - it('can generate a random bitcoin address', sinon.test(function () { + it('can generate a random bitcoin address', function () { // for testing only - this.mock(crypto).expects('randomBytes') - .onCall(0).returns(new Buffer('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz')) + function rng () { return new Buffer('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } - // generate random key - var key = bitcoin.ECKey.makeRandom() - var address = key.pub.getAddress().toString() + // generate random keyPair + var keyPair = bitcoin.ECPair.makeRandom({ rng: rng }) + var address = keyPair.getAddress().toString() assert.equal(address, '1F5VhMHukdnUES9kfXqzPzMeF1GPHKiF64') - })) + }) it('can generate an address from a SHA256 hash', function () { var hash = bitcoin.crypto.sha256('correct horse battery staple') var d = bigi.fromBuffer(hash) - var key = new bitcoin.ECKey(d) + var keyPair = new bitcoin.ECPair(d) + var address = keyPair.getAddress().toString() + + assert.equal(address, '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') + }) + + it('can generate a random keypair for alternative networks', function () { + // for testing only + function rng () { return new Buffer('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } + + var litecoin = bitcoin.networks.litecoin + + var keyPair = bitcoin.ECPair.makeRandom({ network: litecoin, rng: rng }) + var wif = keyPair.toWIF() + var address = keyPair.getAddress().toString() - assert.equal(key.pub.getAddress().toString(), '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') + assert.equal(address, 'LZJSxZbjqJ2XVEquqfqHg1RQTDdfST5PTn') + assert.equal(wif, 'T7A4PUSgTDHecBxW1ZiYFrDNRih2o7M8Gf9xpoCgudPF9gDiNvuS') }) it('can import an address via WIF', function () { - var key = bitcoin.ECKey.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var address = key.pub.getAddress().toString() + var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + var address = keyPair.getAddress().toString() assert.equal(address, '19AAjaTUbRjQCMuVczepkoPswiZRhjtg31') }) it('can create a Transaction', function () { - var key = bitcoin.ECKey.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') + var keyPair = bitcoin.ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') var tx = new bitcoin.TransactionBuilder() tx.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) tx.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) - tx.sign(0, key) + tx.sign(0, keyPair) assert.equal(tx.build().toHex(), '0100000001313eb630b128102b60241ca895f1d0ffca2170d5a0990e094f2182c102ab94aa000000006b483045022100aefbcf847900b01dd3e3debe054d3b6d03d715d50aea8525f5ea3396f168a1fb022013d181d05b15b90111808b22ef4f9ebe701caf2ab48db269691fdf4e9048f4f60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01983a0000000000001976a914ad618cf4333b3b248f9744e8e81db2964d0ae39788ac00000000') }) diff --git a/test/integration/crypto.js b/test/integration/crypto.js index 631809235..8e69e44bf 100644 --- a/test/integration/crypto.js +++ b/test/integration/crypto.js @@ -9,17 +9,17 @@ var crypto = require('crypto') describe('bitcoinjs-lib (crypto)', function () { it('can generate a single-key stealth address', function () { - var receiver = bitcoin.ECKey.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') + var receiver = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') // XXX: ephemeral, must be random (and secret to sender) to preserve privacy - var sender = bitcoin.ECKey.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') + var sender = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') - var G = bitcoin.ECKey.curve.G + var G = bitcoin.ECPair.curve.G var d = receiver.d // secret (receiver only) - var Q = receiver.pub.Q // shared + var Q = receiver.Q // shared var e = sender.d // secret (sender only) - var P = sender.pub.Q // shared + var P = sender.Q // shared // derived shared secret var eQ = Q.multiply(e) // sender @@ -35,7 +35,7 @@ describe('bitcoinjs-lib (crypto)', function () { assert.deepEqual(QprimeR.getEncoded(), QprimeS.getEncoded()) // derived shared-secret address - var address = new bitcoin.ECPubKey(QprimeS).getAddress().toString() + var address = new bitcoin.ECPair(null, QprimeS).getAddress().toString() assert.equal(address, '1EwCNJNZM5q58YPPTnjR1H5BvYRNeyZi47') }) @@ -45,13 +45,14 @@ describe('bitcoinjs-lib (crypto)', function () { it("can recover a parent private key from the parent's public key and a derived non-hardened child private key", function () { function recoverParent (master, child) { - assert(!master.privKey, 'You already have the parent private key') - assert(child.privKey, 'Missing child private key') + assert(!master.keyPair.d, 'You already have the parent private key') + assert(child.keyPair.d, 'Missing child private key') - var curve = bitcoin.ECKey.curve - var QP = master.pubKey.toBuffer() - var QP64 = QP.toString('base64') - var d1 = child.privKey.d + var curve = bitcoin.ECPair.curve + var QP = master.keyPair.Q + var serQP = master.keyPair.getPublicKeyBuffer() + + var d1 = child.keyPair.d var d2 var indexBuffer = new Buffer(4) @@ -60,7 +61,7 @@ describe('bitcoinjs-lib (crypto)', function () { indexBuffer.writeUInt32BE(i, 0) // calculate I - var data = Buffer.concat([QP, indexBuffer]) + var data = Buffer.concat([serQP, indexBuffer]) var I = crypto.createHmac('sha512', master.chainCode).update(data).digest() var IL = I.slice(0, 32) var pIL = bigi.fromBuffer(IL) @@ -68,11 +69,11 @@ describe('bitcoinjs-lib (crypto)', function () { // See hdnode.js:273 to understand d2 = d1.subtract(pIL).mod(curve.n) - var Qp = new bitcoin.ECKey(d2, true).pub.toBuffer() - if (Qp.toString('base64') === QP64) break + var Qp = new bitcoin.ECPair(d2).Q + if (Qp.equals(QP)) break } - var node = new bitcoin.HDNode(d2, master.chainCode, master.network) + var node = new bitcoin.HDNode(new bitcoin.ECPair(d2), master.chainCode, master.network) node.depth = master.depth node.index = master.index node.masterFingerprint = master.masterFingerprint @@ -133,7 +134,7 @@ describe('bitcoinjs-lib (crypto)', function () { var prevOutScript = prevOut.outs[prevVout].script var scriptSignature = bitcoin.ECSignature.parseScriptSignature(script.chunks[0]) - var publicKey = bitcoin.ECPubKey.fromBuffer(script.chunks[1]) + var publicKey = bitcoin.ECPair.fromPublicKeyBuffer(script.chunks[1]) var m = transaction.hashForSignature(input.vout, prevOutScript, scriptSignature.hashType) assert(publicKey.verify(m, scriptSignature.signature), 'Invalid m') @@ -151,7 +152,8 @@ describe('bitcoinjs-lib (crypto)', function () { async.parallel(tasks, function (err) { if (err) throw err - var n = bitcoin.ECKey.curve.n + + var n = bitcoin.ECPair.curve.n for (var i = 0; i < inputs.length; ++i) { for (var j = i + 1; j < inputs.length; ++j) { diff --git a/test/integration/multisig.js b/test/integration/multisig.js index fabaad998..1392d95c8 100644 --- a/test/integration/multisig.js +++ b/test/integration/multisig.js @@ -10,27 +10,29 @@ describe('bitcoinjs-lib (multisig)', function () { '026477115981fe981a6918a6297d9803c4dc04f328f22041bedff886bbc2962e01', '02c96db2302d19b43d4c69368babace7854cc84eb9e061cde51cfa77ca4a22b8b9', '03c6103b3b83e4a24a0e33a4df246ef11772f9992663db0c35759a5e2ebf68d8e9' - ].map(bitcoin.ECPubKey.fromHex) + ].map(function (hex) { + return new Buffer(hex, 'hex') + }) var redeemScript = bitcoin.scripts.multisigOutput(2, pubKeys) // 2 of 3 var scriptPubKey = bitcoin.scripts.scriptHashOutput(redeemScript.getHash()) - var address = bitcoin.Address.fromOutputScript(scriptPubKey).toString() + var address = bitcoin.Address.fromOutputScript(scriptPubKey) assert.equal(address, '36NUkt6FWUi3LAWBqWRdDmdTWbt91Yvfu7') }) - it('can spend from a 2-of-2 multsig P2SH address', function (done) { + it('can spend from a 2-of-4 multsig P2SH address', function (done) { this.timeout(20000) - var privKeys = [ + var keyPairs = [ '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx', - '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT' - ].map(bitcoin.ECKey.fromWIF) - var pubKeys = privKeys.map(function (x) { - return x.pub - }) + '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT', + '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', + '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx9rcrL7' + ].map(bitcoin.ECPair.fromWIF) + var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() }) - var redeemScript = bitcoin.scripts.multisigOutput(2, pubKeys) // 2 of 2 + var redeemScript = bitcoin.scripts.multisigOutput(2, pubKeys) // 2 of 4 var scriptPubKey = bitcoin.scripts.scriptHashOutput(redeemScript.getHash()) var address = bitcoin.Address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet).toString() @@ -42,7 +44,7 @@ describe('bitcoinjs-lib (multisig)', function () { blockchain.addresses.unspents(address, function (err, unspents) { if (err) return done(err) - // filter small unspents + // filter small unspents unspents = unspents.filter(function (unspent) { return unspent.value > 1e4 }) @@ -51,16 +53,17 @@ describe('bitcoinjs-lib (multisig)', function () { var unspent = unspents.pop() // make a random destination address - var targetAddress = bitcoin.ECKey.makeRandom().pub.getAddress(bitcoin.networks.testnet).toString() + var targetAddress = bitcoin.ECPair.makeRandom({ + network: bitcoin.networks.testnet + }).getAddress().toString() var txb = new bitcoin.TransactionBuilder() txb.addInput(unspent.txId, unspent.vout) txb.addOutput(targetAddress, 1e4) - // sign w/ each private key - privKeys.forEach(function (privKey) { - txb.sign(0, privKey, redeemScript) - }) + // sign with 1st and 3rd key + txb.sign(0, keyPairs[0], redeemScript) + txb.sign(0, keyPairs[2], redeemScript) // broadcast our transaction blockchain.transactions.propagate(txb.build().toHex(), function (err) { diff --git a/test/message.js b/test/message.js index e982a4533..28d69532b 100644 --- a/test/message.js +++ b/test/message.js @@ -6,7 +6,7 @@ var networks = require('../src/networks') var Address = require('../src/address') var BigInteger = require('bigi') -var ECKey = require('../src/eckey') +var ECPair = require('../src/ecpair') var fixtures = require('./fixtures/message.json') @@ -55,12 +55,14 @@ describe('message', function () { it(f.description, function () { var network = networks[f.network] - var privKey = new ECKey(new BigInteger(f.d), false) - var signature = message.sign(privKey, f.message, network) + var keyPair = new ECPair(new BigInteger(f.d), null, { + compressed: false + }) + var signature = message.sign(keyPair, f.message, network) assert.equal(signature.toString('base64'), f.signature) if (f.compressed) { - var compressedPrivKey = new ECKey(new BigInteger(f.d)) + var compressedPrivKey = new ECPair(new BigInteger(f.d)) var compressedSignature = message.sign(compressedPrivKey, f.message) assert.equal(compressedSignature.toString('base64'), f.compressed.signature) diff --git a/test/network.js b/test/network.js deleted file mode 100644 index aef09e0e2..000000000 --- a/test/network.js +++ /dev/null @@ -1,54 +0,0 @@ -/* global describe, it, before, after */ - -var assert = require('assert') -var networks = require('../src/networks') -var sinon = require('sinon') - -var HDNode = require('../src/hdnode') -var Transaction = require('../src/transaction') - -var fixtures = require('./fixtures/network') - -describe('networks', function () { - var txToBuffer - before(function () { - txToBuffer = sinon.stub(Transaction.prototype, 'toBuffer') - }) - - after(function () { - Transaction.prototype.toBuffer.restore() - }) - - describe('constants', function () { - fixtures.valid.constants.forEach(function (f) { - var network = networks[f.network] - - Object.keys(f.bip32).forEach(function (name) { - var extb58 = f.bip32[name] - - it('resolves ' + extb58 + ' to ' + f.network, function () { - assert.equal(HDNode.fromBase58(extb58, network).network, network) - }) - }) - }) - }) - - describe('estimateFee', function () { - fixtures.valid.estimateFee.forEach(function (f) { - describe('(' + f.network + ')', function () { - var network = networks[f.network] - - it('calculates the fee correctly for ' + f.description, function () { - var buffer = new Buffer(f.txSize) - txToBuffer.returns(buffer) - - var estimateFee = network.estimateFee - var tx = new Transaction() - tx.outs = f.outputs || [] - - assert.equal(estimateFee(tx), f.fee) - }) - }) - }) - }) -}) diff --git a/test/networks.js b/test/networks.js new file mode 100644 index 000000000..9a1ff4861 --- /dev/null +++ b/test/networks.js @@ -0,0 +1,22 @@ +/* global describe, it */ + +var assert = require('assert') +var networks = require('../src/networks') + +var HDNode = require('../src/hdnode') + +var fixtures = require('./fixtures/network') + +describe('networks', function () { + fixtures.forEach(function (f) { + var network = networks[f.network] + + Object.keys(f.bip32).forEach(function (name) { + var extb58 = f.bip32[name] + + it(extb58 + ' auto-detects ' + f.network, function () { + assert.equal(HDNode.fromBase58(extb58).keyPair.network, network) + }) + }) + }) +}) diff --git a/test/script.js b/test/script.js index 7017522e4..a20220633 100644 --- a/test/script.js +++ b/test/script.js @@ -28,8 +28,13 @@ describe('Script', function () { describe('fromASM/toASM', function () { fixtures.valid.forEach(function (f) { + if (!f.asm) return + it('decodes/encodes ' + f.description, function () { - assert.equal(Script.fromASM(f.asm).toASM(), f.asm) + var script = Script.fromASM(f.asm) + + assert.equal(script.toASM(), f.asm) + assert.equal(script.toHex(), f.hex) }) }) }) @@ -37,14 +42,17 @@ describe('Script', function () { describe('fromHex/toHex', function () { fixtures.valid.forEach(function (f) { it('decodes/encodes ' + f.description, function () { - assert.equal(Script.fromHex(f.hex).toHex(), f.hex) + var script = Script.fromHex(f.hex) + + assert.equal(script.toASM(), f.asm) + assert.equal(script.toHex(), f.hex) }) }) }) describe('getHash', function () { fixtures.valid.forEach(function (f) { - it('produces a HASH160 of "' + f.asm + '"', function () { + it('produces a HASH160 of ' + f.description, function () { var script = Script.fromHex(f.hex) assert.equal(script.getHash().toString('hex'), f.hash) diff --git a/test/scripts.js b/test/scripts.js index ccfee0b13..a9e3bb489 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -4,7 +4,7 @@ var assert = require('assert') var ops = require('../src/opcodes') var scripts = require('../src/scripts') -var ECPubKey = require('../src/ecpubkey') +var ECPair = require('../src/ecpair') var Script = require('../src/script') var fixtures = require('./fixtures/scripts.json') @@ -64,9 +64,15 @@ describe('Scripts', function () { var expected = type.toLowerCase() === f.type if (inputFn && f.scriptSig) { - it('returns ' + expected + ' for ' + f.scriptSig, function () { - var script = Script.fromASM(f.scriptSig) + var script + + if (f.scriptSig) { + script = Script.fromASM(f.scriptSig) + } else { + script = Script.fromHex(f.scriptSigHex) + } + it('returns ' + expected + ' for ' + f.scriptSig, function () { assert.equal(inputFn(script), expected) }) @@ -74,8 +80,6 @@ describe('Scripts', function () { var expectedIncomplete = type.toLowerCase() === f.typeIncomplete it('returns ' + expected + ' for ' + f.scriptSig, function () { - var script = Script.fromASM(f.scriptSig) - assert.equal(inputFn(script, true), expectedIncomplete) }) } @@ -85,9 +89,15 @@ describe('Scripts', function () { if (!(inputFnName in fixtures.invalid)) return fixtures.invalid[inputFnName].forEach(function (f) { - if (inputFn && f.scriptSig) { - it('returns false for ' + f.scriptSig, function () { - var script = Script.fromASM(f.scriptSig) + if (inputFn && (f.scriptSig || f.scriptSigHex)) { + it('returns false for ' + f.description + ' (' + (f.scriptSig || f.scriptSigHex) + ')', function () { + var script + + if (f.scriptSig) { + script = Script.fromASM(f.scriptSig) + } else { + script = Script.fromHex(f.scriptSigHex) + } assert.equal(inputFn(script), false) }) @@ -112,7 +122,7 @@ describe('Scripts', function () { fixtures.invalid[outputFnName].forEach(function (f) { if (outputFn && f.scriptPubKey) { - it('returns false for ' + f.scriptPubKey, function () { + it('returns false for ' + f.description + ' (' + f.scriptPubKey + ')', function () { var script = Script.fromASM(f.scriptPubKey) assert.equal(outputFn(script), false) @@ -140,8 +150,7 @@ describe('Scripts', function () { if (f.type !== 'pubkey') return it('returns ' + f.scriptPubKey, function () { - var pubKey = ECPubKey.fromHex(f.pubKey) - + var pubKey = new Buffer(f.pubKey, 'hex') var scriptPubKey = scripts.pubKeyOutput(pubKey) assert.equal(scriptPubKey.toASM(), f.scriptPubKey) }) @@ -152,7 +161,7 @@ describe('Scripts', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkeyhash') return - var pubKey = ECPubKey.fromHex(f.pubKey) + var pubKey = new Buffer(f.pubKey, 'hex') it('returns ' + f.scriptSig, function () { var signature = new Buffer(f.signature, 'hex') @@ -167,8 +176,8 @@ describe('Scripts', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'pubkeyhash') return - var pubKey = ECPubKey.fromHex(f.pubKey) - var address = pubKey.getAddress() + var pubKey = new Buffer(f.pubKey, 'hex') + var address = ECPair.fromPublicKeyBuffer(pubKey).getAddress() it('returns ' + f.scriptPubKey, function () { var scriptPubKey = scripts.pubKeyHashOutput(address.hash) @@ -192,8 +201,7 @@ describe('Scripts', function () { }) fixtures.invalid.multisigInput.forEach(function (f) { - var pubKeys = f.pubKeys.map(ECPubKey.fromHex) - var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) + var scriptPubKey = Script.fromASM(f.scriptPubKey) it('throws on ' + f.exception, function () { var signatures = f.signatures.map(function (signature) { @@ -211,7 +219,7 @@ describe('Scripts', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'multisig') return - var pubKeys = f.pubKeys.map(ECPubKey.fromHex) + var pubKeys = f.pubKeys.map(function (p) { return new Buffer(p, 'hex') }) var scriptPubKey = scripts.multisigOutput(pubKeys.length, pubKeys) it('returns ' + f.scriptPubKey, function () { @@ -220,7 +228,9 @@ describe('Scripts', function () { }) fixtures.invalid.multisigOutput.forEach(function (f) { - var pubKeys = f.pubKeys.map(ECPubKey.fromHex) + var pubKeys = f.pubKeys.map(function (p) { + return new Buffer(p, 'hex') + }) it('throws on ' + f.exception, function () { assert.throws(function () { @@ -240,7 +250,11 @@ describe('Scripts', function () { it('returns ' + f.scriptSig, function () { var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript) - assert.equal(scriptSig.toASM(), f.scriptSig) + if (f.scriptSig) { + assert.equal(scriptSig.toASM(), f.scriptSig) + } else { + assert.equal(scriptSig.toHex(), f.scriptSigHex) + } }) }) }) @@ -248,10 +262,10 @@ describe('Scripts', function () { describe('scriptHashOutput', function () { fixtures.valid.forEach(function (f) { if (f.type !== 'scripthash') return - - var redeemScript = Script.fromASM(f.redeemScript) + if (!f.scriptPubKey) return it('returns ' + f.scriptPubKey, function () { + var redeemScript = Script.fromASM(f.redeemScript) var scriptPubKey = scripts.scriptHashOutput(redeemScript.getHash()) assert.equal(scriptPubKey.toASM(), f.scriptPubKey) diff --git a/test/transaction.js b/test/transaction.js index 4a64493f1..f177318aa 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -18,7 +18,8 @@ describe('Transaction', function () { var script if (txIn.data) { - script = new Script(new Buffer(txIn.data, 'hex'), []) + var data = new Buffer(txIn.data, 'hex') + script = new Script(data, []) } else if (txIn.script) { script = Script.fromASM(txIn.script) } @@ -27,7 +28,16 @@ describe('Transaction', function () { }) raw.outs.forEach(function (txOut) { - tx.addOutput(Script.fromASM(txOut.script), txOut.value) + var script + + if (txOut.data) { + var data = new Buffer(txOut.data, 'hex') + script = new Script(data, []) + } else if (txOut.script) { + script = Script.fromASM(txOut.script) + } + + tx.addOutput(script, txOut.value) }) return tx @@ -35,10 +45,10 @@ describe('Transaction', function () { describe('fromBuffer/fromHex', function () { fixtures.valid.forEach(function (f) { - it('imports ' + f.id + ' correctly', function () { + it('imports ' + f.description + ' (' + f.id + ')', function () { var actual = Transaction.fromHex(f.hex) - assert.deepEqual(actual.toHex(), f.hex) + assert.equal(actual.toHex(), f.hex, actual.toHex()) }) }) @@ -53,10 +63,10 @@ describe('Transaction', function () { describe('toBuffer/toHex', function () { fixtures.valid.forEach(function (f) { - it('exports ' + f.id + ' correctly', function () { + it('exports ' + f.description + ' (' + f.id + ')', function () { var actual = fromRaw(f.raw) - assert.deepEqual(actual.toHex(), f.hex) + assert.equal(actual.toHex(), f.hex, actual.toHex()) }) }) }) @@ -108,19 +118,10 @@ describe('Transaction', function () { }) describe('addOutput', function () { - fixtures.valid.forEach(function (f) { - it('should add the outputs for ' + f.id + ' correctly', function () { - var tx = new Transaction() - - f.raw.outs.forEach(function (txOut, i) { - var scriptPubKey = Script.fromASM(txOut.script) - var j = tx.addOutput(scriptPubKey, txOut.value) - - assert.equal(i, j) - assert.equal(tx.outs[i].script, scriptPubKey) - assert.equal(tx.outs[i].value, txOut.value) - }) - }) + it('returns an index', function () { + var tx = new Transaction() + assert.equal(tx.addOutput(Script.EMPTY, 0), 0) + assert.equal(tx.addOutput(Script.EMPTY, 0), 1) }) }) @@ -158,7 +159,7 @@ describe('Transaction', function () { it('should return the hash for ' + f.id, function () { var tx = Transaction.fromHex(f.hex) - assert.deepEqual(tx.getHash().toString('hex'), f.hash) + assert.equal(tx.getHash().toString('hex'), f.hash) }) }) }) diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 350011be2..4b9f25589 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -1,10 +1,12 @@ /* global describe, it, beforeEach */ var assert = require('assert') +var ops = require('../src/opcodes') +var scripts = require('../src/scripts') var Address = require('../src/address') var BigInteger = require('bigi') -var ECKey = require('../src/eckey') +var ECPair = require('../src/ecpair') var Script = require('../src/script') var Transaction = require('../src/transaction') var TransactionBuilder = require('../src/transaction_builder') @@ -31,14 +33,14 @@ function construct (txb, f, sign) { if (sign === undefined || sign) { f.inputs.forEach(function (input, index) { input.signs.forEach(function (sign) { - var privKey = ECKey.fromWIF(sign.privKey) + var keyPair = ECPair.fromWIF(sign.keyPair) var redeemScript if (sign.redeemScript) { redeemScript = Script.fromASM(sign.redeemScript) } - txb.sign(index, privKey, redeemScript, sign.hashType) + txb.sign(index, keyPair, redeemScript, sign.hashType) }) }) } @@ -56,7 +58,7 @@ function construct (txb, f, sign) { describe('TransactionBuilder', function () { var privAddress, privScript var prevTx, prevTxHash - var privKey + var keyPair var txb beforeEach(function () { @@ -67,8 +69,8 @@ describe('TransactionBuilder', function () { prevTx.addOutput(Address.fromBase58Check('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP').toOutputScript(), 1) prevTxHash = prevTx.getHash() - privKey = new ECKey(BigInteger.ONE, false) - privAddress = privKey.pub.getAddress() + keyPair = new ECPair(BigInteger.ONE) + privAddress = keyPair.getAddress() privScript = privAddress.toOutputScript() }) @@ -113,7 +115,7 @@ describe('TransactionBuilder', function () { it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', function () { txb.addInput(prevTxHash, 0) - txb.sign(0, privKey) + txb.sign(0, keyPair) assert.throws(function () { txb.addInput(prevTxHash, 0) @@ -152,7 +154,7 @@ describe('TransactionBuilder', function () { it('throws if SIGHASH_ALL has been used to sign any existing scriptSigs', function () { txb.addInput(prevTxHash, 0) txb.addOutput(privScript, 2000) - txb.sign(0, privKey) + txb.sign(0, keyPair) assert.throws(function () { txb.addOutput(privScript, 9000) @@ -167,7 +169,7 @@ describe('TransactionBuilder', function () { f.inputs.forEach(function (input, index) { input.signs.forEach(function (sign) { - var privKey = ECKey.fromWIF(sign.privKey) + var keyPair = ECPair.fromWIF(sign.keyPair) var redeemScript if (sign.redeemScript) { @@ -175,10 +177,11 @@ describe('TransactionBuilder', function () { } if (!sign.throws) { - txb.sign(index, privKey, redeemScript, sign.hashType) + txb.sign(index, keyPair, redeemScript, sign.hashType) + } else { assert.throws(function () { - txb.sign(index, privKey, redeemScript, sign.hashType) + txb.sign(index, keyPair, redeemScript, sign.hashType) }, new RegExp(f.exception)) } }) @@ -222,6 +225,55 @@ describe('TransactionBuilder', function () { }) }) + describe('multisig', function () { + fixtures.valid.multisig.forEach(function (f) { + it(f.description, function () { + construct(txb, f, false) + + var tx + + f.inputs.forEach(function (input, i) { + var redeemScript = Script.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 + + // ignore OP_0 on the front, ignore redeemScript + var signatures = scriptSig.chunks.slice(1, -1).filter(function (x) { return x !== ops.OP_0 }) + + // rebuild/replace the scriptSig without them + var replacement = scripts.scriptHashInput(scripts.multisigInput(signatures), redeemScript) + assert.equal(replacement.toASM(), sign.scriptSigFiltered) + sign.scriptSigFiltered = replacement.toASM() + + tx.ins[i].script = replacement + } + + // now import it + txb = TransactionBuilder.fromTransaction(tx) + } + + var keyPair = ECPair.fromWIF(sign.keyPair) + txb.sign(i, keyPair, redeemScript, sign.hashType) + + // update the tx + tx = txb.buildIncomplete() + + // now verify the serialized scriptSig is as expected + assert.equal(tx.ins[i].script.toASM(), sign.scriptSig) + }) + }) + + tx = txb.build() + assert.equal(tx.toHex(), f.txHex) + }) + }) + }) + describe('fromTransaction', function () { fixtures.valid.build.forEach(function (f) { it('builds the correct TransactionBuilder for ' + f.description, function () { @@ -241,30 +293,5 @@ describe('TransactionBuilder', function () { }, new RegExp(f.exception)) }) }) - - it('works for the out-of-order P2SH multisig case', function () { - var privKeys = [ - '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT', - '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx' - ].map(ECKey.fromWIF) - var redeemScript = Script.fromASM('OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG') - - txb.addInput('4971f016798a167331bcbc67248313fbc444c6e92e4416efd06964425588f5cf', 0) - txb.addOutput('1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', 10000) - txb.sign(0, privKeys[0], redeemScript) - - var tx = txb.buildIncomplete() - - // in another galaxy... - // ... far, far away - var txb2 = TransactionBuilder.fromTransaction(tx) - - // [you should] verify that Transaction is what you want... - // ... then sign it - txb2.sign(0, privKeys[1], redeemScript) - var tx2 = txb2.build() - - assert.equal(tx2.toHex(), '0100000001cff58855426469d0ef16442ee9c644c4fb13832467bcbc3173168a7916f0714900000000fd1c01004830450221009c92c1ae1767ac04e424da7f6db045d979b08cde86b1ddba48621d59a109d818022004f5bb21ad72255177270abaeb2d7940ac18f1e5ca1f53db4f3fd1045647a8a8014830450221009418caa5bc18da87b188a180125c0cf06dce6092f75b2d3c01a29493466800fd02206ead65e7ca6e0f17eefe6f78457c084eab59af7c9882be1437de2e7116358eb9014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0110270000000000001976a914751e76e8199196d454941c45d1b3a323f1433bd688ac00000000') - }) }) })