Merge pull request #1070 from bitcoinjs/tinyec

rm ecdsa, switch out ECPair secp256k1 impl.
This commit is contained in:
Daniel Cousens 2018-05-30 17:55:26 +10:00 committed by GitHub
commit d7eb6c8e77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 225 additions and 578 deletions

View file

@ -31,14 +31,12 @@
], ],
"dependencies": { "dependencies": {
"bech32": "^1.1.2", "bech32": "^1.1.2",
"bigi": "^1.4.0",
"bip32": "^0.1.0", "bip32": "^0.1.0",
"bip66": "^1.1.0", "bip66": "^1.1.0",
"bitcoin-ops": "^1.4.0", "bitcoin-ops": "^1.4.0",
"bs58check": "^2.0.0", "bs58check": "^2.0.0",
"create-hash": "^1.1.0", "create-hash": "^1.1.0",
"create-hmac": "^1.1.3", "create-hmac": "^1.1.3",
"ecurve": "^1.0.0",
"merkle-lib": "^2.0.10", "merkle-lib": "^2.0.10",
"pushdata-bitcoin": "^1.0.1", "pushdata-bitcoin": "^1.0.1",
"randombytes": "^2.0.1", "randombytes": "^2.0.1",

View file

@ -1,163 +0,0 @@
var Buffer = require('safe-buffer').Buffer
var createHmac = require('create-hmac')
var typeforce = require('typeforce')
var types = require('./types')
var BigInteger = require('bigi')
var ZERO = Buffer.alloc(1, 0)
var ONE = Buffer.alloc(1, 1)
var ecurve = require('ecurve')
var secp256k1 = ecurve.getCurveByName('secp256k1')
// https://tools.ietf.org/html/rfc6979#section-3.2
function deterministicGenerateK (hash, x, checkSig) {
typeforce(types.tuple(
types.Hash256bit,
types.Buffer256bit,
types.Function
), arguments)
// Step A, ignored as hash already provided
// Step B
// Step C
var k = Buffer.alloc(32, 0)
var v = Buffer.alloc(32, 1)
// Step D
k = createHmac('sha256', k)
.update(v)
.update(ZERO)
.update(x)
.update(hash)
.digest()
// Step E
v = createHmac('sha256', k).update(v).digest()
// Step F
k = createHmac('sha256', k)
.update(v)
.update(ONE)
.update(x)
.update(hash)
.digest()
// Step G
v = createHmac('sha256', k).update(v).digest()
// Step H1/H2a, ignored as tlen === qlen (256 bit)
// Step H2b
v = createHmac('sha256', k).update(v).digest()
var T = BigInteger.fromBuffer(v)
// Step H3, repeat until T is within the interval [1, n - 1] and is suitable for ECDSA
while (T.signum() <= 0 || T.compareTo(secp256k1.n) >= 0 || !checkSig(T)) {
k = createHmac('sha256', k)
.update(v)
.update(ZERO)
.digest()
v = createHmac('sha256', k).update(v).digest()
// Step H1/H2a, again, ignored as tlen === qlen (256 bit)
// Step H2b again
v = createHmac('sha256', k).update(v).digest()
T = BigInteger.fromBuffer(v)
}
return T
}
var N_OVER_TWO = secp256k1.n.shiftRight(1)
function sign (hash, d) {
typeforce(types.tuple(types.Hash256bit, types.BigInt), arguments)
var x = d.toBuffer(32)
var e = BigInteger.fromBuffer(hash)
var n = secp256k1.n
var G = secp256k1.G
var r, s
deterministicGenerateK(hash, x, function (k) {
var Q = G.multiply(k)
if (secp256k1.isInfinity(Q)) return false
r = Q.affineX.mod(n)
if (r.signum() === 0) return false
s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n)
if (s.signum() === 0) return false
return true
})
// enforce low S values, see bip62: 'low s values in signatures'
if (s.compareTo(N_OVER_TWO) > 0) {
s = n.subtract(s)
}
return {
r: r,
s: s
}
}
function verify (hash, signature, Q) {
typeforce(types.tuple(
types.Hash256bit,
types.ECSignature,
types.ECPoint
), arguments)
var n = secp256k1.n
var G = secp256k1.G
var r = signature.r
var s = signature.s
// 1.4.1 Enforce r and s are both integers in the interval [1, n 1]
if (r.signum() <= 0 || r.compareTo(n) >= 0) return false
if (s.signum() <= 0 || s.compareTo(n) >= 0) return false
// 1.4.2 H = Hash(M), already done by the user
// 1.4.3 e = H
var e = BigInteger.fromBuffer(hash)
// Compute s^-1
var sInv = s.modInverse(n)
// 1.4.4 Compute u1 = es^1 mod n
// u2 = rs^1 mod n
var u1 = e.multiply(sInv).mod(n)
var u2 = r.multiply(sInv).mod(n)
// 1.4.5 Compute R = (xR, yR)
// R = u1G + u2Q
var R = G.multiplyTwo(u1, Q, u2)
// 1.4.5 (cont.) Enforce R is not at infinity
if (secp256k1.isInfinity(R)) return false
// 1.4.6 Convert the field element R.x to an integer
var xR = R.affineX
// 1.4.7 Set v = xR mod n
var v = xR.mod(n)
// 1.4.8 If v = r, output "valid", and if v != r, output "invalid"
return v.equals(r)
}
module.exports = {
deterministicGenerateK: deterministicGenerateK,
sign: sign,
verify: verify,
// TODO: remove
__curve: secp256k1
}

View file

@ -1,63 +1,70 @@
let ecdsa = require('./ecdsa') let ecc = require('tiny-secp256k1')
let randomBytes = require('randombytes') let randomBytes = require('randombytes')
let typeforce = require('typeforce') let typeforce = require('typeforce')
let types = require('./types') let types = require('./types')
let wif = require('wif') let wif = require('wif')
let NETWORKS = require('./networks') let NETWORKS = require('./networks')
let BigInteger = require('bigi')
let ecurve = require('ecurve') // TODO: why is the function name toJSON weird?
let secp256k1 = ecdsa.__curve function isPoint (x) { return ecc.isPoint(x) }
let isOptions = typeforce.maybe(typeforce.compile({
function ECPair (d, Q, options) {
if (options) {
typeforce({
compressed: types.maybe(types.Boolean), compressed: types.maybe(types.Boolean),
network: types.maybe(types.Network) network: types.maybe(types.Network)
}, options) }))
}
function ECPair (d, Q, options) {
options = options || {} options = options || {}
if (d) {
if (d.signum() <= 0) throw new Error('Private key must be greater than 0')
if (d.compareTo(secp256k1.n) >= 0) throw new Error('Private key must be less than the curve order')
if (Q) throw new TypeError('Unexpected publicKey parameter')
this.d = d
} else {
typeforce(types.ECPoint, Q)
this.__Q = Q
}
this.compressed = options.compressed === undefined ? true : options.compressed this.compressed = options.compressed === undefined ? true : options.compressed
this.network = options.network || NETWORKS.bitcoin this.network = options.network || NETWORKS.bitcoin
this.__d = d || null
this.__Q = null
if (Q) this.__Q = ecc.pointCompress(Q, this.compressed)
} }
Object.defineProperty(ECPair.prototype, 'Q', { Object.defineProperty(ECPair.prototype, 'privateKey', {
get: function () { enumerable: false,
if (!this.__Q && this.d) { get: function () { return this.__d }
this.__Q = secp256k1.G.multiply(this.d)
}
return this.__Q
}
}) })
ECPair.fromPublicKeyBuffer = function (buffer, network) { Object.defineProperty(ECPair.prototype, 'publicKey', { get: function () {
var Q = ecurve.Point.decodeFrom(secp256k1, buffer) if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed)
return this.__Q
}})
return new ECPair(null, Q, { ECPair.prototype.toWIF = function () {
compressed: Q.compressed, if (!this.__d) throw new Error('Missing private key')
network: network return wif.encode(this.network.wif, this.__d, this.compressed)
})
} }
ECPair.fromWIF = function (string, network) { ECPair.prototype.sign = function (hash) {
var decoded = wif.decode(string) if (!this.__d) throw new Error('Missing private key')
var version = decoded.version return ecc.sign(hash, this.__d)
}
ECPair.prototype.verify = function (hash, signature) {
return ecc.verify(hash, this.publicKey, signature)
}
function fromPrivateKey (buffer, options) {
typeforce(types.Buffer256bit, buffer)
if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)')
typeforce(isOptions, options)
return new ECPair(buffer, null, options)
}
function fromPublicKey (buffer, options) {
typeforce(isPoint, buffer)
typeforce(isOptions, options)
return new ECPair(null, buffer, options)
}
function fromWIF (string, network) {
let decoded = wif.decode(string)
let version = decoded.version
// list of networks? // list of networks?
if (types.Array(network)) { if (types.Array(network)) {
@ -74,58 +81,29 @@ ECPair.fromWIF = function (string, network) {
if (version !== network.wif) throw new Error('Invalid network version') if (version !== network.wif) throw new Error('Invalid network version')
} }
var d = BigInteger.fromBuffer(decoded.privateKey) return fromPrivateKey(decoded.privateKey, {
return new ECPair(d, null, {
compressed: decoded.compressed, compressed: decoded.compressed,
network: network network: network
}) })
} }
ECPair.makeRandom = function (options) { function makeRandom (options) {
typeforce(isOptions, options)
options = options || {} options = options || {}
let rng = options.rng || randomBytes
var rng = options.rng || randomBytes let d
var d
do { do {
var buffer = rng(32) d = rng(32)
typeforce(types.Buffer256bit, buffer) typeforce(types.Buffer256bit, d)
} while (!ecc.isPrivate(d))
d = BigInteger.fromBuffer(buffer) return fromPrivateKey(d, options)
} while (d.signum() <= 0 || d.compareTo(secp256k1.n) >= 0)
return new ECPair(d, null, options)
} }
ECPair.prototype.getNetwork = function () { module.exports = {
return this.network makeRandom,
fromPrivateKey,
fromPublicKey,
fromWIF
} }
ECPair.prototype.getPublicKeyBuffer = function () {
return this.Q.getEncoded(this.compressed)
}
ECPair.prototype.sign = function (hash) {
if (!this.d) throw new Error('Missing private key')
let signature = ecdsa.sign(hash, this.d)
return Buffer.concat([signature.r.toBuffer(32), signature.s.toBuffer(32)], 64)
}
ECPair.prototype.toWIF = function () {
if (!this.d) throw new Error('Missing private key')
return wif.encode(this.network.wif, this.d.toBuffer(32), this.compressed)
}
ECPair.prototype.verify = function (hash, signature) {
signature = {
r: BigInteger.fromBuffer(signature.slice(0, 32)),
s: BigInteger.fromBuffer(signature.slice(32, 64))
}
return ecdsa.verify(hash, signature, this.Q)
}
module.exports = ECPair

View file

@ -180,7 +180,7 @@ function fixMultisigOrder (input, transaction, vin) {
var unmatched = input.signatures.concat() var unmatched = input.signatures.concat()
input.signatures = input.pubKeys.map(function (pubKey) { input.signatures = input.pubKeys.map(function (pubKey) {
var keyPair = ECPair.fromPublicKeyBuffer(pubKey) var keyPair = ECPair.fromPublicKey(pubKey)
var match var match
// check for a signature // check for a signature
@ -686,7 +686,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
throw new Error('Inconsistent redeemScript') throw new Error('Inconsistent redeemScript')
} }
var kpPubKey = keyPair.publicKey || keyPair.getPublicKeyBuffer() var kpPubKey = keyPair.publicKey || keyPair.getPublicKey()
if (!canSign(input)) { if (!canSign(input)) {
if (witnessValue !== undefined) { if (witnessValue !== undefined) {
if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue') if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue')

View file

@ -16,11 +16,9 @@ function Satoshi (value) {
} }
// external dependent types // external dependent types
var BigInt = typeforce.quacksLike('BigInteger')
var ECPoint = typeforce.quacksLike('Point') var ECPoint = typeforce.quacksLike('Point')
// exposed, external API // exposed, external API
var ECSignature = typeforce.compile({ r: BigInt, s: BigInt })
var Network = typeforce.compile({ var Network = typeforce.compile({
messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String),
bip32: { bip32: {
@ -34,11 +32,9 @@ var Network = typeforce.compile({
// extend typeforce types with ours // extend typeforce types with ours
var types = { var types = {
BigInt: BigInt,
BIP32Path: BIP32Path, BIP32Path: BIP32Path,
Buffer256bit: typeforce.BufferN(32), Buffer256bit: typeforce.BufferN(32),
ECPoint: ECPoint, ECPoint: ECPoint,
ECSignature: ECSignature,
Hash160bit: typeforce.BufferN(20), Hash160bit: typeforce.BufferN(20),
Hash256bit: typeforce.BufferN(32), Hash256bit: typeforce.BufferN(32),
Network: Network, Network: Network,

View file

@ -94,7 +94,7 @@ describe('Bitcoin-core', function () {
var keyPair = bitcoin.ECPair.fromWIF(string, network) var keyPair = bitcoin.ECPair.fromWIF(string, network)
it('fromWIF imports ' + string, function () { it('fromWIF imports ' + string, function () {
assert.strictEqual(keyPair.d.toHex(), hex) assert.strictEqual(keyPair.privateKey.toString('hex'), hex)
assert.strictEqual(keyPair.compressed, params.isCompressed) assert.strictEqual(keyPair.compressed, params.isCompressed)
}) })

View file

@ -1,135 +0,0 @@
/* global describe, it */
var assert = require('assert')
var bcrypto = require('../src/crypto')
var ecdsa = require('../src/ecdsa')
var hoodwink = require('hoodwink')
var BigInteger = require('bigi')
var curve = ecdsa.__curve
var fixtures = require('./fixtures/ecdsa.json')
describe('ecdsa', function () {
function fromRaw (signature) {
return {
r: new BigInteger(signature.r, 16),
s: new BigInteger(signature.s, 16)
}
}
function toRaw (signature) {
return {
r: signature.r.toHex(),
s: signature.s.toHex()
}
}
describe('deterministicGenerateK', function () {
function checkSig () {
return true
}
fixtures.valid.ecdsa.forEach(function (f) {
it('for "' + f.message + '"', function () {
var x = BigInteger.fromHex(f.d).toBuffer(32)
var h1 = bcrypto.sha256(f.message)
var k = ecdsa.deterministicGenerateK(h1, x, checkSig)
assert.strictEqual(k.toHex(), f.k)
})
})
it('loops until an appropriate k value is found', hoodwink(function () {
this.mock(BigInteger, 'fromBuffer', function f (b) {
assert.strictEqual(b.length, 32)
if (f.calls === 0) return BigInteger.ZERO // < 1
if (f.calls === 1) return curve.n // > n - 1
if (f.calls === 2) return new BigInteger('42') // valid
}, 3)
var x = new BigInteger('1').toBuffer(32)
var h1 = Buffer.alloc(32)
var k = ecdsa.deterministicGenerateK(h1, x, checkSig)
assert.strictEqual(k.toString(), '42')
}))
it('loops until a suitable signature is found', hoodwink(function () {
var checkSigStub = this.stub(function f () {
if (f.calls === 0) return false // bad signature
if (f.calls === 1) return true // good signature
}, 2)
var x = BigInteger.ONE.toBuffer(32)
var h1 = Buffer.alloc(32)
var k = ecdsa.deterministicGenerateK(h1, x, checkSigStub)
assert.strictEqual(k.toHex(), 'a9b1a1a84a4c2f96b6158ed7a81404c50cb74373c22e8d9e02d0411d719acae2')
}))
fixtures.valid.rfc6979.forEach(function (f) {
it('produces the expected k values for ' + f.message + " if k wasn't suitable", function () {
var x = BigInteger.fromHex(f.d).toBuffer(32)
var h1 = bcrypto.sha256(f.message)
var results = []
ecdsa.deterministicGenerateK(h1, x, function (k) {
results.push(k)
return results.length === 16
})
assert.strictEqual(results[0].toHex(), f.k0)
assert.strictEqual(results[1].toHex(), f.k1)
assert.strictEqual(results[15].toHex(), f.k15)
})
})
})
describe('sign', function () {
fixtures.valid.ecdsa.forEach(function (f) {
it('produces a deterministic signature for "' + f.message + '"', function () {
var d = BigInteger.fromHex(f.d)
var hash = bcrypto.sha256(f.message)
var signature = ecdsa.sign(hash, d)
assert.deepEqual(toRaw(signature), f.signature)
})
})
it('should sign with low S value', function () {
var hash = bcrypto.sha256('Vires in numeris')
var sig = ecdsa.sign(hash, BigInteger.ONE)
// See BIP62 for more information
var N_OVER_TWO = curve.n.shiftRight(1)
assert(sig.s.compareTo(N_OVER_TWO) <= 0)
})
})
describe('verify', function () {
fixtures.valid.ecdsa.forEach(function (f) {
it('verifies a valid signature for "' + f.message + '"', function () {
var d = BigInteger.fromHex(f.d)
var H = bcrypto.sha256(f.message)
var signature = fromRaw(f.signature)
var Q = curve.G.multiply(d)
assert(ecdsa.verify(H, signature, Q))
})
})
fixtures.invalid.verify.forEach(function (f) {
it('fails to verify with ' + f.description, function () {
var H = bcrypto.sha256(f.message)
var d = BigInteger.fromHex(f.d)
var signature = fromRaw(f.signature)
var Q = curve.G.multiply(d)
assert.strictEqual(ecdsa.verify(H, signature, Q), false)
})
})
})
})

View file

@ -1,34 +1,36 @@
/* global describe, it, beforeEach */ /* global describe, it, beforeEach */
/* eslint-disable no-new */ /* eslint-disable no-new */
var assert = require('assert') let assert = require('assert')
var ecdsa = require('../src/ecdsa') let proxyquire = require('proxyquire')
var ecurve = require('ecurve') let hoodwink = require('hoodwink')
var proxyquire = require('proxyquire')
var hoodwink = require('hoodwink')
var BigInteger = require('bigi') let ECPair = require('../src/ecpair')
var ECPair = require('../src/ecpair') let tinysecp = require('tiny-secp256k1')
var fixtures = require('./fixtures/ecpair.json') let fixtures = require('./fixtures/ecpair.json')
var curve = ecdsa.__curve
var NETWORKS = require('../src/networks') let NETWORKS = require('../src/networks')
var NETWORKS_LIST = [] // Object.values(NETWORKS) let NETWORKS_LIST = [] // Object.values(NETWORKS)
for (var networkName in NETWORKS) { for (let networkName in NETWORKS) {
NETWORKS_LIST.push(NETWORKS[networkName]) NETWORKS_LIST.push(NETWORKS[networkName])
} }
let ZERO = Buffer.alloc(32, 0)
let ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')
let GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 'hex')
let GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 'hex')
describe('ECPair', function () { describe('ECPair', function () {
describe('constructor', function () { describe('constructor', function () {
it('defaults to compressed', function () { it('defaults to compressed', function () {
var keyPair = new ECPair(BigInteger.ONE) let keyPair = ECPair.fromPrivateKey(ONE)
assert.strictEqual(keyPair.compressed, true) assert.strictEqual(keyPair.compressed, true)
}) })
it('supports the uncompressed option', function () { it('supports the uncompressed option', function () {
var keyPair = new ECPair(BigInteger.ONE, null, { let keyPair = ECPair.fromPrivateKey(ONE, {
compressed: false compressed: false
}) })
@ -36,7 +38,7 @@ describe('ECPair', function () {
}) })
it('supports the network option', function () { it('supports the network option', function () {
var keyPair = new ECPair(BigInteger.ONE, null, { let keyPair = ECPair.fromPrivateKey(ONE, {
compressed: false, compressed: false,
network: NETWORKS.testnet network: NETWORKS.testnet
}) })
@ -45,51 +47,58 @@ describe('ECPair', function () {
}) })
fixtures.valid.forEach(function (f) { fixtures.valid.forEach(function (f) {
it('calculates the public point for ' + f.WIF, function () { it('derives public key for ' + f.WIF, function () {
var d = new BigInteger(f.d) let d = Buffer.from(f.d, 'hex')
var keyPair = new ECPair(d, null, { console.log(d)
let keyPair = ECPair.fromPrivateKey(d, {
compressed: f.compressed compressed: f.compressed
}) })
assert.strictEqual(keyPair.getPublicKeyBuffer().toString('hex'), f.Q) assert.strictEqual(keyPair.publicKey.toString('hex'), f.Q)
}) })
}) })
fixtures.invalid.constructor.forEach(function (f) { fixtures.invalid.constructor.forEach(function (f) {
it('throws ' + f.exception, function () { it('throws ' + f.exception, function () {
var d = f.d && new BigInteger(f.d) if (f.d) {
var Q = f.Q && ecurve.Point.decodeFrom(curve, Buffer.from(f.Q, 'hex')) let d = Buffer.from(f.d, 'hex')
assert.throws(function () { assert.throws(function () {
new ECPair(d, Q, f.options) ECPair.fromPrivateKey(d, f.options)
}, new RegExp(f.exception)) }, new RegExp(f.exception))
} else {
let Q = Buffer.from(f.Q, 'hex')
assert.throws(function () {
ECPair.fromPublicKey(Q, f.options)
}, new RegExp(f.exception))
}
}) })
}) })
}) })
describe('getPublicKeyBuffer', function () { describe('getPublicKey', function () {
var keyPair let keyPair
beforeEach(function () { beforeEach(function () {
keyPair = new ECPair(BigInteger.ONE) keyPair = ECPair.fromPrivateKey(ONE)
}) })
it('wraps Q.getEncoded', hoodwink(function () { it('calls pointFromScalar lazily', hoodwink(function () {
this.mock(keyPair.Q, 'getEncoded', function (compressed) { assert.strictEqual(keyPair.__Q, null)
assert.strictEqual(compressed, keyPair.compressed)
}, 1)
keyPair.getPublicKeyBuffer() // .publicKey forces the memoization
assert.strictEqual(keyPair.publicKey.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
assert.strictEqual(keyPair.__Q.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
})) }))
}) })
describe('fromWIF', function () { describe('fromWIF', function () {
fixtures.valid.forEach(function (f) { fixtures.valid.forEach(function (f) {
it('imports ' + f.WIF + ' (' + f.network + ')', function () { it('imports ' + f.WIF + ' (' + f.network + ')', function () {
var network = NETWORKS[f.network] let network = NETWORKS[f.network]
var keyPair = ECPair.fromWIF(f.WIF, network) let keyPair = ECPair.fromWIF(f.WIF, network)
assert.strictEqual(keyPair.d.toString(), f.d) assert.strictEqual(keyPair.privateKey.toString('hex'), f.d)
assert.strictEqual(keyPair.compressed, f.compressed) assert.strictEqual(keyPair.compressed, f.compressed)
assert.strictEqual(keyPair.network, network) assert.strictEqual(keyPair.network, network)
}) })
@ -97,9 +106,9 @@ describe('ECPair', function () {
fixtures.valid.forEach(function (f) { fixtures.valid.forEach(function (f) {
it('imports ' + f.WIF + ' (via list of networks)', function () { it('imports ' + f.WIF + ' (via list of networks)', function () {
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
assert.strictEqual(keyPair.d.toString(), f.d) assert.strictEqual(keyPair.privateKey.toString('hex'), f.d)
assert.strictEqual(keyPair.compressed, f.compressed) assert.strictEqual(keyPair.compressed, f.compressed)
assert.strictEqual(keyPair.network, NETWORKS[f.network]) assert.strictEqual(keyPair.network, NETWORKS[f.network])
}) })
@ -108,7 +117,7 @@ describe('ECPair', function () {
fixtures.invalid.fromWIF.forEach(function (f) { fixtures.invalid.fromWIF.forEach(function (f) {
it('throws on ' + f.WIF, function () { it('throws on ' + f.WIF, function () {
assert.throws(function () { assert.throws(function () {
var networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST let networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST
ECPair.fromWIF(f.WIF, networks) ECPair.fromWIF(f.WIF, networks)
}, new RegExp(f.exception)) }, new RegExp(f.exception))
@ -119,30 +128,29 @@ describe('ECPair', function () {
describe('toWIF', function () { describe('toWIF', function () {
fixtures.valid.forEach(function (f) { fixtures.valid.forEach(function (f) {
it('exports ' + f.WIF, function () { it('exports ' + f.WIF, function () {
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
var result = keyPair.toWIF() let result = keyPair.toWIF()
assert.strictEqual(result, f.WIF) assert.strictEqual(result, f.WIF)
}) })
}) })
}) })
describe('makeRandom', function () { describe('makeRandom', function () {
var d = Buffer.from('0404040404040404040404040404040404040404040404040404040404040404', 'hex') let d = Buffer.alloc(32, 4)
var exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv' let exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv'
describe('uses randombytes RNG', function () { describe('uses randombytes RNG', function () {
it('generates a ECPair', function () { it('generates a ECPair', function () {
var stub = { randombytes: function () { return d } } let stub = { randombytes: function () { return d } }
var ProxiedECPair = proxyquire('../src/ecpair', stub) let ProxiedECPair = proxyquire('../src/ecpair', stub)
var keyPair = ProxiedECPair.makeRandom() let keyPair = ProxiedECPair.makeRandom()
assert.strictEqual(keyPair.toWIF(), exWIF) assert.strictEqual(keyPair.toWIF(), exWIF)
}) })
}) })
it('allows a custom RNG to be used', function () { it('allows a custom RNG to be used', function () {
var keyPair = ECPair.makeRandom({ let keyPair = ECPair.makeRandom({
rng: function (size) { return d.slice(0, size) } rng: function (size) { return d.slice(0, size) }
}) })
@ -150,14 +158,14 @@ describe('ECPair', function () {
}) })
it('retains the same defaults as ECPair constructor', function () { it('retains the same defaults as ECPair constructor', function () {
var keyPair = ECPair.makeRandom() let keyPair = ECPair.makeRandom()
assert.strictEqual(keyPair.compressed, true) assert.strictEqual(keyPair.compressed, true)
assert.strictEqual(keyPair.network, NETWORKS.bitcoin) assert.strictEqual(keyPair.network, NETWORKS.bitcoin)
}) })
it('supports the options parameter', function () { it('supports the options parameter', function () {
var keyPair = ECPair.makeRandom({ let keyPair = ECPair.makeRandom({
compressed: false, compressed: false,
network: NETWORKS.testnet network: NETWORKS.testnet
}) })
@ -168,7 +176,7 @@ describe('ECPair', function () {
it('throws if d is bad length', function () { it('throws if d is bad length', function () {
function rng () { function rng () {
return BigInteger.ZERO.toBuffer(28) return Buffer.alloc(28)
} }
assert.throws(function () { assert.throws(function () {
@ -176,58 +184,59 @@ describe('ECPair', function () {
}, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/) }, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/)
}) })
it('loops until d is within interval [1, n - 1] : 1', hoodwink(function () { it('loops until d is within interval [1, n) : 1', hoodwink(function () {
var rng = this.stub(function f () { let rng = this.stub(function f () {
if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // 0 if (f.calls === 0) return ZERO // 0
return BigInteger.ONE.toBuffer(32) // >0 return ONE // >0
}, 2) }, 2)
ECPair.makeRandom({ rng: rng }) ECPair.makeRandom({ rng: rng })
})) }))
it('loops until d is within interval [1, n - 1] : n - 1', hoodwink(function () { it('loops until d is within interval [1, n) : n - 1', hoodwink(function () {
var rng = this.stub(function f () { let rng = this.stub(function f () {
if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // <1 if (f.calls === 0) return ZERO // <1
if (f.calls === 1) return curve.n.toBuffer(32) // >n-1 if (f.calls === 1) return GROUP_ORDER // >n-1
return curve.n.subtract(BigInteger.ONE).toBuffer(32) // n-1 return GROUP_ORDER_LESS_1 // n-1
}, 3) }, 3)
ECPair.makeRandom({ rng: rng }) ECPair.makeRandom({ rng: rng })
})) }))
}) })
describe('getNetwork', function () { describe('.network', function () {
fixtures.valid.forEach(function (f) { fixtures.valid.forEach(function (f) {
it('returns ' + f.network + ' for ' + f.WIF, function () { it('returns ' + f.network + ' for ' + f.WIF, function () {
var network = NETWORKS[f.network] let network = NETWORKS[f.network]
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST) let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
assert.strictEqual(keyPair.getNetwork(), network) assert.strictEqual(keyPair.network, network)
}) })
}) })
}) })
describe('ecdsa wrappers', function () { describe('tinysecp wrappers', function () {
var keyPair, hash let keyPair, hash, signature
beforeEach(function () { beforeEach(function () {
keyPair = ECPair.makeRandom() keyPair = ECPair.makeRandom()
hash = Buffer.alloc(32) hash = ZERO
signature = Buffer.alloc(64, 1)
}) })
describe('signing', function () { describe('signing', function () {
it('wraps ecdsa.sign', hoodwink(function () { it('wraps tinysecp.sign', hoodwink(function () {
this.mock(ecdsa, 'sign', function (h, d) { this.mock(tinysecp, 'sign', function (h, d) {
assert.strictEqual(h, hash) assert.strictEqual(h, hash)
assert.strictEqual(d, keyPair.d) assert.strictEqual(d, keyPair.privateKey)
return { r: BigInteger.ONE, s: BigInteger.ONE } return signature
}, 1) }, 1)
keyPair.sign(hash) assert.strictEqual(keyPair.sign(hash), signature)
})) }))
it('throws if no private key is found', function () { it('throws if no private key is found', function () {
keyPair.d = null delete keyPair.__d
assert.throws(function () { assert.throws(function () {
keyPair.sign(hash) keyPair.sign(hash)
@ -236,24 +245,15 @@ describe('ECPair', function () {
}) })
describe('verify', function () { describe('verify', function () {
var signature it('wraps tinysecp.verify', hoodwink(function () {
this.mock(tinysecp, 'verify', function (h, q, s) {
beforeEach(function () {
signature = keyPair.sign(hash)
})
it('wraps ecdsa.verify', hoodwink(function () {
this.mock(ecdsa, 'verify', function (h, s, q) {
assert.strictEqual(h, hash) assert.strictEqual(h, hash)
// assert.strictEqual(s, signature) assert.strictEqual(q, keyPair.publicKey)
assert.deepEqual(s, { assert.strictEqual(s, signature)
r: BigInteger.fromBuffer(signature.slice(0, 32)), return true
s: BigInteger.fromBuffer(signature.slice(32, 64))
})
assert.strictEqual(q, keyPair.Q)
}, 1) }, 1)
keyPair.verify(hash, signature) assert.strictEqual(keyPair.verify(hash, signature), true)
})) }))
}) })
}) })

View file

@ -1,7 +1,7 @@
{ {
"valid": [ "valid": [
{ {
"d": "1", "d": "0000000000000000000000000000000000000000000000000000000000000001",
"Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", "Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"compressed": true, "compressed": true,
"network": "bitcoin", "network": "bitcoin",
@ -9,7 +9,7 @@
"WIF": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn" "WIF": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn"
}, },
{ {
"d": "1", "d": "0000000000000000000000000000000000000000000000000000000000000001",
"Q": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", "Q": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
"compressed": false, "compressed": false,
"network": "bitcoin", "network": "bitcoin",
@ -17,7 +17,7 @@
"WIF": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf" "WIF": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf"
}, },
{ {
"d": "19898843618908353587043383062236220484949425084007183071220218307100305431102", "d": "2bfe58ab6d9fd575bdc3a624e4825dd2b375d64ac033fbc46ea79dbab4f69a3e",
"Q": "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340", "Q": "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340",
"compressed": true, "compressed": true,
"network": "bitcoin", "network": "bitcoin",
@ -25,7 +25,7 @@
"WIF": "KxhEDBQyyEFymvfJD96q8stMbJMbZUb6D1PmXqBWZDU2WvbvVs9o" "WIF": "KxhEDBQyyEFymvfJD96q8stMbJMbZUb6D1PmXqBWZDU2WvbvVs9o"
}, },
{ {
"d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", "d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b",
"Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34", "Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34",
"compressed": true, "compressed": true,
"network": "bitcoin", "network": "bitcoin",
@ -33,7 +33,7 @@
"WIF": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx" "WIF": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx"
}, },
{ {
"d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", "d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b",
"Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6",
"compressed": false, "compressed": false,
"network": "bitcoin", "network": "bitcoin",
@ -41,7 +41,7 @@
"WIF": "5JdxzLtFPHNe7CAL8EBC6krdFv9pwPoRo4e3syMZEQT9srmK8hh" "WIF": "5JdxzLtFPHNe7CAL8EBC6krdFv9pwPoRo4e3syMZEQT9srmK8hh"
}, },
{ {
"d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", "d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b",
"Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34", "Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34",
"compressed": true, "compressed": true,
"network": "testnet", "network": "testnet",
@ -49,7 +49,7 @@
"WIF": "cRD9b1m3vQxmwmjSoJy7Mj56f4uNFXjcWMCzpQCEmHASS4edEwXv" "WIF": "cRD9b1m3vQxmwmjSoJy7Mj56f4uNFXjcWMCzpQCEmHASS4edEwXv"
}, },
{ {
"d": "48968302285117906840285529799176770990048954789747953886390402978935544927851", "d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b",
"Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6", "Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6",
"compressed": false, "compressed": false,
"network": "testnet", "network": "testnet",
@ -57,7 +57,7 @@
"WIF": "92Qba5hnyWSn5Ffcka56yMQauaWY6ZLd91Vzxbi4a9CCetaHtYj" "WIF": "92Qba5hnyWSn5Ffcka56yMQauaWY6ZLd91Vzxbi4a9CCetaHtYj"
}, },
{ {
"d": "115792089237316195423570985008687907852837564279074904382605163141518161494336", "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
"Q": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", "Q": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
"compressed": true, "compressed": true,
"network": "bitcoin", "network": "bitcoin",
@ -68,36 +68,27 @@
"invalid": { "invalid": {
"constructor": [ "constructor": [
{ {
"exception": "Private key must be greater than 0", "exception": "Private key not in range \\[1, n\\)",
"d": "-1" "d": "0000000000000000000000000000000000000000000000000000000000000000"
}, },
{ {
"exception": "Private key must be greater than 0", "exception": "Private key not in range \\[1, n\\)",
"d": "0" "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"
}, },
{ {
"exception": "Private key must be less than the curve order", "exception": "Private key not in range \\[1, n\\)",
"d": "115792089237316195423570985008687907852837564279074904382605163141518161494337" "d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142"
},
{
"exception": "Private key must be less than the curve order",
"d": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
}, },
{ {
"exception": "Expected property \"compressed\" of type \\?Boolean, got Number 2", "exception": "Expected property \"compressed\" of type \\?Boolean, got Number 2",
"d": "1", "d": "0000000000000000000000000000000000000000000000000000000000000001",
"options": { "options": {
"compressed": 2 "compressed": 2
} }
}, },
{
"exception": "Unexpected publicKey parameter",
"d": "1",
"Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
},
{ {
"exception": "Expected property \"network.messagePrefix\" of type Buffer|String, got undefined", "exception": "Expected property \"network.messagePrefix\" of type Buffer|String, got undefined",
"d": "1", "d": "0000000000000000000000000000000000000000000000000000000000000001",
"options": { "options": {
"network": {} "network": {}
} }

View file

@ -72,7 +72,7 @@ let baddress = bitcoin.address
let bcrypto = bitcoin.crypto let bcrypto = bitcoin.crypto
function getAddress (node, network) { function getAddress (node, network) {
network = network || bitcoin.networks.bitcoin network = network || bitcoin.networks.bitcoin
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash) return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash)
} }
function randomAddress () { function randomAddress () {

View file

@ -12,7 +12,7 @@ let baddress = bitcoin.address
let bcrypto = bitcoin.crypto let bcrypto = bitcoin.crypto
function getAddress (node, network) { function getAddress (node, network) {
network = network || bitcoin.networks.bitcoin network = network || bitcoin.networks.bitcoin
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash) return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash)
} }
describe('bitcoinjs-lib (addresses)', function () { describe('bitcoinjs-lib (addresses)', function () {
@ -26,7 +26,7 @@ describe('bitcoinjs-lib (addresses)', function () {
it('can generate an address from a SHA256 hash', function () { it('can generate an address from a SHA256 hash', function () {
var hash = bitcoin.crypto.sha256(Buffer.from('correct horse battery staple')) var hash = bitcoin.crypto.sha256(Buffer.from('correct horse battery staple'))
var keyPair = bitcoin.ECPair.makeRandom({ rng: () => hash }) var keyPair = bitcoin.ECPair.fromPrivateKey(hash)
var address = getAddress(keyPair) var address = getAddress(keyPair)
// Generating addresses from SHA256 hashes is not secure if the input to the hash function is predictable // Generating addresses from SHA256 hashes is not secure if the input to the hash function is predictable
@ -57,9 +57,8 @@ describe('bitcoinjs-lib (addresses)', function () {
it('can generate a SegWit address', function () { it('can generate a SegWit address', function () {
var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct')
var pubKey = keyPair.getPublicKeyBuffer()
var scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(pubKey)) var scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey))
var address = bitcoin.address.fromOutputScript(scriptPubKey) var address = bitcoin.address.fromOutputScript(scriptPubKey)
assert.strictEqual(address, 'bc1qt97wqg464zrhnx23upykca5annqvwkwujjglky') assert.strictEqual(address, 'bc1qt97wqg464zrhnx23upykca5annqvwkwujjglky')
@ -67,9 +66,8 @@ describe('bitcoinjs-lib (addresses)', function () {
it('can generate a SegWit address (via P2SH)', function () { it('can generate a SegWit address (via P2SH)', function () {
var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct') var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct')
var pubKey = keyPair.getPublicKeyBuffer()
var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(pubKey)) var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(keyPair.publicKey))
var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
var address = bitcoin.address.fromOutputScript(scriptPubKey) var address = bitcoin.address.fromOutputScript(scriptPubKey)

View file

@ -25,11 +25,11 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
bitcoin.opcodes.OP_DROP, bitcoin.opcodes.OP_DROP,
bitcoin.opcodes.OP_ELSE, bitcoin.opcodes.OP_ELSE,
bQ.getPublicKeyBuffer(), bQ.publicKey,
bitcoin.opcodes.OP_CHECKSIGVERIFY, bitcoin.opcodes.OP_CHECKSIGVERIFY,
bitcoin.opcodes.OP_ENDIF, bitcoin.opcodes.OP_ENDIF,
aQ.getPublicKeyBuffer(), aQ.publicKey,
bitcoin.opcodes.OP_CHECKSIG bitcoin.opcodes.OP_CHECKSIG
]) ])
} }

View file

@ -24,7 +24,7 @@ describe('bitcoinjs-lib (crypto)', function () {
assert(bitcoin.script.pubKeyHash.input.check(scriptChunks), 'Expected pubKeyHash script') assert(bitcoin.script.pubKeyHash.input.check(scriptChunks), 'Expected pubKeyHash script')
var prevOutScript = bitcoin.address.toOutputScript('1ArJ9vRaQcoQ29mTWZH768AmRwzb6Zif1z') var prevOutScript = bitcoin.address.toOutputScript('1ArJ9vRaQcoQ29mTWZH768AmRwzb6Zif1z')
var scriptSignature = bitcoin.script.signature.decode(scriptChunks[0]) var scriptSignature = bitcoin.script.signature.decode(scriptChunks[0])
var publicKey = bitcoin.ECPair.fromPublicKeyBuffer(scriptChunks[1]) var publicKey = bitcoin.ECPair.fromPublicKey(scriptChunks[1])
var m = tx.hashForSignature(vin, prevOutScript, scriptSignature.hashType) var m = tx.hashForSignature(vin, prevOutScript, scriptSignature.hashType)
assert(publicKey.verify(m, scriptSignature.signature), 'Invalid m') assert(publicKey.verify(m, scriptSignature.signature), 'Invalid m')

View file

@ -26,11 +26,11 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () {
bitcoin.opcodes.OP_DROP, bitcoin.opcodes.OP_DROP,
bitcoin.opcodes.OP_ELSE, bitcoin.opcodes.OP_ELSE,
bQ.getPublicKeyBuffer(), bQ.publicKey,
bitcoin.opcodes.OP_CHECKSIGVERIFY, bitcoin.opcodes.OP_CHECKSIGVERIFY,
bitcoin.opcodes.OP_ENDIF, bitcoin.opcodes.OP_ENDIF,
aQ.getPublicKeyBuffer(), aQ.publicKey,
bitcoin.opcodes.OP_CHECKSIG bitcoin.opcodes.OP_CHECKSIG
]) ])
} }

View file

@ -1,74 +1,72 @@
/* global describe, it */ /* global describe, it */
let assert = require('assert') let assert = require('assert')
let bigi = require('bigi')
let bitcoin = require('../../') let bitcoin = require('../../')
let ecc = require('tiny-secp256k1')
let ecurve = require('ecurve')
let secp256k1 = ecurve.getCurveByName('secp256k1')
let G = secp256k1.G
let n = secp256k1.n
// TODO: remove // TODO: remove
let baddress = bitcoin.address let baddress = bitcoin.address
let bcrypto = bitcoin.crypto let bcrypto = bitcoin.crypto
function getAddress (node) { function getAddress (node) {
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), bitcoin.networks.bitcoin.pubKeyHash) return baddress.toBase58Check(bcrypto.hash160(node.publicKey), bitcoin.networks.bitcoin.pubKeyHash)
} }
// vG = (dG \+ sha256(e * dG)G) // vG = (dG \+ sha256(e * dG)G)
function stealthSend (e, Q) { function stealthSend (e, Q) {
var eQ = Q.multiply(e) // shared secret var eQ = ecc.pointMultiply(Q, e, true) // shared secret
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) var c = bitcoin.crypto.sha256(eQ)
var cG = G.multiply(c) var Qc = ecc.pointAddScalar(Q, c)
var vG = new bitcoin.ECPair(null, Q.add(cG)) var vG = bitcoin.ECPair.fromPublicKey(Qc)
return vG return vG
} }
// v = (d + sha256(eG * d)) // v = (d + sha256(eG * d))
function stealthReceive (d, eG) { function stealthReceive (d, eG) {
var eQ = eG.multiply(d) // shared secret var eQ = ecc.pointMultiply(eG, d) // shared secret
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) var c = bitcoin.crypto.sha256(eQ)
var v = new bitcoin.ECPair(d.add(c).mod(n)) var dc = ecc.privateAdd(d, c)
var v = bitcoin.ECPair.fromPrivateKey(dc)
return v return v
} }
// d = (v - sha256(e * dG)) // d = (v - sha256(e * dG))
function stealthRecoverLeaked (v, e, Q) { function stealthRecoverLeaked (v, e, Q) {
var eQ = Q.multiply(e) // shared secret var eQ = ecc.pointMultiply(Q, e) // shared secret
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) var c = bitcoin.crypto.sha256(eQ)
var d = new bitcoin.ECPair(v.subtract(c).mod(n)) var vc = ecc.privateSub(v, c)
var d = bitcoin.ECPair.fromPrivateKey(vc)
return d return d
} }
// vG = (rG \+ sha256(e * dG)G) // vG = (rG \+ sha256(e * dG)G)
function stealthDualSend (e, R, Q) { function stealthDualSend (e, R, Q) {
var eQ = Q.multiply(e) // shared secret var eQ = ecc.pointMultiply(Q, e) // shared secret
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) var c = bitcoin.crypto.sha256(eQ)
var cG = G.multiply(c) var Rc = ecc.pointAddScalar(R, c)
var vG = new bitcoin.ECPair(null, R.add(cG)) var vG = bitcoin.ECPair.fromPublicKey(Rc)
return vG return vG
} }
// vG = (rG \+ sha256(eG * d)G) // vG = (rG \+ sha256(eG * d)G)
function stealthDualScan (d, R, eG) { function stealthDualScan (d, R, eG) {
var eQ = eG.multiply(d) // shared secret var eQ = ecc.pointMultiply(eG, d) // shared secret
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) var c = bitcoin.crypto.sha256(eQ)
var cG = G.multiply(c) var Rc = ecc.pointAddScalar(R, c)
var vG = new bitcoin.ECPair(null, R.add(cG)) var vG = bitcoin.ECPair.fromPublicKey(Rc)
return vG return vG
} }
// v = (r + sha256(eG * d)) // v = (r + sha256(eG * d))
function stealthDualReceive (d, r, eG) { function stealthDualReceive (d, r, eG) {
var eQ = eG.multiply(d) // shared secret var eQ = ecc.pointMultiply(eG, d) // shared secret
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded())) var c = bitcoin.crypto.sha256(eQ)
var v = new bitcoin.ECPair(r.add(c).mod(n)) var rc = ecc.privateAdd(r, c)
var v = bitcoin.ECPair.fromPrivateKey(rc)
return v return v
} }
@ -80,12 +78,12 @@ describe('bitcoinjs-lib (crypto)', function () {
var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender
// ... recipient reveals public key (recipient.Q) to sender // ... recipient reveals public key (recipient.Q) to sender
var forSender = stealthSend(nonce.d, recipient.Q) var forSender = stealthSend(nonce.privateKey, recipient.publicKey)
assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE')
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
// ... sender reveals nonce public key (nonce.Q) to recipient // ... sender reveals nonce public key (nonce.Q) to recipient
var forRecipient = stealthReceive(recipient.d, nonce.Q) var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey)
assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE') assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE')
assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n') assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n')
@ -98,11 +96,11 @@ describe('bitcoinjs-lib (crypto)', function () {
var nonce = bitcoin.ECPair.makeRandom() // private to sender var nonce = bitcoin.ECPair.makeRandom() // private to sender
// ... recipient reveals public key (recipient.Q) to sender // ... recipient reveals public key (recipient.Q) to sender
var forSender = stealthSend(nonce.d, recipient.Q) var forSender = stealthSend(nonce.privateKey, recipient.publicKey)
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
// ... sender reveals nonce public key (nonce.Q) to recipient // ... sender reveals nonce public key (nonce.Q) to recipient
var forRecipient = stealthReceive(recipient.d, nonce.Q) var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey)
assert.doesNotThrow(function () { forRecipient.toWIF() }) assert.doesNotThrow(function () { forRecipient.toWIF() })
// sender and recipient, both derived same address // sender and recipient, both derived same address
@ -114,15 +112,15 @@ describe('bitcoinjs-lib (crypto)', function () {
var nonce = bitcoin.ECPair.makeRandom() // private to sender var nonce = bitcoin.ECPair.makeRandom() // private to sender
// ... recipient reveals public key (recipient.Q) to sender // ... recipient reveals public key (recipient.Q) to sender
var forSender = stealthSend(nonce.d, recipient.Q) var forSender = stealthSend(nonce.privateKey, recipient.publicKey)
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
// ... sender reveals nonce public key (nonce.Q) to recipient // ... sender reveals nonce public key (nonce.Q) to recipient
var forRecipient = stealthReceive(recipient.d, nonce.Q) var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey)
assert.doesNotThrow(function () { forRecipient.toWIF() }) assert.doesNotThrow(function () { forRecipient.toWIF() })
// ... recipient accidentally leaks forRecipient.d on the blockchain // ... recipient accidentally leaks forRecipient.d on the blockchain
var leaked = stealthRecoverLeaked(forRecipient.d, nonce.d, recipient.Q) var leaked = stealthRecoverLeaked(forRecipient.privateKey, nonce.privateKey, recipient.publicKey)
assert.equal(leaked.toWIF(), recipient.toWIF()) assert.equal(leaked.toWIF(), recipient.toWIF())
}) })
@ -133,15 +131,15 @@ describe('bitcoinjs-lib (crypto)', function () {
var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender
// ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender
var forSender = stealthDualSend(nonce.d, recipient.Q, scan.Q) var forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey)
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
// ... sender reveals nonce public key (nonce.Q) to scanner // ... sender reveals nonce public key (nonce.Q) to scanner
var forScanner = stealthDualScan(scan.d, recipient.Q, nonce.Q) var forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey)
assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/)
// ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient
var forRecipient = stealthDualReceive(scan.d, recipient.d, nonce.Q) var forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey)
assert.doesNotThrow(function () { forRecipient.toWIF() }) assert.doesNotThrow(function () { forRecipient.toWIF() })
// scanner, sender and recipient, all derived same address // scanner, sender and recipient, all derived same address
@ -155,15 +153,15 @@ describe('bitcoinjs-lib (crypto)', function () {
var nonce = bitcoin.ECPair.makeRandom() // private to sender var nonce = bitcoin.ECPair.makeRandom() // private to sender
// ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender // ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender
var forSender = stealthDualSend(nonce.d, recipient.Q, scan.Q) var forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey)
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/) assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
// ... sender reveals nonce public key (nonce.Q) to scanner // ... sender reveals nonce public key (nonce.Q) to scanner
var forScanner = stealthDualScan(scan.d, recipient.Q, nonce.Q) var forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey)
assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/) assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/)
// ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient // ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient
var forRecipient = stealthDualReceive(scan.d, recipient.d, nonce.Q) var forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey)
assert.doesNotThrow(function () { forRecipient.toWIF() }) assert.doesNotThrow(function () { forRecipient.toWIF() })
// scanner, sender and recipient, all derived same address // scanner, sender and recipient, all derived same address

View file

@ -10,7 +10,7 @@ let baddress = bitcoin.address
let bcrypto = bitcoin.crypto let bcrypto = bitcoin.crypto
function getAddress (node, network) { function getAddress (node, network) {
network = network || bitcoin.networks.bitcoin network = network || bitcoin.networks.bitcoin
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash) return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash)
} }
function rng () { function rng () {
@ -115,7 +115,7 @@ describe('bitcoinjs-lib (transactions)', function () {
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe', '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe',
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx9rcrL7' '91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx9rcrL7'
].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) }) ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) })
var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() }) var pubKeys = keyPairs.map(function (x) { return x.publicKey })
var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys) var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys)
var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript)) var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
@ -150,8 +150,7 @@ describe('bitcoinjs-lib (transactions)', function () {
this.timeout(30000) this.timeout(30000)
var keyPair = bitcoin.ECPair.fromWIF('cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA', regtest) var keyPair = bitcoin.ECPair.fromWIF('cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA', regtest)
var pubKey = keyPair.getPublicKeyBuffer() var pubKeyHash = bitcoin.crypto.hash160(keyPair.publicKey)
var pubKeyHash = bitcoin.crypto.hash160(pubKey)
var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash) var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash)
var redeemScriptHash = bitcoin.crypto.hash160(redeemScript) var redeemScriptHash = bitcoin.crypto.hash160(redeemScript)
@ -191,7 +190,7 @@ describe('bitcoinjs-lib (transactions)', function () {
'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87KcLPVfXz', 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87KcLPVfXz',
'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87L7FgDCKE' 'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87L7FgDCKE'
].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) }) ].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) })
var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() }) var pubKeys = keyPairs.map(function (x) { return x.publicKey })
var witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys) var witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys)
var redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript)) var redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript))
@ -230,7 +229,7 @@ describe('bitcoinjs-lib (transactions)', function () {
'032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d', '032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d',
'0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a', '0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a',
'039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f' '039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f'
].map(function (q) { return bitcoin.ECPair.fromPublicKeyBuffer(Buffer.from(q, 'hex')) }) ].map(function (q) { return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')) })
var tx = bitcoin.Transaction.fromHex(txHex) var tx = bitcoin.Transaction.fromHex(txHex)
@ -241,7 +240,7 @@ describe('bitcoinjs-lib (transactions)', function () {
var ss = bitcoin.script.signature.decode(scriptSig.signature) var ss = bitcoin.script.signature.decode(scriptSig.signature)
var hash = tx.hashForSignature(i, prevOutScript, ss.hashType) var hash = tx.hashForSignature(i, prevOutScript, ss.hashType)
assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.getPublicKeyBuffer().toString('hex')) assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.publicKey.toString('hex'))
assert.strictEqual(keyPair.verify(hash, ss.signature), true) assert.strictEqual(keyPair.verify(hash, ss.signature), true)
}) })
}) })

View file

@ -7,7 +7,6 @@ let bscript = require('../src/script')
let btemplates = require('../src/templates') let btemplates = require('../src/templates')
let ops = require('bitcoin-ops') let ops = require('bitcoin-ops')
let BigInteger = require('bigi')
let ECPair = require('../src/ecpair') let ECPair = require('../src/ecpair')
let Transaction = require('../src/transaction') let Transaction = require('../src/transaction')
let TransactionBuilder = require('../src/transaction_builder') let TransactionBuilder = require('../src/transaction_builder')
@ -17,7 +16,7 @@ let fixtures = require('./fixtures/transaction_builder')
// TODO: remove // TODO: remove
function getAddress (node) { function getAddress (node) {
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), NETWORKS.bitcoin.pubKeyHash) return baddress.toBase58Check(bcrypto.hash160(node.publicKey), NETWORKS.bitcoin.pubKeyHash)
} }
function construct (f, dontSign) { function construct (f, dontSign) {
@ -89,7 +88,7 @@ function construct (f, dontSign) {
describe('TransactionBuilder', function () { describe('TransactionBuilder', function () {
// constants // constants
var keyPair = new ECPair(BigInteger.ONE) var keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'))
var scripts = [ var scripts = [
'1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH',
'1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP' '1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP'

View file

@ -5,18 +5,6 @@ var types = require('../src/types')
var typeforce = require('typeforce') var typeforce = require('typeforce')
describe('types', function () { describe('types', function () {
describe('BigInt/ECPoint', function () {
it('return true for duck types', function () {
assert(types.BigInt(new function BigInteger () {}()))
assert(types.ECPoint(new function Point () {}()))
})
it('return false for bad types', function () {
assert(!types.BigInt(new function NotABigInteger () {}()))
assert(!types.ECPoint(new function NotAPoint () {}()))
})
})
describe('Buffer Hash160/Hash256', function () { describe('Buffer Hash160/Hash256', function () {
var buffer20byte = Buffer.alloc(20) var buffer20byte = Buffer.alloc(20)
var buffer32byte = Buffer.alloc(32) var buffer32byte = Buffer.alloc(32)