@dashevo/dashcore-lib
Version:
A pure and powerful JavaScript Dash library.
438 lines (399 loc) • 11.5 kB
JavaScript
/* eslint-disable */
// TODO: Remove previous line and work through linting issues at next edit
;
var BN = require('./crypto/bn');
var Point = require('./crypto/point');
var Hash = require('./crypto/hash');
var JSUtil = require('./util/js');
var Network = require('./networks');
var _ = require('lodash');
var $ = require('./util/preconditions');
/**
* Instantiate a PublicKey from a {@link PrivateKey}, {@link Point}, `string`, or `Buffer`.
*
* There are two internal properties, `network` and `compressed`, that deal with importing
* a PublicKey from a PrivateKey in WIF format. More details described on {@link PrivateKey}
*
* @example
* ```javascript
* // instantiate from a private key
* var key = PublicKey(privateKey, true);
*
* // export to as a DER hex encoded string
* var exported = key.toString();
*
* // import the public key
* var imported = PublicKey.fromString(exported);
* ```
*
* @param {string} data - The encoded data in various formats
* @param {Object} extra - additional options
* @param {Network=} extra.network - Which network should the address for this public key be for
* @param {Boolean=} extra.compressed - If the public key is compressed
* @returns {PublicKey} A new valid instance of a PublicKey
* @constructor
*/
function PublicKey(data, extra) {
if (!(this instanceof PublicKey)) {
return new PublicKey(data, extra);
}
$.checkArgument(
data,
'First argument is required, please include public key data.'
);
if (data instanceof PublicKey) {
// Return copy, but as it's an immutable object, return same argument
return data;
}
extra = extra || {};
var info = this._classifyArgs(data, extra);
// validation
info.point.validate();
JSUtil.defineImmutable(this, {
point: info.point,
compressed: info.compressed,
network: info.network || Network.defaultNetwork,
});
return this;
}
var hashProperty = {
configurable: false,
enumerable: true,
get: function () {
return this._getID();
},
};
Object.defineProperty(PublicKey.prototype, 'hash', hashProperty);
Object.defineProperty(PublicKey.prototype, 'id', hashProperty);
/**
* Internal function to differentiate between arguments passed to the constructor
* @param {*} data
* @param {Object} extra
*/
PublicKey.prototype._classifyArgs = function (data, extra) {
/* jshint maxcomplexity: 10 */
var info = {
compressed: _.isUndefined(extra.compressed) || extra.compressed,
};
// detect type of data
if (data instanceof Point) {
info.point = data;
} else if (data.x && data.y) {
info = PublicKey._transformObject(data);
} else if (typeof data === 'string') {
info = PublicKey._transformDER(Buffer.from(data, 'hex'));
} else if (PublicKey._isBuffer(data)) {
info = PublicKey._transformDER(data);
} else if (PublicKey._isPrivateKey(data)) {
info = PublicKey._transformPrivateKey(data);
} else {
throw new TypeError('First argument is an unrecognized data format.');
}
if (!info.network) {
info.network = _.isUndefined(extra.network)
? undefined
: Network.get(extra.network);
}
return info;
};
/**
* Internal function to detect if an object is a {@link PrivateKey}
*
* @param {*} param - object to test
* @returns {boolean}
* @private
*/
PublicKey._isPrivateKey = function (param) {
var PrivateKey = require('./privatekey');
return param instanceof PrivateKey;
};
/**
* Internal function to detect if an object is a Buffer
*
* @param {*} param - object to test
* @returns {boolean}
* @private
*/
PublicKey._isBuffer = function (param) {
return param instanceof Buffer || param instanceof Uint8Array;
};
/**
* Internal function to transform a private key into a public key point
*
* @param {PrivateKey} privkey - An instance of PrivateKey
* @returns {Object} An object with keys: point and compressed
* @private
*/
PublicKey._transformPrivateKey = function (privkey) {
$.checkArgument(
PublicKey._isPrivateKey(privkey),
'Must be an instance of PrivateKey'
);
var info = {};
info.point = Point.getG().mul(privkey.bn);
info.compressed = privkey.compressed;
info.network = privkey.network;
return info;
};
/**
* Internal function to transform DER into a public key point
*
* @param {Buffer} buf - A hex encoded buffer
* @param {bool=} strict - if set to false, will loosen some conditions
* @returns {Object} An object with keys: point and compressed
* @private
*/
PublicKey._transformDER = function (buf, strict) {
/* jshint maxstatements: 30 */
/* jshint maxcomplexity: 12 */
$.checkArgument(
PublicKey._isBuffer(buf),
'Must be a hex buffer of DER encoded public key'
);
var info = {};
strict = _.isUndefined(strict) ? true : strict;
var x;
var y;
var xbuf;
var ybuf;
if (buf[0] === 0x04 || (!strict && (buf[0] === 0x06 || buf[0] === 0x07))) {
xbuf = buf.slice(1, 33);
ybuf = buf.slice(33, 65);
if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) {
throw new TypeError('Length of x and y must be 32 bytes');
}
x = new BN(xbuf);
y = new BN(ybuf);
info.point = new Point(x, y);
info.compressed = false;
} else if (buf[0] === 0x03) {
xbuf = buf.slice(1);
x = new BN(xbuf);
info = PublicKey._transformX(true, x);
info.compressed = true;
} else if (buf[0] === 0x02) {
xbuf = buf.slice(1);
x = new BN(xbuf);
info = PublicKey._transformX(false, x);
info.compressed = true;
} else {
throw new TypeError('Invalid DER format public key');
}
return info;
};
/**
* Internal function to transform X into a public key point
*
* @param {Boolean} odd - If the point is above or below the x axis
* @param {Point} x - The x point
* @returns {Object} An object with keys: point and compressed
* @private
*/
PublicKey._transformX = function (odd, x) {
$.checkArgument(
typeof odd === 'boolean',
'Must specify whether y is odd or not (true or false)'
);
var info = {};
info.point = Point.fromX(odd, x);
return info;
};
/**
* Internal function to transform a JSON into a public key point
*
* @param {String|Object} json - a JSON string or plain object
* @returns {Object} An object with keys: point and compressed
* @private
*/
PublicKey._transformObject = function (json) {
var x = new BN(json.x, 'hex');
var y = new BN(json.y, 'hex');
var point = new Point(x, y);
return new PublicKey(point, {
compressed: json.compressed,
});
};
/**
* Instantiate a PublicKey from a PrivateKey
*
* @param {PrivateKey} privkey - An instance of PrivateKey
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromPrivateKey = function (privkey) {
$.checkArgument(
PublicKey._isPrivateKey(privkey),
'Must be an instance of PrivateKey'
);
var info = PublicKey._transformPrivateKey(privkey);
return new PublicKey(info.point, {
compressed: info.compressed,
network: info.network,
});
};
/**
* Instantiate a PublicKey from a Buffer
*
* @function
* @param {Buffer} buf - A DER hex buffer
* @param {bool=} strict - if set to false, will loosen some conditions
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromDER = PublicKey.fromBuffer = function (buf, strict) {
$.checkArgument(
PublicKey._isBuffer(buf),
'Must be a hex buffer of DER encoded public key'
);
var info = PublicKey._transformDER(buf, strict);
return new PublicKey(info.point, {
compressed: info.compressed,
});
};
/**
* Instantiate a PublicKey from a Point
*
* @param {Point} point - A Point instance
* @param {boolean=} compressed - whether to store this public key as compressed format
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromPoint = function (point, compressed) {
$.checkArgument(
point instanceof Point,
'First argument must be an instance of Point.'
);
return new PublicKey(point, {
compressed: compressed,
});
};
/**
* Instantiate a PublicKey from a DER hex encoded string
*
* @param {string} str - A DER hex string
* @param {String=} encoding - The type of string encoding
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromString = function (str, encoding) {
var buf = Buffer.from(str, encoding || 'hex');
var info = PublicKey._transformDER(buf);
return new PublicKey(info.point, {
compressed: info.compressed,
});
};
/**
* Instantiate a PublicKey from an X Point
*
* @param {Boolean} odd - If the point is above or below the x axis
* @param {Point} x - The x point
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromX = function (odd, x) {
var info = PublicKey._transformX(odd, x);
return new PublicKey(info.point, {
compressed: info.compressed,
});
};
/**
* Check if there would be any errors when initializing a PublicKey
*
* @param {string} data - The encoded data in various formats
* @returns {null|Error} An error if exists
*/
PublicKey.getValidationError = function (data) {
var error;
try {
/* jshint nonew: false */
new PublicKey(data);
} catch (e) {
error = e;
}
return error;
};
/**
* Check if the parameters are valid
*
* @param {string} data - The encoded data in various formats
* @returns {Boolean} If the public key would be valid
*/
PublicKey.isValid = function (data) {
return !PublicKey.getValidationError(data);
};
/**
* @function
* @returns {Object} A plain object of the PublicKey
*/
PublicKey.prototype.toObject = PublicKey.prototype.toJSON =
function toObject() {
return {
x: this.point.getX().toString('hex', 2),
y: this.point.getY().toString('hex', 2),
compressed: this.compressed,
};
};
/**
* Will output the PublicKey to a DER Buffer
*
* @function
* @returns {Buffer} A DER hex encoded buffer
*/
PublicKey.prototype.toBuffer = PublicKey.prototype.toDER = function () {
var x = this.point.getX();
var y = this.point.getY();
var xbuf = x.toBuffer({
size: 32,
});
var ybuf = y.toBuffer({
size: 32,
});
var prefix;
if (!this.compressed) {
prefix = Buffer.from([0x04]);
return Buffer.concat([prefix, xbuf, ybuf]);
} else {
var odd = ybuf[ybuf.length - 1] % 2;
if (odd) {
prefix = Buffer.from([0x03]);
} else {
prefix = Buffer.from([0x02]);
}
return Buffer.concat([prefix, xbuf]);
}
};
/**
* Will return a sha256 + ripemd160 hash of the serialized public key
* @see https://github.com/bitcoin/bitcoin/blob/master/src/pubkey.h#L141
* @returns {Buffer}
*/
PublicKey.prototype._getID = function _getID() {
return Hash.sha256ripemd160(this.toBuffer());
};
/**
* Will return an address for the public key
*
* @param {String|Network=} network - Which network should the address be for
* @returns {Address} An address generated from the public key
*/
PublicKey.prototype.toAddress = function (network) {
var Address = require('./address');
return Address.fromPublicKey(this, network || this.network);
};
/**
* Will output the PublicKey to a DER encoded hex string
*
* @returns {string} A DER hex encoded string
*/
PublicKey.prototype.toString = function () {
return this.toDER().toString('hex');
};
/**
* Will return a string formatted for the console
*
* @returns {string} Public key
*/
PublicKey.prototype.inspect = function () {
return (
'<PublicKey: ' +
this.toString() +
(this.compressed ? '' : ', uncompressed') +
'>'
);
};
module.exports = PublicKey;