UNPKG

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