Merge pull request #1070 from bitcoinjs/tinyec
rm ecdsa, switch out ECPair secp256k1 impl.
This commit is contained in:
commit
d7eb6c8e77
18 changed files with 225 additions and 578 deletions
|
@ -31,14 +31,12 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"bech32": "^1.1.2",
|
||||
"bigi": "^1.4.0",
|
||||
"bip32": "^0.1.0",
|
||||
"bip66": "^1.1.0",
|
||||
"bitcoin-ops": "^1.4.0",
|
||||
"bs58check": "^2.0.0",
|
||||
"create-hash": "^1.1.0",
|
||||
"create-hmac": "^1.1.3",
|
||||
"ecurve": "^1.0.0",
|
||||
"merkle-lib": "^2.0.10",
|
||||
"pushdata-bitcoin": "^1.0.1",
|
||||
"randombytes": "^2.0.1",
|
||||
|
|
163
src/ecdsa.js
163
src/ecdsa.js
|
@ -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
|
||||
}
|
142
src/ecpair.js
142
src/ecpair.js
|
@ -1,63 +1,70 @@
|
|||
let ecdsa = require('./ecdsa')
|
||||
let ecc = require('tiny-secp256k1')
|
||||
let randomBytes = require('randombytes')
|
||||
let typeforce = require('typeforce')
|
||||
let types = require('./types')
|
||||
let wif = require('wif')
|
||||
|
||||
let NETWORKS = require('./networks')
|
||||
let BigInteger = require('bigi')
|
||||
|
||||
let ecurve = require('ecurve')
|
||||
let secp256k1 = ecdsa.__curve
|
||||
|
||||
function ECPair (d, Q, options) {
|
||||
if (options) {
|
||||
typeforce({
|
||||
// TODO: why is the function name toJSON weird?
|
||||
function isPoint (x) { return ecc.isPoint(x) }
|
||||
let isOptions = typeforce.maybe(typeforce.compile({
|
||||
compressed: types.maybe(types.Boolean),
|
||||
network: types.maybe(types.Network)
|
||||
}, options)
|
||||
}
|
||||
}))
|
||||
|
||||
function ECPair (d, Q, options) {
|
||||
options = options || {}
|
||||
|
||||
if (d) {
|
||||
if (d.signum() <= 0) throw new Error('Private key must be greater than 0')
|
||||
if (d.compareTo(secp256k1.n) >= 0) throw new Error('Private key must be less than the curve order')
|
||||
if (Q) throw new TypeError('Unexpected publicKey parameter')
|
||||
|
||||
this.d = d
|
||||
} else {
|
||||
typeforce(types.ECPoint, Q)
|
||||
|
||||
this.__Q = Q
|
||||
}
|
||||
|
||||
this.compressed = options.compressed === undefined ? true : options.compressed
|
||||
this.network = options.network || NETWORKS.bitcoin
|
||||
|
||||
this.__d = d || null
|
||||
this.__Q = null
|
||||
if (Q) this.__Q = ecc.pointCompress(Q, this.compressed)
|
||||
}
|
||||
|
||||
Object.defineProperty(ECPair.prototype, 'Q', {
|
||||
get: function () {
|
||||
if (!this.__Q && this.d) {
|
||||
this.__Q = secp256k1.G.multiply(this.d)
|
||||
}
|
||||
Object.defineProperty(ECPair.prototype, 'privateKey', {
|
||||
enumerable: false,
|
||||
get: function () { return this.__d }
|
||||
})
|
||||
|
||||
Object.defineProperty(ECPair.prototype, 'publicKey', { get: function () {
|
||||
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed)
|
||||
return this.__Q
|
||||
}
|
||||
})
|
||||
}})
|
||||
|
||||
ECPair.fromPublicKeyBuffer = function (buffer, network) {
|
||||
var Q = ecurve.Point.decodeFrom(secp256k1, buffer)
|
||||
|
||||
return new ECPair(null, Q, {
|
||||
compressed: Q.compressed,
|
||||
network: network
|
||||
})
|
||||
ECPair.prototype.toWIF = function () {
|
||||
if (!this.__d) throw new Error('Missing private key')
|
||||
return wif.encode(this.network.wif, this.__d, this.compressed)
|
||||
}
|
||||
|
||||
ECPair.fromWIF = function (string, network) {
|
||||
var decoded = wif.decode(string)
|
||||
var version = decoded.version
|
||||
ECPair.prototype.sign = function (hash) {
|
||||
if (!this.__d) throw new Error('Missing private key')
|
||||
return ecc.sign(hash, this.__d)
|
||||
}
|
||||
|
||||
ECPair.prototype.verify = function (hash, signature) {
|
||||
return ecc.verify(hash, this.publicKey, signature)
|
||||
}
|
||||
|
||||
function fromPrivateKey (buffer, options) {
|
||||
typeforce(types.Buffer256bit, buffer)
|
||||
if (!ecc.isPrivate(buffer)) throw new TypeError('Private key not in range [1, n)')
|
||||
typeforce(isOptions, options)
|
||||
|
||||
return new ECPair(buffer, null, options)
|
||||
}
|
||||
|
||||
function fromPublicKey (buffer, options) {
|
||||
typeforce(isPoint, buffer)
|
||||
typeforce(isOptions, options)
|
||||
return new ECPair(null, buffer, options)
|
||||
}
|
||||
|
||||
function fromWIF (string, network) {
|
||||
let decoded = wif.decode(string)
|
||||
let version = decoded.version
|
||||
|
||||
// list of networks?
|
||||
if (types.Array(network)) {
|
||||
|
@ -74,58 +81,29 @@ ECPair.fromWIF = function (string, network) {
|
|||
if (version !== network.wif) throw new Error('Invalid network version')
|
||||
}
|
||||
|
||||
var d = BigInteger.fromBuffer(decoded.privateKey)
|
||||
|
||||
return new ECPair(d, null, {
|
||||
return fromPrivateKey(decoded.privateKey, {
|
||||
compressed: decoded.compressed,
|
||||
network: network
|
||||
})
|
||||
}
|
||||
|
||||
ECPair.makeRandom = function (options) {
|
||||
function makeRandom (options) {
|
||||
typeforce(isOptions, options)
|
||||
options = options || {}
|
||||
let rng = options.rng || randomBytes
|
||||
|
||||
var rng = options.rng || randomBytes
|
||||
|
||||
var d
|
||||
let d
|
||||
do {
|
||||
var buffer = rng(32)
|
||||
typeforce(types.Buffer256bit, buffer)
|
||||
d = rng(32)
|
||||
typeforce(types.Buffer256bit, d)
|
||||
} while (!ecc.isPrivate(d))
|
||||
|
||||
d = BigInteger.fromBuffer(buffer)
|
||||
} while (d.signum() <= 0 || d.compareTo(secp256k1.n) >= 0)
|
||||
|
||||
return new ECPair(d, null, options)
|
||||
return fromPrivateKey(d, options)
|
||||
}
|
||||
|
||||
ECPair.prototype.getNetwork = function () {
|
||||
return this.network
|
||||
module.exports = {
|
||||
makeRandom,
|
||||
fromPrivateKey,
|
||||
fromPublicKey,
|
||||
fromWIF
|
||||
}
|
||||
|
||||
ECPair.prototype.getPublicKeyBuffer = function () {
|
||||
return this.Q.getEncoded(this.compressed)
|
||||
}
|
||||
|
||||
ECPair.prototype.sign = function (hash) {
|
||||
if (!this.d) throw new Error('Missing private key')
|
||||
|
||||
let signature = ecdsa.sign(hash, this.d)
|
||||
return Buffer.concat([signature.r.toBuffer(32), signature.s.toBuffer(32)], 64)
|
||||
}
|
||||
|
||||
ECPair.prototype.toWIF = function () {
|
||||
if (!this.d) throw new Error('Missing private key')
|
||||
|
||||
return wif.encode(this.network.wif, this.d.toBuffer(32), this.compressed)
|
||||
}
|
||||
|
||||
ECPair.prototype.verify = function (hash, signature) {
|
||||
signature = {
|
||||
r: BigInteger.fromBuffer(signature.slice(0, 32)),
|
||||
s: BigInteger.fromBuffer(signature.slice(32, 64))
|
||||
}
|
||||
|
||||
return ecdsa.verify(hash, signature, this.Q)
|
||||
}
|
||||
|
||||
module.exports = ECPair
|
||||
|
|
|
@ -180,7 +180,7 @@ function fixMultisigOrder (input, transaction, vin) {
|
|||
var unmatched = input.signatures.concat()
|
||||
|
||||
input.signatures = input.pubKeys.map(function (pubKey) {
|
||||
var keyPair = ECPair.fromPublicKeyBuffer(pubKey)
|
||||
var keyPair = ECPair.fromPublicKey(pubKey)
|
||||
var match
|
||||
|
||||
// check for a signature
|
||||
|
@ -686,7 +686,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
|
|||
throw new Error('Inconsistent redeemScript')
|
||||
}
|
||||
|
||||
var kpPubKey = keyPair.publicKey || keyPair.getPublicKeyBuffer()
|
||||
var kpPubKey = keyPair.publicKey || keyPair.getPublicKey()
|
||||
if (!canSign(input)) {
|
||||
if (witnessValue !== undefined) {
|
||||
if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue')
|
||||
|
|
|
@ -16,11 +16,9 @@ function Satoshi (value) {
|
|||
}
|
||||
|
||||
// external dependent types
|
||||
var BigInt = typeforce.quacksLike('BigInteger')
|
||||
var ECPoint = typeforce.quacksLike('Point')
|
||||
|
||||
// exposed, external API
|
||||
var ECSignature = typeforce.compile({ r: BigInt, s: BigInt })
|
||||
var Network = typeforce.compile({
|
||||
messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String),
|
||||
bip32: {
|
||||
|
@ -34,11 +32,9 @@ var Network = typeforce.compile({
|
|||
|
||||
// extend typeforce types with ours
|
||||
var types = {
|
||||
BigInt: BigInt,
|
||||
BIP32Path: BIP32Path,
|
||||
Buffer256bit: typeforce.BufferN(32),
|
||||
ECPoint: ECPoint,
|
||||
ECSignature: ECSignature,
|
||||
Hash160bit: typeforce.BufferN(20),
|
||||
Hash256bit: typeforce.BufferN(32),
|
||||
Network: Network,
|
||||
|
|
|
@ -94,7 +94,7 @@ describe('Bitcoin-core', function () {
|
|||
var keyPair = bitcoin.ECPair.fromWIF(string, network)
|
||||
|
||||
it('fromWIF imports ' + string, function () {
|
||||
assert.strictEqual(keyPair.d.toHex(), hex)
|
||||
assert.strictEqual(keyPair.privateKey.toString('hex'), hex)
|
||||
assert.strictEqual(keyPair.compressed, params.isCompressed)
|
||||
})
|
||||
|
||||
|
|
135
test/ecdsa.js
135
test/ecdsa.js
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
172
test/ecpair.js
172
test/ecpair.js
|
@ -1,34 +1,36 @@
|
|||
/* global describe, it, beforeEach */
|
||||
/* eslint-disable no-new */
|
||||
|
||||
var assert = require('assert')
|
||||
var ecdsa = require('../src/ecdsa')
|
||||
var ecurve = require('ecurve')
|
||||
var proxyquire = require('proxyquire')
|
||||
var hoodwink = require('hoodwink')
|
||||
let assert = require('assert')
|
||||
let proxyquire = require('proxyquire')
|
||||
let hoodwink = require('hoodwink')
|
||||
|
||||
var BigInteger = require('bigi')
|
||||
var ECPair = require('../src/ecpair')
|
||||
let ECPair = require('../src/ecpair')
|
||||
let tinysecp = require('tiny-secp256k1')
|
||||
|
||||
var fixtures = require('./fixtures/ecpair.json')
|
||||
var curve = ecdsa.__curve
|
||||
let fixtures = require('./fixtures/ecpair.json')
|
||||
|
||||
var NETWORKS = require('../src/networks')
|
||||
var NETWORKS_LIST = [] // Object.values(NETWORKS)
|
||||
for (var networkName in NETWORKS) {
|
||||
let NETWORKS = require('../src/networks')
|
||||
let NETWORKS_LIST = [] // Object.values(NETWORKS)
|
||||
for (let networkName in NETWORKS) {
|
||||
NETWORKS_LIST.push(NETWORKS[networkName])
|
||||
}
|
||||
|
||||
let ZERO = Buffer.alloc(32, 0)
|
||||
let ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex')
|
||||
let GROUP_ORDER = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 'hex')
|
||||
let GROUP_ORDER_LESS_1 = Buffer.from('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 'hex')
|
||||
|
||||
describe('ECPair', function () {
|
||||
describe('constructor', function () {
|
||||
it('defaults to compressed', function () {
|
||||
var keyPair = new ECPair(BigInteger.ONE)
|
||||
let keyPair = ECPair.fromPrivateKey(ONE)
|
||||
|
||||
assert.strictEqual(keyPair.compressed, true)
|
||||
})
|
||||
|
||||
it('supports the uncompressed option', function () {
|
||||
var keyPair = new ECPair(BigInteger.ONE, null, {
|
||||
let keyPair = ECPair.fromPrivateKey(ONE, {
|
||||
compressed: false
|
||||
})
|
||||
|
||||
|
@ -36,7 +38,7 @@ describe('ECPair', function () {
|
|||
})
|
||||
|
||||
it('supports the network option', function () {
|
||||
var keyPair = new ECPair(BigInteger.ONE, null, {
|
||||
let keyPair = ECPair.fromPrivateKey(ONE, {
|
||||
compressed: false,
|
||||
network: NETWORKS.testnet
|
||||
})
|
||||
|
@ -45,51 +47,58 @@ describe('ECPair', function () {
|
|||
})
|
||||
|
||||
fixtures.valid.forEach(function (f) {
|
||||
it('calculates the public point for ' + f.WIF, function () {
|
||||
var d = new BigInteger(f.d)
|
||||
var keyPair = new ECPair(d, null, {
|
||||
it('derives public key for ' + f.WIF, function () {
|
||||
let d = Buffer.from(f.d, 'hex')
|
||||
console.log(d)
|
||||
|
||||
let keyPair = ECPair.fromPrivateKey(d, {
|
||||
compressed: f.compressed
|
||||
})
|
||||
|
||||
assert.strictEqual(keyPair.getPublicKeyBuffer().toString('hex'), f.Q)
|
||||
assert.strictEqual(keyPair.publicKey.toString('hex'), f.Q)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.constructor.forEach(function (f) {
|
||||
it('throws ' + f.exception, function () {
|
||||
var d = f.d && new BigInteger(f.d)
|
||||
var Q = f.Q && ecurve.Point.decodeFrom(curve, Buffer.from(f.Q, 'hex'))
|
||||
|
||||
if (f.d) {
|
||||
let d = Buffer.from(f.d, 'hex')
|
||||
assert.throws(function () {
|
||||
new ECPair(d, Q, f.options)
|
||||
ECPair.fromPrivateKey(d, f.options)
|
||||
}, new RegExp(f.exception))
|
||||
} else {
|
||||
let Q = Buffer.from(f.Q, 'hex')
|
||||
assert.throws(function () {
|
||||
ECPair.fromPublicKey(Q, f.options)
|
||||
}, new RegExp(f.exception))
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPublicKeyBuffer', function () {
|
||||
var keyPair
|
||||
describe('getPublicKey', function () {
|
||||
let keyPair
|
||||
|
||||
beforeEach(function () {
|
||||
keyPair = new ECPair(BigInteger.ONE)
|
||||
keyPair = ECPair.fromPrivateKey(ONE)
|
||||
})
|
||||
|
||||
it('wraps Q.getEncoded', hoodwink(function () {
|
||||
this.mock(keyPair.Q, 'getEncoded', function (compressed) {
|
||||
assert.strictEqual(compressed, keyPair.compressed)
|
||||
}, 1)
|
||||
it('calls pointFromScalar lazily', hoodwink(function () {
|
||||
assert.strictEqual(keyPair.__Q, null)
|
||||
|
||||
keyPair.getPublicKeyBuffer()
|
||||
// .publicKey forces the memoization
|
||||
assert.strictEqual(keyPair.publicKey.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
|
||||
assert.strictEqual(keyPair.__Q.toString('hex'), '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
|
||||
}))
|
||||
})
|
||||
|
||||
describe('fromWIF', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
it('imports ' + f.WIF + ' (' + f.network + ')', function () {
|
||||
var network = NETWORKS[f.network]
|
||||
var keyPair = ECPair.fromWIF(f.WIF, network)
|
||||
let network = NETWORKS[f.network]
|
||||
let keyPair = ECPair.fromWIF(f.WIF, network)
|
||||
|
||||
assert.strictEqual(keyPair.d.toString(), f.d)
|
||||
assert.strictEqual(keyPair.privateKey.toString('hex'), f.d)
|
||||
assert.strictEqual(keyPair.compressed, f.compressed)
|
||||
assert.strictEqual(keyPair.network, network)
|
||||
})
|
||||
|
@ -97,9 +106,9 @@ describe('ECPair', function () {
|
|||
|
||||
fixtures.valid.forEach(function (f) {
|
||||
it('imports ' + f.WIF + ' (via list of networks)', function () {
|
||||
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
||||
let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
||||
|
||||
assert.strictEqual(keyPair.d.toString(), f.d)
|
||||
assert.strictEqual(keyPair.privateKey.toString('hex'), f.d)
|
||||
assert.strictEqual(keyPair.compressed, f.compressed)
|
||||
assert.strictEqual(keyPair.network, NETWORKS[f.network])
|
||||
})
|
||||
|
@ -108,7 +117,7 @@ describe('ECPair', function () {
|
|||
fixtures.invalid.fromWIF.forEach(function (f) {
|
||||
it('throws on ' + f.WIF, function () {
|
||||
assert.throws(function () {
|
||||
var networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST
|
||||
let networks = f.network ? NETWORKS[f.network] : NETWORKS_LIST
|
||||
|
||||
ECPair.fromWIF(f.WIF, networks)
|
||||
}, new RegExp(f.exception))
|
||||
|
@ -119,30 +128,29 @@ describe('ECPair', function () {
|
|||
describe('toWIF', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
it('exports ' + f.WIF, function () {
|
||||
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
||||
var result = keyPair.toWIF()
|
||||
|
||||
let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
||||
let result = keyPair.toWIF()
|
||||
assert.strictEqual(result, f.WIF)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('makeRandom', function () {
|
||||
var d = Buffer.from('0404040404040404040404040404040404040404040404040404040404040404', 'hex')
|
||||
var exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv'
|
||||
let d = Buffer.alloc(32, 4)
|
||||
let exWIF = 'KwMWvwRJeFqxYyhZgNwYuYjbQENDAPAudQx5VEmKJrUZcq6aL2pv'
|
||||
|
||||
describe('uses randombytes RNG', function () {
|
||||
it('generates a ECPair', function () {
|
||||
var stub = { randombytes: function () { return d } }
|
||||
var ProxiedECPair = proxyquire('../src/ecpair', stub)
|
||||
let stub = { randombytes: function () { return d } }
|
||||
let ProxiedECPair = proxyquire('../src/ecpair', stub)
|
||||
|
||||
var keyPair = ProxiedECPair.makeRandom()
|
||||
let keyPair = ProxiedECPair.makeRandom()
|
||||
assert.strictEqual(keyPair.toWIF(), exWIF)
|
||||
})
|
||||
})
|
||||
|
||||
it('allows a custom RNG to be used', function () {
|
||||
var keyPair = ECPair.makeRandom({
|
||||
let keyPair = ECPair.makeRandom({
|
||||
rng: function (size) { return d.slice(0, size) }
|
||||
})
|
||||
|
||||
|
@ -150,14 +158,14 @@ describe('ECPair', function () {
|
|||
})
|
||||
|
||||
it('retains the same defaults as ECPair constructor', function () {
|
||||
var keyPair = ECPair.makeRandom()
|
||||
let keyPair = ECPair.makeRandom()
|
||||
|
||||
assert.strictEqual(keyPair.compressed, true)
|
||||
assert.strictEqual(keyPair.network, NETWORKS.bitcoin)
|
||||
})
|
||||
|
||||
it('supports the options parameter', function () {
|
||||
var keyPair = ECPair.makeRandom({
|
||||
let keyPair = ECPair.makeRandom({
|
||||
compressed: false,
|
||||
network: NETWORKS.testnet
|
||||
})
|
||||
|
@ -168,7 +176,7 @@ describe('ECPair', function () {
|
|||
|
||||
it('throws if d is bad length', function () {
|
||||
function rng () {
|
||||
return BigInteger.ZERO.toBuffer(28)
|
||||
return Buffer.alloc(28)
|
||||
}
|
||||
|
||||
assert.throws(function () {
|
||||
|
@ -176,58 +184,59 @@ describe('ECPair', function () {
|
|||
}, /Expected Buffer\(Length: 32\), got Buffer\(Length: 28\)/)
|
||||
})
|
||||
|
||||
it('loops until d is within interval [1, n - 1] : 1', hoodwink(function () {
|
||||
var rng = this.stub(function f () {
|
||||
if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // 0
|
||||
return BigInteger.ONE.toBuffer(32) // >0
|
||||
it('loops until d is within interval [1, n) : 1', hoodwink(function () {
|
||||
let rng = this.stub(function f () {
|
||||
if (f.calls === 0) return ZERO // 0
|
||||
return ONE // >0
|
||||
}, 2)
|
||||
|
||||
ECPair.makeRandom({ rng: rng })
|
||||
}))
|
||||
|
||||
it('loops until d is within interval [1, n - 1] : n - 1', hoodwink(function () {
|
||||
var rng = this.stub(function f () {
|
||||
if (f.calls === 0) return BigInteger.ZERO.toBuffer(32) // <1
|
||||
if (f.calls === 1) return curve.n.toBuffer(32) // >n-1
|
||||
return curve.n.subtract(BigInteger.ONE).toBuffer(32) // n-1
|
||||
it('loops until d is within interval [1, n) : n - 1', hoodwink(function () {
|
||||
let rng = this.stub(function f () {
|
||||
if (f.calls === 0) return ZERO // <1
|
||||
if (f.calls === 1) return GROUP_ORDER // >n-1
|
||||
return GROUP_ORDER_LESS_1 // n-1
|
||||
}, 3)
|
||||
|
||||
ECPair.makeRandom({ rng: rng })
|
||||
}))
|
||||
})
|
||||
|
||||
describe('getNetwork', function () {
|
||||
describe('.network', function () {
|
||||
fixtures.valid.forEach(function (f) {
|
||||
it('returns ' + f.network + ' for ' + f.WIF, function () {
|
||||
var network = NETWORKS[f.network]
|
||||
var keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
||||
let network = NETWORKS[f.network]
|
||||
let keyPair = ECPair.fromWIF(f.WIF, NETWORKS_LIST)
|
||||
|
||||
assert.strictEqual(keyPair.getNetwork(), network)
|
||||
assert.strictEqual(keyPair.network, network)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ecdsa wrappers', function () {
|
||||
var keyPair, hash
|
||||
describe('tinysecp wrappers', function () {
|
||||
let keyPair, hash, signature
|
||||
|
||||
beforeEach(function () {
|
||||
keyPair = ECPair.makeRandom()
|
||||
hash = Buffer.alloc(32)
|
||||
hash = ZERO
|
||||
signature = Buffer.alloc(64, 1)
|
||||
})
|
||||
|
||||
describe('signing', function () {
|
||||
it('wraps ecdsa.sign', hoodwink(function () {
|
||||
this.mock(ecdsa, 'sign', function (h, d) {
|
||||
it('wraps tinysecp.sign', hoodwink(function () {
|
||||
this.mock(tinysecp, 'sign', function (h, d) {
|
||||
assert.strictEqual(h, hash)
|
||||
assert.strictEqual(d, keyPair.d)
|
||||
return { r: BigInteger.ONE, s: BigInteger.ONE }
|
||||
assert.strictEqual(d, keyPair.privateKey)
|
||||
return signature
|
||||
}, 1)
|
||||
|
||||
keyPair.sign(hash)
|
||||
assert.strictEqual(keyPair.sign(hash), signature)
|
||||
}))
|
||||
|
||||
it('throws if no private key is found', function () {
|
||||
keyPair.d = null
|
||||
delete keyPair.__d
|
||||
|
||||
assert.throws(function () {
|
||||
keyPair.sign(hash)
|
||||
|
@ -236,24 +245,15 @@ describe('ECPair', function () {
|
|||
})
|
||||
|
||||
describe('verify', function () {
|
||||
var signature
|
||||
|
||||
beforeEach(function () {
|
||||
signature = keyPair.sign(hash)
|
||||
})
|
||||
|
||||
it('wraps ecdsa.verify', hoodwink(function () {
|
||||
this.mock(ecdsa, 'verify', function (h, s, q) {
|
||||
it('wraps tinysecp.verify', hoodwink(function () {
|
||||
this.mock(tinysecp, 'verify', function (h, q, s) {
|
||||
assert.strictEqual(h, hash)
|
||||
// assert.strictEqual(s, signature)
|
||||
assert.deepEqual(s, {
|
||||
r: BigInteger.fromBuffer(signature.slice(0, 32)),
|
||||
s: BigInteger.fromBuffer(signature.slice(32, 64))
|
||||
})
|
||||
assert.strictEqual(q, keyPair.Q)
|
||||
assert.strictEqual(q, keyPair.publicKey)
|
||||
assert.strictEqual(s, signature)
|
||||
return true
|
||||
}, 1)
|
||||
|
||||
keyPair.verify(hash, signature)
|
||||
assert.strictEqual(keyPair.verify(hash, signature), true)
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
|
41
test/fixtures/ecpair.json
vendored
41
test/fixtures/ecpair.json
vendored
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"valid": [
|
||||
{
|
||||
"d": "1",
|
||||
"d": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
"compressed": true,
|
||||
"network": "bitcoin",
|
||||
|
@ -9,7 +9,7 @@
|
|||
"WIF": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn"
|
||||
},
|
||||
{
|
||||
"d": "1",
|
||||
"d": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"Q": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
|
||||
"compressed": false,
|
||||
"network": "bitcoin",
|
||||
|
@ -17,7 +17,7 @@
|
|||
"WIF": "5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf"
|
||||
},
|
||||
{
|
||||
"d": "19898843618908353587043383062236220484949425084007183071220218307100305431102",
|
||||
"d": "2bfe58ab6d9fd575bdc3a624e4825dd2b375d64ac033fbc46ea79dbab4f69a3e",
|
||||
"Q": "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340",
|
||||
"compressed": true,
|
||||
"network": "bitcoin",
|
||||
|
@ -25,7 +25,7 @@
|
|||
"WIF": "KxhEDBQyyEFymvfJD96q8stMbJMbZUb6D1PmXqBWZDU2WvbvVs9o"
|
||||
},
|
||||
{
|
||||
"d": "48968302285117906840285529799176770990048954789747953886390402978935544927851",
|
||||
"d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b",
|
||||
"Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34",
|
||||
"compressed": true,
|
||||
"network": "bitcoin",
|
||||
|
@ -33,7 +33,7 @@
|
|||
"WIF": "KzrA86mCVMGWnLGBQu9yzQa32qbxb5dvSK4XhyjjGAWSBKYX4rHx"
|
||||
},
|
||||
{
|
||||
"d": "48968302285117906840285529799176770990048954789747953886390402978935544927851",
|
||||
"d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b",
|
||||
"Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6",
|
||||
"compressed": false,
|
||||
"network": "bitcoin",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"WIF": "5JdxzLtFPHNe7CAL8EBC6krdFv9pwPoRo4e3syMZEQT9srmK8hh"
|
||||
},
|
||||
{
|
||||
"d": "48968302285117906840285529799176770990048954789747953886390402978935544927851",
|
||||
"d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b",
|
||||
"Q": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34",
|
||||
"compressed": true,
|
||||
"network": "testnet",
|
||||
|
@ -49,7 +49,7 @@
|
|||
"WIF": "cRD9b1m3vQxmwmjSoJy7Mj56f4uNFXjcWMCzpQCEmHASS4edEwXv"
|
||||
},
|
||||
{
|
||||
"d": "48968302285117906840285529799176770990048954789747953886390402978935544927851",
|
||||
"d": "6c4313b03f2e7324d75e642f0ab81b734b724e13fec930f309e222470236d66b",
|
||||
"Q": "044289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34cec320a0565fb7caf11b1ca2f445f9b7b012dda5718b3cface369ee3a034ded6",
|
||||
"compressed": false,
|
||||
"network": "testnet",
|
||||
|
@ -57,7 +57,7 @@
|
|||
"WIF": "92Qba5hnyWSn5Ffcka56yMQauaWY6ZLd91Vzxbi4a9CCetaHtYj"
|
||||
},
|
||||
{
|
||||
"d": "115792089237316195423570985008687907852837564279074904382605163141518161494336",
|
||||
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
|
||||
"Q": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
|
||||
"compressed": true,
|
||||
"network": "bitcoin",
|
||||
|
@ -68,36 +68,27 @@
|
|||
"invalid": {
|
||||
"constructor": [
|
||||
{
|
||||
"exception": "Private key must be greater than 0",
|
||||
"d": "-1"
|
||||
"exception": "Private key not in range \\[1, n\\)",
|
||||
"d": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
{
|
||||
"exception": "Private key must be greater than 0",
|
||||
"d": "0"
|
||||
"exception": "Private key not in range \\[1, n\\)",
|
||||
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"
|
||||
},
|
||||
{
|
||||
"exception": "Private key must be less than the curve order",
|
||||
"d": "115792089237316195423570985008687907852837564279074904382605163141518161494337"
|
||||
},
|
||||
{
|
||||
"exception": "Private key must be less than the curve order",
|
||||
"d": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
|
||||
"exception": "Private key not in range \\[1, n\\)",
|
||||
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142"
|
||||
},
|
||||
{
|
||||
"exception": "Expected property \"compressed\" of type \\?Boolean, got Number 2",
|
||||
"d": "1",
|
||||
"d": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"options": {
|
||||
"compressed": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"exception": "Unexpected publicKey parameter",
|
||||
"d": "1",
|
||||
"Q": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
|
||||
},
|
||||
{
|
||||
"exception": "Expected property \"network.messagePrefix\" of type Buffer|String, got undefined",
|
||||
"d": "1",
|
||||
"d": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"options": {
|
||||
"network": {}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ let baddress = bitcoin.address
|
|||
let bcrypto = bitcoin.crypto
|
||||
function getAddress (node, network) {
|
||||
network = network || bitcoin.networks.bitcoin
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash)
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash)
|
||||
}
|
||||
|
||||
function randomAddress () {
|
||||
|
|
|
@ -12,7 +12,7 @@ let baddress = bitcoin.address
|
|||
let bcrypto = bitcoin.crypto
|
||||
function getAddress (node, network) {
|
||||
network = network || bitcoin.networks.bitcoin
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash)
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash)
|
||||
}
|
||||
|
||||
describe('bitcoinjs-lib (addresses)', function () {
|
||||
|
@ -26,7 +26,7 @@ describe('bitcoinjs-lib (addresses)', function () {
|
|||
it('can generate an address from a SHA256 hash', function () {
|
||||
var hash = bitcoin.crypto.sha256(Buffer.from('correct horse battery staple'))
|
||||
|
||||
var keyPair = bitcoin.ECPair.makeRandom({ rng: () => hash })
|
||||
var keyPair = bitcoin.ECPair.fromPrivateKey(hash)
|
||||
var address = getAddress(keyPair)
|
||||
|
||||
// Generating addresses from SHA256 hashes is not secure if the input to the hash function is predictable
|
||||
|
@ -57,9 +57,8 @@ describe('bitcoinjs-lib (addresses)', function () {
|
|||
|
||||
it('can generate a SegWit address', function () {
|
||||
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)
|
||||
|
||||
assert.strictEqual(address, 'bc1qt97wqg464zrhnx23upykca5annqvwkwujjglky')
|
||||
|
@ -67,9 +66,8 @@ describe('bitcoinjs-lib (addresses)', function () {
|
|||
|
||||
it('can generate a SegWit address (via P2SH)', function () {
|
||||
var keyPair = bitcoin.ECPair.fromWIF('Kxr9tQED9H44gCmp6HAdmemAzU3n84H3dGkuWTKvE23JgHMW8gct')
|
||||
var pubKey = keyPair.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 address = bitcoin.address.fromOutputScript(scriptPubKey)
|
||||
|
||||
|
|
|
@ -25,11 +25,11 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () {
|
|||
bitcoin.opcodes.OP_DROP,
|
||||
|
||||
bitcoin.opcodes.OP_ELSE,
|
||||
bQ.getPublicKeyBuffer(),
|
||||
bQ.publicKey,
|
||||
bitcoin.opcodes.OP_CHECKSIGVERIFY,
|
||||
bitcoin.opcodes.OP_ENDIF,
|
||||
|
||||
aQ.getPublicKeyBuffer(),
|
||||
aQ.publicKey,
|
||||
bitcoin.opcodes.OP_CHECKSIG
|
||||
])
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ describe('bitcoinjs-lib (crypto)', function () {
|
|||
assert(bitcoin.script.pubKeyHash.input.check(scriptChunks), 'Expected pubKeyHash script')
|
||||
var prevOutScript = bitcoin.address.toOutputScript('1ArJ9vRaQcoQ29mTWZH768AmRwzb6Zif1z')
|
||||
var scriptSignature = bitcoin.script.signature.decode(scriptChunks[0])
|
||||
var publicKey = bitcoin.ECPair.fromPublicKeyBuffer(scriptChunks[1])
|
||||
var publicKey = bitcoin.ECPair.fromPublicKey(scriptChunks[1])
|
||||
|
||||
var m = tx.hashForSignature(vin, prevOutScript, scriptSignature.hashType)
|
||||
assert(publicKey.verify(m, scriptSignature.signature), 'Invalid m')
|
||||
|
|
|
@ -26,11 +26,11 @@ describe('bitcoinjs-lib (transactions w/ CSV)', function () {
|
|||
bitcoin.opcodes.OP_DROP,
|
||||
|
||||
bitcoin.opcodes.OP_ELSE,
|
||||
bQ.getPublicKeyBuffer(),
|
||||
bQ.publicKey,
|
||||
bitcoin.opcodes.OP_CHECKSIGVERIFY,
|
||||
bitcoin.opcodes.OP_ENDIF,
|
||||
|
||||
aQ.getPublicKeyBuffer(),
|
||||
aQ.publicKey,
|
||||
bitcoin.opcodes.OP_CHECKSIG
|
||||
])
|
||||
}
|
||||
|
|
|
@ -1,74 +1,72 @@
|
|||
/* global describe, it */
|
||||
|
||||
let assert = require('assert')
|
||||
let bigi = require('bigi')
|
||||
let bitcoin = require('../../')
|
||||
|
||||
let ecurve = require('ecurve')
|
||||
let secp256k1 = ecurve.getCurveByName('secp256k1')
|
||||
let G = secp256k1.G
|
||||
let n = secp256k1.n
|
||||
let ecc = require('tiny-secp256k1')
|
||||
|
||||
// TODO: remove
|
||||
let baddress = bitcoin.address
|
||||
let bcrypto = bitcoin.crypto
|
||||
function getAddress (node) {
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), bitcoin.networks.bitcoin.pubKeyHash)
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.publicKey), bitcoin.networks.bitcoin.pubKeyHash)
|
||||
}
|
||||
|
||||
// vG = (dG \+ sha256(e * dG)G)
|
||||
function stealthSend (e, Q) {
|
||||
var eQ = Q.multiply(e) // shared secret
|
||||
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded()))
|
||||
var cG = G.multiply(c)
|
||||
var vG = new bitcoin.ECPair(null, Q.add(cG))
|
||||
var eQ = ecc.pointMultiply(Q, e, true) // shared secret
|
||||
var c = bitcoin.crypto.sha256(eQ)
|
||||
var Qc = ecc.pointAddScalar(Q, c)
|
||||
var vG = bitcoin.ECPair.fromPublicKey(Qc)
|
||||
|
||||
return vG
|
||||
}
|
||||
|
||||
// v = (d + sha256(eG * d))
|
||||
function stealthReceive (d, eG) {
|
||||
var eQ = eG.multiply(d) // shared secret
|
||||
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded()))
|
||||
var v = new bitcoin.ECPair(d.add(c).mod(n))
|
||||
var eQ = ecc.pointMultiply(eG, d) // shared secret
|
||||
var c = bitcoin.crypto.sha256(eQ)
|
||||
var dc = ecc.privateAdd(d, c)
|
||||
var v = bitcoin.ECPair.fromPrivateKey(dc)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// d = (v - sha256(e * dG))
|
||||
function stealthRecoverLeaked (v, e, Q) {
|
||||
var eQ = Q.multiply(e) // shared secret
|
||||
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded()))
|
||||
var d = new bitcoin.ECPair(v.subtract(c).mod(n))
|
||||
var eQ = ecc.pointMultiply(Q, e) // shared secret
|
||||
var c = bitcoin.crypto.sha256(eQ)
|
||||
var vc = ecc.privateSub(v, c)
|
||||
var d = bitcoin.ECPair.fromPrivateKey(vc)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// vG = (rG \+ sha256(e * dG)G)
|
||||
function stealthDualSend (e, R, Q) {
|
||||
var eQ = Q.multiply(e) // shared secret
|
||||
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded()))
|
||||
var cG = G.multiply(c)
|
||||
var vG = new bitcoin.ECPair(null, R.add(cG))
|
||||
var eQ = ecc.pointMultiply(Q, e) // shared secret
|
||||
var c = bitcoin.crypto.sha256(eQ)
|
||||
var Rc = ecc.pointAddScalar(R, c)
|
||||
var vG = bitcoin.ECPair.fromPublicKey(Rc)
|
||||
|
||||
return vG
|
||||
}
|
||||
|
||||
// vG = (rG \+ sha256(eG * d)G)
|
||||
function stealthDualScan (d, R, eG) {
|
||||
var eQ = eG.multiply(d) // shared secret
|
||||
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded()))
|
||||
var cG = G.multiply(c)
|
||||
var vG = new bitcoin.ECPair(null, R.add(cG))
|
||||
var eQ = ecc.pointMultiply(eG, d) // shared secret
|
||||
var c = bitcoin.crypto.sha256(eQ)
|
||||
var Rc = ecc.pointAddScalar(R, c)
|
||||
var vG = bitcoin.ECPair.fromPublicKey(Rc)
|
||||
|
||||
return vG
|
||||
}
|
||||
|
||||
// v = (r + sha256(eG * d))
|
||||
function stealthDualReceive (d, r, eG) {
|
||||
var eQ = eG.multiply(d) // shared secret
|
||||
var c = bigi.fromBuffer(bitcoin.crypto.sha256(eQ.getEncoded()))
|
||||
var v = new bitcoin.ECPair(r.add(c).mod(n))
|
||||
var eQ = ecc.pointMultiply(eG, d) // shared secret
|
||||
var c = bitcoin.crypto.sha256(eQ)
|
||||
var rc = ecc.privateAdd(r, c)
|
||||
var v = bitcoin.ECPair.fromPrivateKey(rc)
|
||||
|
||||
return v
|
||||
}
|
||||
|
@ -80,12 +78,12 @@ describe('bitcoinjs-lib (crypto)', function () {
|
|||
var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender
|
||||
|
||||
// ... recipient reveals public key (recipient.Q) to sender
|
||||
var forSender = stealthSend(nonce.d, recipient.Q)
|
||||
var forSender = stealthSend(nonce.privateKey, recipient.publicKey)
|
||||
assert.equal(getAddress(forSender), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE')
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
|
||||
|
||||
// ... sender reveals nonce public key (nonce.Q) to recipient
|
||||
var forRecipient = stealthReceive(recipient.d, nonce.Q)
|
||||
var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey)
|
||||
assert.equal(getAddress(forRecipient), '1CcZWwCpACJL3AxqoDbwEt4JgDFuTHUspE')
|
||||
assert.equal(forRecipient.toWIF(), 'L1yjUN3oYyCXV3LcsBrmxCNTa62bZKWCybxVJMvqjMmmfDE8yk7n')
|
||||
|
||||
|
@ -98,11 +96,11 @@ describe('bitcoinjs-lib (crypto)', function () {
|
|||
var nonce = bitcoin.ECPair.makeRandom() // private to sender
|
||||
|
||||
// ... recipient reveals public key (recipient.Q) to sender
|
||||
var forSender = stealthSend(nonce.d, recipient.Q)
|
||||
var forSender = stealthSend(nonce.privateKey, recipient.publicKey)
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
|
||||
|
||||
// ... sender reveals nonce public key (nonce.Q) to recipient
|
||||
var forRecipient = stealthReceive(recipient.d, nonce.Q)
|
||||
var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey)
|
||||
assert.doesNotThrow(function () { forRecipient.toWIF() })
|
||||
|
||||
// sender and recipient, both derived same address
|
||||
|
@ -114,15 +112,15 @@ describe('bitcoinjs-lib (crypto)', function () {
|
|||
var nonce = bitcoin.ECPair.makeRandom() // private to sender
|
||||
|
||||
// ... recipient reveals public key (recipient.Q) to sender
|
||||
var forSender = stealthSend(nonce.d, recipient.Q)
|
||||
var forSender = stealthSend(nonce.privateKey, recipient.publicKey)
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
|
||||
|
||||
// ... sender reveals nonce public key (nonce.Q) to recipient
|
||||
var forRecipient = stealthReceive(recipient.d, nonce.Q)
|
||||
var forRecipient = stealthReceive(recipient.privateKey, nonce.publicKey)
|
||||
assert.doesNotThrow(function () { forRecipient.toWIF() })
|
||||
|
||||
// ... recipient accidentally leaks forRecipient.d on the blockchain
|
||||
var leaked = stealthRecoverLeaked(forRecipient.d, nonce.d, recipient.Q)
|
||||
var leaked = stealthRecoverLeaked(forRecipient.privateKey, nonce.privateKey, recipient.publicKey)
|
||||
assert.equal(leaked.toWIF(), recipient.toWIF())
|
||||
})
|
||||
|
||||
|
@ -133,15 +131,15 @@ describe('bitcoinjs-lib (crypto)', function () {
|
|||
var nonce = bitcoin.ECPair.fromWIF('KxVqB96pxbw1pokzQrZkQbLfVBjjHFfp2mFfEp8wuEyGenLFJhM9') // private to sender
|
||||
|
||||
// ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender
|
||||
var forSender = stealthDualSend(nonce.d, recipient.Q, scan.Q)
|
||||
var forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey)
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
|
||||
|
||||
// ... sender reveals nonce public key (nonce.Q) to scanner
|
||||
var forScanner = stealthDualScan(scan.d, recipient.Q, nonce.Q)
|
||||
var forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey)
|
||||
assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/)
|
||||
|
||||
// ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient
|
||||
var forRecipient = stealthDualReceive(scan.d, recipient.d, nonce.Q)
|
||||
var forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey)
|
||||
assert.doesNotThrow(function () { forRecipient.toWIF() })
|
||||
|
||||
// scanner, sender and recipient, all derived same address
|
||||
|
@ -155,15 +153,15 @@ describe('bitcoinjs-lib (crypto)', function () {
|
|||
var nonce = bitcoin.ECPair.makeRandom() // private to sender
|
||||
|
||||
// ... recipient reveals public key(s) (recipient.Q, scan.Q) to sender
|
||||
var forSender = stealthDualSend(nonce.d, recipient.Q, scan.Q)
|
||||
var forSender = stealthDualSend(nonce.privateKey, recipient.publicKey, scan.publicKey)
|
||||
assert.throws(function () { forSender.toWIF() }, /Error: Missing private key/)
|
||||
|
||||
// ... sender reveals nonce public key (nonce.Q) to scanner
|
||||
var forScanner = stealthDualScan(scan.d, recipient.Q, nonce.Q)
|
||||
var forScanner = stealthDualScan(scan.privateKey, recipient.publicKey, nonce.publicKey)
|
||||
assert.throws(function () { forScanner.toWIF() }, /Error: Missing private key/)
|
||||
|
||||
// ... scanner reveals relevant transaction + nonce public key (nonce.Q) to recipient
|
||||
var forRecipient = stealthDualReceive(scan.d, recipient.d, nonce.Q)
|
||||
var forRecipient = stealthDualReceive(scan.privateKey, recipient.privateKey, nonce.publicKey)
|
||||
assert.doesNotThrow(function () { forRecipient.toWIF() })
|
||||
|
||||
// scanner, sender and recipient, all derived same address
|
||||
|
|
|
@ -10,7 +10,7 @@ let baddress = bitcoin.address
|
|||
let bcrypto = bitcoin.crypto
|
||||
function getAddress (node, network) {
|
||||
network = network || bitcoin.networks.bitcoin
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), network.pubKeyHash)
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.publicKey), network.pubKeyHash)
|
||||
}
|
||||
|
||||
function rng () {
|
||||
|
@ -115,7 +115,7 @@ describe('bitcoinjs-lib (transactions)', function () {
|
|||
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx3cTMqe',
|
||||
'91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjJoQFacbgx9rcrL7'
|
||||
].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) })
|
||||
var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() })
|
||||
var pubKeys = keyPairs.map(function (x) { return x.publicKey })
|
||||
|
||||
var redeemScript = bitcoin.script.multisig.output.encode(2, pubKeys)
|
||||
var scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
|
||||
|
@ -150,8 +150,7 @@ describe('bitcoinjs-lib (transactions)', function () {
|
|||
this.timeout(30000)
|
||||
|
||||
var keyPair = bitcoin.ECPair.fromWIF('cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA', regtest)
|
||||
var pubKey = keyPair.getPublicKeyBuffer()
|
||||
var pubKeyHash = bitcoin.crypto.hash160(pubKey)
|
||||
var pubKeyHash = bitcoin.crypto.hash160(keyPair.publicKey)
|
||||
|
||||
var redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash)
|
||||
var redeemScriptHash = bitcoin.crypto.hash160(redeemScript)
|
||||
|
@ -191,7 +190,7 @@ describe('bitcoinjs-lib (transactions)', function () {
|
|||
'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87KcLPVfXz',
|
||||
'cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87L7FgDCKE'
|
||||
].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, regtest) })
|
||||
var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() })
|
||||
var pubKeys = keyPairs.map(function (x) { return x.publicKey })
|
||||
|
||||
var witnessScript = bitcoin.script.multisig.output.encode(3, pubKeys)
|
||||
var redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript))
|
||||
|
@ -230,7 +229,7 @@ describe('bitcoinjs-lib (transactions)', function () {
|
|||
'032b4c06c06c3ec0b7fa29519dfa5aae193ee2cc35ca127f29f14ec605d62fb63d',
|
||||
'0216c92abe433106491bdeb4a261226f20f5a4ac86220cc6e37655aac6bf3c1f2a',
|
||||
'039e05da8b8ea4f9868ecebb25998c7701542986233f4401799551fbecf316b18f'
|
||||
].map(function (q) { return bitcoin.ECPair.fromPublicKeyBuffer(Buffer.from(q, 'hex')) })
|
||||
].map(function (q) { return bitcoin.ECPair.fromPublicKey(Buffer.from(q, 'hex')) })
|
||||
|
||||
var tx = bitcoin.Transaction.fromHex(txHex)
|
||||
|
||||
|
@ -241,7 +240,7 @@ describe('bitcoinjs-lib (transactions)', function () {
|
|||
var ss = bitcoin.script.signature.decode(scriptSig.signature)
|
||||
var hash = tx.hashForSignature(i, prevOutScript, ss.hashType)
|
||||
|
||||
assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.getPublicKeyBuffer().toString('hex'))
|
||||
assert.strictEqual(scriptSig.pubKey.toString('hex'), keyPair.publicKey.toString('hex'))
|
||||
assert.strictEqual(keyPair.verify(hash, ss.signature), true)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -7,7 +7,6 @@ let bscript = require('../src/script')
|
|||
let btemplates = require('../src/templates')
|
||||
let ops = require('bitcoin-ops')
|
||||
|
||||
let BigInteger = require('bigi')
|
||||
let ECPair = require('../src/ecpair')
|
||||
let Transaction = require('../src/transaction')
|
||||
let TransactionBuilder = require('../src/transaction_builder')
|
||||
|
@ -17,7 +16,7 @@ let fixtures = require('./fixtures/transaction_builder')
|
|||
|
||||
// TODO: remove
|
||||
function getAddress (node) {
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.getPublicKeyBuffer()), NETWORKS.bitcoin.pubKeyHash)
|
||||
return baddress.toBase58Check(bcrypto.hash160(node.publicKey), NETWORKS.bitcoin.pubKeyHash)
|
||||
}
|
||||
|
||||
function construct (f, dontSign) {
|
||||
|
@ -89,7 +88,7 @@ function construct (f, dontSign) {
|
|||
|
||||
describe('TransactionBuilder', function () {
|
||||
// constants
|
||||
var keyPair = new ECPair(BigInteger.ONE)
|
||||
var keyPair = ECPair.fromPrivateKey(Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex'))
|
||||
var scripts = [
|
||||
'1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH',
|
||||
'1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP'
|
||||
|
|
|
@ -5,18 +5,6 @@ var types = require('../src/types')
|
|||
var typeforce = require('typeforce')
|
||||
|
||||
describe('types', function () {
|
||||
describe('BigInt/ECPoint', function () {
|
||||
it('return true for duck types', function () {
|
||||
assert(types.BigInt(new function BigInteger () {}()))
|
||||
assert(types.ECPoint(new function Point () {}()))
|
||||
})
|
||||
|
||||
it('return false for bad types', function () {
|
||||
assert(!types.BigInt(new function NotABigInteger () {}()))
|
||||
assert(!types.ECPoint(new function NotAPoint () {}()))
|
||||
})
|
||||
})
|
||||
|
||||
describe('Buffer Hash160/Hash256', function () {
|
||||
var buffer20byte = Buffer.alloc(20)
|
||||
var buffer32byte = Buffer.alloc(32)
|
||||
|
|
Loading…
Reference in a new issue