From 7559ee880d6945eb7b3b554543e0ae66314b315d Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 17 Oct 2014 13:31:01 +1100 Subject: [PATCH 1/4] add ECPair module --- src/ecpair.js | 142 +++++++++++++++++++++++ test/ecpair.js | 234 ++++++++++++++++++++++++++++++++++++++ test/fixtures/ecpair.json | 102 +++++++++++++++++ 3 files changed, 478 insertions(+) create mode 100644 src/ecpair.js create mode 100644 test/ecpair.js create mode 100644 test/fixtures/ecpair.json diff --git a/src/ecpair.js b/src/ecpair.js new file mode 100644 index 0000000..b4ecf48 --- /dev/null +++ b/src/ecpair.js @@ -0,0 +1,142 @@ +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') + Q = ECPair.curve.G.multiply(d) + + // enforce Q is a public key if no private key given + } else { + typeForce('Point', Q) + } + + this.compressed = compressed + this.d = d + this.Q = Q + this.network = network +} + +// 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/test/ecpair.js b/test/ecpair.js new file mode 100644 index 0000000..9135284 --- /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/fixtures/ecpair.json b/test/fixtures/ecpair.json new file mode 100644 index 0000000..bc117e6 --- /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" + } + ] + } +} From 31832293ddb92f4b61e82e0756a16d6bcdb60ac8 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 19 Mar 2015 13:25:41 +1100 Subject: [PATCH 2/4] ECPair: lazily calculate Q --- src/ecpair.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index b4ecf48..7f340b4 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -34,19 +34,28 @@ function ECPair (d, Q, options) { assert(d.compareTo(ECPair.curve.n) < 0, 'Private key must be less than the curve order') assert(!Q, 'Unexpected publicKey parameter') - Q = ECPair.curve.G.multiply(d) + 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.d = d - this.Q = Q 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') From 4c8b0f38eae6f4bb9f63fb9e77ee54964a41d5a3 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Mon, 2 Mar 2015 16:48:36 +1100 Subject: [PATCH 3/4] replace ECKey/ECPubKey with ECPair --- src/hdnode.js | 91 ++++++++-------- src/index.js | 3 +- src/message.js | 17 +-- src/scripts.js | 15 ++- src/transaction_builder.js | 41 +++---- test/bitcoin.core.js | 27 +++-- test/eckey.js | 143 ------------------------- test/ecpubkey.js | 120 --------------------- test/fixtures/eckey.json | 102 ------------------ test/fixtures/ecpubkey.json | 19 ---- test/fixtures/transaction_builder.json | 94 ++++++++-------- test/hdnode.js | 115 +++++++------------- test/integration/advanced.js | 12 ++- test/integration/basic.js | 19 ++-- test/integration/crypto.js | 38 +++---- test/integration/multisig.js | 24 +++-- test/message.js | 10 +- test/networks.js | 2 +- test/scripts.js | 17 +-- test/transaction_builder.js | 27 ++--- 20 files changed, 266 insertions(+), 670 deletions(-) delete mode 100644 test/eckey.js delete mode 100644 test/ecpubkey.js delete mode 100644 test/fixtures/eckey.json delete mode 100644 test/fixtures/ecpubkey.json diff --git a/src/hdnode.js b/src/hdnode.js index 9152e1d..49f5ae4 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -6,8 +6,7 @@ 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') @@ -67,10 +53,13 @@ HDNode.fromSeedBuffer = function (seed, network) { 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) + // 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,7 +220,7 @@ 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 ]) } @@ -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 3b5317b..2819bd2 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 a210c76..f3791fe 100644 --- a/src/message.js +++ b/src/message.js @@ -4,7 +4,7 @@ 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') @@ -19,15 +19,15 @@ function magicHash (message, network) { 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/scripts.js b/src/scripts.js index 63d3502..fde409f 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -170,7 +170,7 @@ function classifyInput (script, allowIncomplete) { // {pubKey} OP_CHECKSIG function pubKeyOutput (pubKey) { return Script.fromChunks([ - pubKey.toBuffer(), + pubKey, ops.OP_CHECKSIG ]) } @@ -201,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 )) @@ -228,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} diff --git a/src/transaction_builder.js b/src/transaction_builder.js index f4d9923..a08af72 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -1,9 +1,10 @@ var assert = require('assert') +var bufferutils = require('./bufferutils') 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 +34,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 +47,7 @@ function extractInput (txIn) { signatures = [parsed.signature] if (redeemScript) { - pubKeys = [ECPubKey.fromBuffer(redeemScript.chunks[0])] + pubKeys = redeemScript.chunks.slice(0, 1) } break @@ -63,7 +64,7 @@ function extractInput (txIn) { }) if (redeemScript) { - pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer) + pubKeys = redeemScript.chunks.slice(1, -2) } break @@ -141,12 +142,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 } } @@ -280,7 +281,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 @@ -292,6 +293,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 @@ -319,21 +322,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 } } @@ -357,9 +360,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 } } @@ -378,10 +381,11 @@ TransactionBuilder.prototype.sign = function (index, privKey, redeemScript, hash 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 (!pubKey.verify(signatureHash, signature)) return false + if (!keyPair2.verify(signatureHash, signature)) return false match = signature // remove matched signature from unmatched @@ -396,14 +400,15 @@ TransactionBuilder.prototype.sign = function (index, privKey, redeemScript, hash // enforce in order signing of public keys assert(input.pubKeys.some(function (pubKey, i) { - if (!privKey.pub.Q.equals(pubKey.Q)) return false + if (!bufferutils.equal(kpPubKey, pubKey)) return false assert(!input.signatures[i], 'Signature already exists') - var signature = privKey.sign(signatureHash) + + var signature = keyPair.sign(signatureHash) input.signatures[i] = signature return true - }, this), 'privateKey cannot sign for this input') + }, this), 'key pair cannot sign for this input') } module.exports = TransactionBuilder diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index 3306665..bce7741 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -2,12 +2,11 @@ var assert = require('assert') var base58 = require('bs58') -var base58check = require('bs58check') var Bitcoin = require('../') var Address = Bitcoin.Address var Block = Bitcoin.Block -var ECKey = Bitcoin.ECKey +var ECPair = Bitcoin.ECPair var ECSignature = Bitcoin.ECSignature var Transaction = Bitcoin.Transaction var Script = Bitcoin.Script @@ -95,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) { @@ -128,11 +126,10 @@ 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.notEqual(allowedNetworks.indexOf(version), -1, 'Invalid network') - }, /Invalid (checksum|compression flag|network|WIF payload)/) + assert(allowedNetworks.indexOf(keyPair.network) > -1, 'Invalid network') + }, /(Invalid|Unknown) (checksum|compression flag|network|WIF payload)/) }) }) }) diff --git a/test/eckey.js b/test/eckey.js deleted file mode 100644 index 0e56fb5..0000000 --- a/test/eckey.js +++ /dev/null @@ -1,143 +0,0 @@ -/* global describe, it */ -/* eslint-disable no-new */ - -var assert = require('assert') -var ecurve = require('ecurve') -var networks = require('../src/networks') -var proxyquire = require('proxyquire') -var randomBytes = require('randombytes') - -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) - - it("uses the RNG provided by the 'randombytes' module by default", function () { - var stub = { randombytes: function () { return exBuffer } } - var ProxiedECKey = proxyquire('../src/eckey', stub) - - var privKey = ProxiedECKey.makeRandom() - - assert.equal(privKey.toWIF(), exWIF) - }) - - 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) - }) - - it('supports compression', function () { - assert.equal(ECKey.makeRandom(true).pub.compressed, true) - assert.equal(ECKey.makeRandom(false).pub.compressed, false) - }) - }) - - describe('signing', function () { - var hash = 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/ecpubkey.js b/test/ecpubkey.js deleted file mode 100644 index 76943ed..0000000 --- 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/eckey.json b/test/fixtures/eckey.json deleted file mode 100644 index 7d1c4a4..0000000 --- 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/ecpubkey.json b/test/fixtures/ecpubkey.json deleted file mode 100644 index 87b8aa2..0000000 --- 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/transaction_builder.json b/test/fixtures/transaction_builder.json index b3f91ce..9fd03f3 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" } ] } @@ -53,7 +53,7 @@ "vout": 0, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_DUP OP_HASH160 751e76e8199196d454941c45d1b3a323f1433bd6 OP_EQUALVERIFY OP_CHECKSIG" } ] @@ -75,11 +75,11 @@ "vout": 0, "signs": [ { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG" }, { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" } ] } @@ -101,10 +101,10 @@ "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "signs": [ { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" }, { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" } ] } @@ -126,10 +126,10 @@ "prevTxScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a OP_2 OP_CHECKMULTISIG", "signs": [ { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT" }, { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx" } ] } @@ -150,11 +150,11 @@ "vout": 0, "signs": [ { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "redeemScript": "OP_2 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a 04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672 OP_3 OP_CHECKMULTISIG" }, { - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe" + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe" } ] } @@ -176,7 +176,7 @@ "prevTxScript": "OP_HASH160 e89677d91455e541630d62c63718bef738b478b1 OP_EQUAL", "signs": [ { - "privKey": "KxLDMPtVM7sLSu2v5n1LybDibw6P9FFbL4pUwJ51UDm7rp5AmXWW", + "keyPair": "KxLDMPtVM7sLSu2v5n1LybDibw6P9FFbL4pUwJ51UDm7rp5AmXWW", "redeemScript": "033e29aea1168a835d5e386c292082db7b7807172a10ec634ad34226f36d79e70f OP_CHECKSIG" } ] @@ -198,7 +198,7 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] } @@ -222,7 +222,7 @@ "sequence": 2147001, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] } @@ -246,12 +246,12 @@ "signs": [ { "pubKeyIndex": 0, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" }, { "pubKeyIndex": 1, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" } ] @@ -275,12 +275,12 @@ "signs": [ { "pubKeyIndex": 1, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "scriptSig": "OP_0 OP_0 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" }, { "pubKeyIndex": 0, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" } ] @@ -304,12 +304,12 @@ "signs": [ { "pubKeyIndex": 0, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" }, { "pubKeyIndex": 1, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "scriptSig": "OP_0 3045022100e37e33a4fe5fccfb87afb0e951e83fcea4820d73b327d21edc1adec3b916c437022061c5786908b674e323a1863cc2feeb60e1679f1892c10ee08ac21e51fd50ba9001 3045022100aa0c323bc639d3d71591be98ccaf7b8cb8c86aa95f060aef5e36fc3f04c4d029022010e2b18de17e307a12ae7e0e88518fe814f18a0ca1ee4510ba23a65628b0657601 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52ae" } ] @@ -333,12 +333,12 @@ "signs": [ { "pubKeyIndex": 0, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { "pubKeyIndex": 1, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100ae06d8269b79b5cfa0297d1d88261b0061e52fc2814948c3aa05fa78ee76894302206e0c79a5c90569d8c72a542ef9a06471cbbcd2c651b312339983dfba4f8ff46301 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" } ] @@ -362,12 +362,12 @@ "signs": [ { "pubKeyIndex": 0, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { "pubKeyIndex": 2, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" } ] @@ -391,12 +391,12 @@ "signs": [ { "pubKeyIndex": 2, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", "scriptSig": "OP_0 OP_0 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { "pubKeyIndex": 0, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" } ] @@ -420,12 +420,12 @@ "signs": [ { "pubKeyIndex": 0, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 OP_0 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { "pubKeyIndex": 2, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" } ] @@ -449,13 +449,13 @@ "signs": [ { "pubKeyIndex": 2, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe", "scriptSig": "OP_0 OP_0 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" }, { "filterOP_0": true, "pubKeyIndex": 0, - "privKey": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", + "keyPair": "91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx", "scriptSig": "OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae", "scriptSigFiltered": "OP_0 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae" } @@ -522,7 +522,7 @@ "vout": 0, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" } ] }, @@ -556,10 +556,10 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" }, { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "throws": true } ] @@ -580,7 +580,7 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_RETURN 06deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474", "throws": true } @@ -603,7 +603,7 @@ "prevTxScript": "OP_HASH160 7f67f0521934a57d3039f77f9f32cf313f3ac74b OP_EQUAL", "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "throws": true } ] @@ -625,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 } ] @@ -651,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 } @@ -677,7 +677,7 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", "redeemScript": "OP_HASH160 7f67f0521934a57d3039f77f9f32cf313f3ac74b OP_EQUAL", "throws": true } @@ -700,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 } @@ -723,11 +723,11 @@ "vout": 1, "signs": [ { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf", "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG" }, { - "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", + "keyPair": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf", "throws": true } ] @@ -741,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 } @@ -772,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 1cbfd7c..46ff82b 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -3,96 +3,52 @@ 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) + var keyPair, chainCode - it('calculates the publicKey from a BigInteger', function () { - var hd = new HDNode(d, chainCode) + beforeEach(function () { + var d = BigInteger.ONE - 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/) - }) - - it('only uses compressed points', function () { - var hd = new HDNode(Q, chainCode) - var hdP = new HDNode(d, chainCode) - - 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) + it('throws on uncompressed keyPair', function () { + keyPair.compressed = false - assert.equal(hd.network, networks.bitcoin) - }) - - it('supports alternative networks', function () { - var hd = new HDNode(Q, chainCode, networks.testnet) - - 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) }) }) @@ -148,7 +104,7 @@ describe('HDNode', function () { var hd = HDNode.fromBase58(f.master.base58) assert.equal(hd.toBase58(), f.master.base58) - assert.equal(hd.network, network) + assert.equal(hd.keyPair.network, network) }) }) @@ -158,7 +114,7 @@ describe('HDNode', function () { var hd = HDNode.fromBase58(f.master.base58Priv, network) assert.equal(hd.toBase58(), f.master.base58Priv) - assert.equal(hd.network, network) + assert.equal(hd.keyPair.network, network) }) }) @@ -194,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 () { @@ -210,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) @@ -219,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) @@ -245,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 3d20228..45e52a7 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 7a76889..6814fbb 100644 --- a/test/integration/basic.js +++ b/test/integration/basic.js @@ -9,9 +9,9 @@ describe('bitcoinjs-lib (basic)', function () { // for testing only function rng () { return new Buffer('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz') } - // generate random key (custom rng for testing only) - var key = bitcoin.ECKey.makeRandom(undefined, rng) - 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') }) @@ -20,25 +20,26 @@ describe('bitcoinjs-lib (basic)', 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(key.pub.getAddress().toString(), '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') + assert.equal(address, '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') }) 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 6318092..8e69e44 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 32be5e2..1392d95 100644 --- a/test/integration/multisig.js +++ b/test/integration/multisig.js @@ -10,11 +10,13 @@ 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') }) @@ -22,15 +24,13 @@ describe('bitcoinjs-lib (multisig)', function () { it('can spend from a 2-of-4 multsig P2SH address', function (done) { this.timeout(20000) - var privKeys = [ + var keyPairs = [ '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgwmaKkrx', '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgww7vXtT', '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx9rcrL7' - ].map(bitcoin.ECKey.fromWIF) - var pubKeys = privKeys.map(function (x) { - return x.pub - }) + ].map(bitcoin.ECPair.fromWIF) + var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() }) var redeemScript = bitcoin.scripts.multisigOutput(2, pubKeys) // 2 of 4 var scriptPubKey = bitcoin.scripts.scriptHashOutput(redeemScript.getHash()) @@ -44,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 }) @@ -53,15 +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 with 1st and 3rd key - txb.sign(0, privKeys[0], redeemScript) - txb.sign(0, privKeys[2], redeemScript) + 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 e982a45..28d6953 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/networks.js b/test/networks.js index 3c9762d..9a1ff48 100644 --- a/test/networks.js +++ b/test/networks.js @@ -15,7 +15,7 @@ describe('networks', function () { var extb58 = f.bip32[name] it(extb58 + ' auto-detects ' + f.network, function () { - assert.equal(HDNode.fromBase58(extb58).network, network) + assert.equal(HDNode.fromBase58(extb58).keyPair.network, network) }) }) }) diff --git a/test/scripts.js b/test/scripts.js index 14421c8..a9e3bb4 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') @@ -150,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) }) @@ -162,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') @@ -177,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) @@ -220,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 () { @@ -229,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 () { diff --git a/test/transaction_builder.js b/test/transaction_builder.js index 651b255..4b9f255 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -6,7 +6,7 @@ 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') @@ -33,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) }) }) } @@ -58,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 () { @@ -69,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() }) @@ -115,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) @@ -154,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) @@ -169,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) { @@ -177,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)) } }) @@ -256,8 +257,8 @@ describe('TransactionBuilder', function () { txb = TransactionBuilder.fromTransaction(tx) } - var privKey = ECKey.fromWIF(sign.privKey) - txb.sign(i, privKey, redeemScript, sign.hashType) + var keyPair = ECPair.fromWIF(sign.keyPair) + txb.sign(i, keyPair, redeemScript, sign.hashType) // update the tx tx = txb.buildIncomplete() From e106d0273130fade4a66752b3e7d9a248709f6dd Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 19 Mar 2015 13:35:25 +1100 Subject: [PATCH 4/4] standard formatting fixes --- src/ecpair.js | 2 +- src/hdnode.js | 2 +- test/hdnode.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ecpair.js b/src/ecpair.js index 7f340b4..990c2dd 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -47,7 +47,7 @@ function ECPair (d, Q, options) { } Object.defineProperty(ECPair.prototype, 'Q', { - get: function() { + get: function () { if (!this.__Q && this.d) { this.__Q = ECPair.curve.G.multiply(this.d) } diff --git a/src/hdnode.js b/src/hdnode.js index 49f5ae4..6989b3e 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -99,7 +99,7 @@ HDNode.fromBase58 = function (string, network) { var chainCode = buffer.slice(13, 45) var data, keyPair - // 33 bytes: private key data (0x00 + k) + // 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) diff --git a/test/hdnode.js b/test/hdnode.js index 46ff82b..46833e8 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -1,4 +1,4 @@ -/* global describe, it */ +/* global describe, it, beforeEach */ /* eslint-disable no-new */ var assert = require('assert')