2018-05-22 16:33:43 +10:00
|
|
|
let ecc = require('tiny-secp256k1')
|
2018-05-22 17:43:25 +10:00
|
|
|
let randomBytes = require('randombytes')
|
|
|
|
let typeforce = require('typeforce')
|
|
|
|
let types = require('./types')
|
|
|
|
let wif = require('wif')
|
2014-10-17 13:31:01 +11:00
|
|
|
|
2018-05-22 17:43:25 +10:00
|
|
|
let NETWORKS = require('./networks')
|
2014-10-17 13:31:01 +11:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
// 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)
|
|
|
|
}))
|
2015-08-19 15:12:55 +10:00
|
|
|
|
2014-10-17 13:31:01 +11:00
|
|
|
function ECPair (d, Q, options) {
|
2015-08-19 15:12:55 +10:00
|
|
|
options = options || {}
|
2014-10-17 13:31:01 +11:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
this.compressed = options.compressed === undefined ? true : options.compressed
|
|
|
|
this.network = options.network || NETWORKS.bitcoin
|
2014-10-17 13:31:01 +11:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
this.__d = d || null
|
|
|
|
this.__Q = null
|
|
|
|
if (Q) this.__Q = ecc.pointCompress(Q, this.compressed)
|
|
|
|
}
|
2015-08-11 17:01:47 +10:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
ECPair.prototype.getNetwork = function () {
|
|
|
|
return this.network
|
|
|
|
}
|
2014-10-17 13:31:01 +11:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
ECPair.prototype.getPrivateKey = function () {
|
|
|
|
return this.__d
|
2014-10-17 13:31:01 +11:00
|
|
|
}
|
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
ECPair.prototype.getPublicKey = function () {
|
|
|
|
if (!this.__Q) this.__Q = ecc.pointFromScalar(this.__d, this.compressed)
|
|
|
|
return this.__Q
|
|
|
|
}
|
2015-03-19 13:25:41 +11:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
ECPair.prototype.toWIF = function () {
|
|
|
|
if (!this.__d) throw new Error('Missing private key')
|
|
|
|
return wif.encode(this.network.wif, this.__d, this.compressed)
|
|
|
|
}
|
2015-03-19 13:25:41 +11:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
ECPair.prototype.sign = function (hash) {
|
|
|
|
if (!this.__d) throw new Error('Missing private key')
|
|
|
|
return ecc.sign(hash, this.__d)
|
|
|
|
}
|
2014-10-17 13:31:01 +11:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
ECPair.prototype.verify = function (hash, signature) {
|
|
|
|
return ecc.verify(hash, this.getPublicKey(), signature)
|
2014-10-17 13:31:01 +11:00
|
|
|
}
|
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
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
|
2014-10-17 13:31:01 +11:00
|
|
|
|
2017-01-06 13:41:51 +11:00
|
|
|
// list of networks?
|
2015-08-20 20:16:57 +10:00
|
|
|
if (types.Array(network)) {
|
2017-01-06 13:41:51 +11:00
|
|
|
network = network.filter(function (x) {
|
|
|
|
return version === x.wif
|
2016-02-24 13:08:40 +03:00
|
|
|
}).pop()
|
2016-02-25 13:48:29 +11:00
|
|
|
|
|
|
|
if (!network) throw new Error('Unknown network version')
|
2016-04-27 17:05:33 +10:00
|
|
|
|
2017-01-06 13:41:51 +11:00
|
|
|
// otherwise, assume a network object (or default to bitcoin)
|
2016-04-27 18:04:35 +10:00
|
|
|
} else {
|
|
|
|
network = network || NETWORKS.bitcoin
|
|
|
|
|
2016-04-27 17:05:33 +10:00
|
|
|
if (version !== network.wif) throw new Error('Invalid network version')
|
2015-07-28 16:42:57 +10:00
|
|
|
}
|
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
return fromPrivateKey(decoded.privateKey, {
|
2015-08-20 20:16:57 +10:00
|
|
|
compressed: decoded.compressed,
|
2014-10-17 13:31:01 +11:00
|
|
|
network: network
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
function makeRandom (options) {
|
|
|
|
typeforce(isOptions, options)
|
2014-10-17 13:31:01 +11:00
|
|
|
options = options || {}
|
2018-05-22 16:33:43 +10:00
|
|
|
let rng = options.rng || randomBytes
|
2014-10-17 13:31:01 +11:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
let d
|
2015-08-21 16:46:18 +10:00
|
|
|
do {
|
2018-05-22 16:33:43 +10:00
|
|
|
d = rng(32)
|
|
|
|
typeforce(types.Buffer256bit, d)
|
|
|
|
} while (!ecc.isPrivate(d))
|
2014-10-17 13:31:01 +11:00
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
return fromPrivateKey(d, options)
|
2015-09-21 17:37:21 +10:00
|
|
|
}
|
|
|
|
|
2018-05-22 16:33:43 +10:00
|
|
|
module.exports = {
|
|
|
|
makeRandom,
|
|
|
|
fromPrivateKey,
|
|
|
|
fromPublicKey,
|
|
|
|
fromWIF
|
2014-10-17 13:31:01 +11:00
|
|
|
}
|