UNPKG

9.98 kBJavaScriptView Raw
1'use strict'
2
3var _ = require('./util/_')
4var Address = require('./address')
5var Base58Check = require('./encoding/base58check')
6var BN = require('./crypto/bn')
7var JSUtil = require('./util/js')
8var Networks = require('./networks')
9var Point = require('./crypto/point')
10var PublicKey = require('./publickey')
11var Random = require('./crypto/random')
12var $ = require('./util/preconditions')
13
14/**
15 * Instantiate a PrivateKey from a BN, Buffer or WIF string.
16 *
17 * @param {string} data - The encoded data in various formats
18 * @param {Network|string=} network - a {@link Network} object, or a string with the network name
19 * @returns {PrivateKey} A new valid instance of an PrivateKey
20 * @constructor
21 */
22function PrivateKey (data, network) {
23 if (!(this instanceof PrivateKey)) {
24 return new PrivateKey(data, network)
25 }
26 if (data instanceof PrivateKey) {
27 return data
28 }
29
30 var info = this._classifyArguments(data, network)
31
32 // validation
33 if (!info.bn || info.bn.cmp(new BN(0)) === 0) {
34 throw new TypeError('Number can not be equal to zero, undefined, null or false')
35 }
36 if (!info.bn.lt(Point.getN())) {
37 throw new TypeError('Number must be less than N')
38 }
39 if (typeof (info.network) === 'undefined') {
40 throw new TypeError('Must specify the network ("livenet" or "testnet")')
41 }
42
43 JSUtil.defineImmutable(this, {
44 bn: info.bn,
45 compressed: info.compressed,
46 network: info.network
47 })
48
49 Object.defineProperty(this, 'publicKey', {
50 configurable: false,
51 enumerable: true,
52 get: this.toPublicKey.bind(this)
53 })
54
55 return this
56};
57
58/**
59 * Internal helper to instantiate PrivateKey internal `info` object from
60 * different kinds of arguments passed to the constructor.
61 *
62 * @param {*} data
63 * @param {Network|string=} network - a {@link Network} object, or a string with the network name
64 * @return {Object}
65 */
66PrivateKey.prototype._classifyArguments = function (data, network) {
67 var info = {
68 compressed: true,
69 network: network ? Networks.get(network) : Networks.defaultNetwork
70 }
71
72 // detect type of data
73 if (_.isUndefined(data) || _.isNull(data)) {
74 info.bn = PrivateKey._getRandomBN()
75 } else if (data instanceof BN) {
76 info.bn = data
77 } else if (data instanceof Buffer || data instanceof Uint8Array) {
78 info = PrivateKey._transformBuffer(data, network)
79 } else if (data.bn && data.network) {
80 info = PrivateKey._transformObject(data)
81 } else if (!network && Networks.get(data)) {
82 info.bn = PrivateKey._getRandomBN()
83 info.network = Networks.get(data)
84 } else if (typeof (data) === 'string') {
85 if (JSUtil.isHexa(data)) {
86 info.bn = new BN(Buffer.from(data, 'hex'))
87 } else {
88 info = PrivateKey._transformWIF(data, network)
89 }
90 } else {
91 throw new TypeError('First argument is an unrecognized data type.')
92 }
93 return info
94}
95
96/**
97 * Internal function to get a random Big Number (BN)
98 *
99 * @returns {BN} A new randomly generated BN
100 * @private
101 */
102PrivateKey._getRandomBN = function () {
103 var condition
104 var bn
105 do {
106 var privbuf = Random.getRandomBuffer(32)
107 bn = BN.fromBuffer(privbuf)
108 condition = bn.lt(Point.getN())
109 } while (!condition)
110 return bn
111}
112
113/**
114 * Internal function to transform a WIF Buffer into a private key
115 *
116 * @param {Buffer} buf - An WIF string
117 * @param {Network|string=} network - a {@link Network} object, or a string with the network name
118 * @returns {Object} An object with keys: bn, network and compressed
119 * @private
120 */
121PrivateKey._transformBuffer = function (buf, network) {
122 var info = {}
123
124 if (buf.length === 32) {
125 return PrivateKey._transformBNBuffer(buf, network)
126 }
127
128 info.network = Networks.get(buf[0], 'privatekey')
129
130 if (!info.network) {
131 throw new Error('Invalid network')
132 }
133
134 if (network && info.network !== Networks.get(network)) {
135 throw new TypeError('Private key network mismatch')
136 }
137
138 if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] === 1) {
139 info.compressed = true
140 } else if (buf.length === 1 + 32) {
141 info.compressed = false
142 } else {
143 throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)')
144 }
145
146 info.bn = BN.fromBuffer(buf.slice(1, 32 + 1))
147
148 return info
149}
150
151/**
152 * Internal function to transform a BN buffer into a private key
153 *
154 * @param {Buffer} buf
155 * @param {Network|string=} network - a {@link Network} object, or a string with the network name
156 * @returns {object} an Object with keys: bn, network, and compressed
157 * @private
158 */
159PrivateKey._transformBNBuffer = function (buf, network) {
160 var info = {}
161 info.network = Networks.get(network) || Networks.defaultNetwork
162 info.bn = BN.fromBuffer(buf)
163 info.compressed = false
164 return info
165}
166
167/**
168 * Internal function to transform a WIF string into a private key
169 *
170 * @param {string} buf - An WIF string
171 * @returns {Object} An object with keys: bn, network and compressed
172 * @private
173 */
174PrivateKey._transformWIF = function (str, network) {
175 return PrivateKey._transformBuffer(Base58Check.decode(str), network)
176}
177
178/**
179 * Instantiate a PrivateKey from a Buffer with the DER or WIF representation
180 *
181 * @param {Buffer} buf
182 * @param {Network} network
183 * @return {PrivateKey}
184 */
185PrivateKey.fromBuffer = function (buf, network) {
186 return new PrivateKey(buf, network)
187}
188
189PrivateKey.fromHex = function (hex, network) {
190 return PrivateKey.fromBuffer(Buffer.from(hex, 'hex'), network)
191}
192
193/**
194 * Internal function to transform a JSON string on plain object into a private key
195 * return this.
196 *
197 * @param {string} json - A JSON string or plain object
198 * @returns {Object} An object with keys: bn, network and compressed
199 * @private
200 */
201PrivateKey._transformObject = function (json) {
202 var bn = new BN(json.bn, 'hex')
203 var network = Networks.get(json.network)
204 return {
205 bn: bn,
206 network: network,
207 compressed: json.compressed
208 }
209}
210
211/**
212 * Instantiate a PrivateKey from a WIF string
213 *
214 * @param {string} str - The WIF encoded private key string
215 * @returns {PrivateKey} A new valid instance of PrivateKey
216 */
217PrivateKey.fromString = PrivateKey.fromWIF = function (str) {
218 $.checkArgument(_.isString(str), 'First argument is expected to be a string.')
219 return new PrivateKey(str)
220}
221
222/**
223 * Instantiate a PrivateKey from a plain JavaScript object
224 *
225 * @param {Object} obj - The output from privateKey.toObject()
226 */
227PrivateKey.fromObject = PrivateKey.fromJSON = function (obj) {
228 $.checkArgument(_.isObject(obj), 'First argument is expected to be an object.')
229 return new PrivateKey(obj)
230}
231
232/**
233 * Instantiate a PrivateKey from random bytes
234 *
235 * @param {string=} network - Either "livenet" or "testnet"
236 * @returns {PrivateKey} A new valid instance of PrivateKey
237 */
238PrivateKey.fromRandom = function (network) {
239 var bn = PrivateKey._getRandomBN()
240 return new PrivateKey(bn, network)
241}
242
243/**
244 * Check if there would be any errors when initializing a PrivateKey
245 *
246 * @param {string} data - The encoded data in various formats
247 * @param {string=} network - Either "livenet" or "testnet"
248 * @returns {null|Error} An error if exists
249 */
250
251PrivateKey.getValidationError = function (data, network) {
252 var error
253 try {
254 new PrivateKey(data, network) // eslint-disable-line
255 } catch (e) {
256 error = e
257 }
258 return error
259}
260
261/**
262 * Check if the parameters are valid
263 *
264 * @param {string} data - The encoded data in various formats
265 * @param {string=} network - Either "livenet" or "testnet"
266 * @returns {Boolean} If the private key is would be valid
267 */
268PrivateKey.isValid = function (data, network) {
269 if (!data) {
270 return false
271 }
272 return !PrivateKey.getValidationError(data, network)
273}
274
275/**
276 * Will output the PrivateKey in WIF
277 *
278 * @returns {string}
279 */
280PrivateKey.prototype.toString = function () {
281 return this.toWIF()
282}
283
284/**
285 * Will output the PrivateKey to a WIF string
286 *
287 * @returns {string} A WIP representation of the private key
288 */
289PrivateKey.prototype.toWIF = function () {
290 var network = this.network
291 var compressed = this.compressed
292
293 var buf
294 if (compressed) {
295 buf = Buffer.concat([Buffer.from([network.privatekey]),
296 this.bn.toBuffer({ size: 32 }),
297 Buffer.from([0x01])])
298 } else {
299 buf = Buffer.concat([Buffer.from([network.privatekey]),
300 this.bn.toBuffer({ size: 32 })])
301 }
302
303 return Base58Check.encode(buf)
304}
305
306/**
307 * Will return the private key as a BN instance
308 *
309 * @returns {BN} A BN instance of the private key
310 */
311PrivateKey.prototype.toBigNumber = function () {
312 return this.bn
313}
314
315/**
316 * Will return the private key as a BN buffer
317 *
318 * @returns {Buffer} A buffer of the private key
319 */
320PrivateKey.prototype.toBuffer = function () {
321 return this.bn.toBuffer({ size: 32 })
322}
323
324PrivateKey.prototype.toHex = function () {
325 return this.toBuffer().toString('hex')
326}
327
328/**
329 * Will return the corresponding public key
330 *
331 * @returns {PublicKey} A public key generated from the private key
332 */
333PrivateKey.prototype.toPublicKey = function () {
334 if (!this._pubkey) {
335 this._pubkey = PublicKey.fromPrivateKey(this)
336 }
337 return this._pubkey
338}
339
340/**
341 * Will return an address for the private key
342 * @param {Network=} network - optional parameter specifying
343 * the desired network for the address
344 *
345 * @returns {Address} An address generated from the private key
346 */
347PrivateKey.prototype.toAddress = function (network) {
348 var pubkey = this.toPublicKey()
349 return Address.fromPublicKey(pubkey, network || this.network)
350}
351
352/**
353 * @returns {Object} A plain object representation
354 */
355PrivateKey.prototype.toObject = PrivateKey.prototype.toJSON = function toObject () {
356 return {
357 bn: this.bn.toString('hex'),
358 compressed: this.compressed,
359 network: this.network.toString()
360 }
361}
362
363/**
364 * Will return a string formatted for the console
365 *
366 * @returns {string} Private key
367 */
368PrivateKey.prototype.inspect = function () {
369 var uncompressed = !this.compressed ? ', uncompressed' : ''
370 return '<PrivateKey: ' + this.toHex() + ', network: ' + this.network + uncompressed + '>'
371}
372
373module.exports = PrivateKey