diff --git a/index.js b/index.js index e6cfd66..47092f7 100644 --- a/index.js +++ b/index.js @@ -24,8 +24,8 @@ bitcore.Block = require('./lib/block'); bitcore.Blockheader = require('./lib/blockheader'); bitcore.Networks = require('./lib/networks'); bitcore.Opcode = require('./lib/opcode'); -bitcore.Privkey = require('./lib/privkey'); -bitcore.Pubkey = require('./lib/pubkey'); +bitcore.PrivateKey = require('./lib/privatekey'); +bitcore.PublicKey = require('./lib/publickey'); bitcore.Script = require('./lib/script'); bitcore.Signature = require('./lib/signature'); bitcore.Transaction = require('./lib/transaction'); diff --git a/lib/address.js b/lib/address.js index 5cf22a4..9d9185c 100644 --- a/lib/address.js +++ b/lib/address.js @@ -9,7 +9,7 @@ var Hash = require('./crypto/hash'); * Bitcore Address * * Instantiate an address from an address String or Buffer, a public key or script hash Buffer, - * or an instance of Pubkey or Script. + * or an instance of PublicKey or Script. * * @example * @@ -45,8 +45,8 @@ function Address(data, network, type) { info = Address._transformHash(data); } else if ((data instanceof Buffer || data instanceof Uint8Array) && data.length === 21) { info = Address._transformBuffer(data, network, type); - } else if (data.constructor && (data.constructor.name && data.constructor.name === 'Pubkey')) { - info = Address._transformPubkey(data); + } else if (data.constructor && (data.constructor.name && data.constructor.name === 'PublicKey')) { + info = Address._transformPublicKey(data); } else if (data.constructor && (data.constructor.name && data.constructor.name === 'Script')) { info = Address._transformScript(data); } else if (typeof(data) === 'string') { @@ -146,15 +146,15 @@ Address._transformBuffer = function(buffer, network, type){ /** * - * Internal function to transform a Pubkey + * Internal function to transform a PublicKey * - * @param {Pubkey} pubkey - An instance of Pubkey + * @param {PublicKey} pubkey - An instance of PublicKey * @returns {Object} An object with keys: hashBuffer, type */ -Address._transformPubkey = function(pubkey){ +Address._transformPublicKey = function(pubkey){ var info = {}; - if (!pubkey.constructor || (pubkey.constructor.name && pubkey.constructor.name !== 'Pubkey')) { - throw new TypeError('Address must be an instance of Pubkey.'); + if (!pubkey.constructor || (pubkey.constructor.name && pubkey.constructor.name !== 'PublicKey')) { + throw new TypeError('Address must be an instance of PublicKey.'); } info.hashBuffer = Hash.sha256ripemd160(pubkey.toBuffer()); info.type = 'pubkeyhash'; @@ -182,7 +182,7 @@ Address._transformScript = function(script){ * * Internal function to transform a bitcoin address string * - * @param {String} data - An instance of Pubkey + * @param {String} data - An instance of PublicKey * @param {String} [network] - The network: 'mainnet' or 'testnet' * @param {String} [type] - The type: 'pubkeyhash' or 'scripthash' * @returns {Object} An object with keys: hashBuffer, network and type @@ -198,14 +198,14 @@ Address._transformString = function(data, network, type){ /** * - * Instantiate an address from a Pubkey instance + * Instantiate an address from a PublicKey instance * - * @param {String} data - An instance of Pubkey + * @param {String} data - An instance of PublicKey * @param {String} network - The network: 'mainnet' or 'testnet' * @returns {Address} A new valid and frozen instance of an Address */ -Address.fromPubkey = function(data, network){ - var info = Address._transformPubkey(data); +Address.fromPublicKey = function(data, network){ + var info = Address._transformPublicKey(data); return new Address(info.hashBuffer, network, info.type); }; @@ -217,7 +217,7 @@ Address.fromPubkey = function(data, network){ * @param {String} network - The network: 'mainnet' or 'testnet' * @returns {Address} A new valid and frozen instance of an Address */ -Address.fromPubkeyHash = function(hash, network) { +Address.fromPublicKeyHash = function(hash, network) { var info = Address._transformHash(hash); return new Address(info.hashBuffer, network, 'pubkeyhash'); }; diff --git a/lib/bip32.js b/lib/bip32.js index 7c4bc39..ab5891a 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -6,8 +6,8 @@ var Hash = require('./crypto/hash'); var Point = require('./crypto/point'); var Random = require('./crypto/random'); var BN = require('./crypto/bn'); -var Pubkey = require('./pubkey'); -var Privkey = require('./privkey'); +var PublicKey = require('./publickey'); +var PrivateKey = require('./privatekey'); var BIP32 = function BIP32(obj) { if (!(this instanceof BIP32)) @@ -41,8 +41,8 @@ BIP32.prototype.fromRandom = function(networkstr) { this.parentfingerprint = new Buffer([0, 0, 0, 0]); this.childindex = new Buffer([0, 0, 0, 0]); this.chaincode = Random.getRandomBuffer(32); - this.privkey = Privkey().fromRandom(); - this.pubkey = new Pubkey().fromPrivkey(this.privkey); + this.privkey = PrivateKey.fromRandom(); + this.pubkey = PublicKey.fromPrivateKey(this.privkey); this.hasprivkey = true; this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); this.buildxpubkey(); @@ -72,8 +72,8 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) { this.childindex = new Buffer([0, 0, 0, 0]); this.chaincode = hash.slice(32, 64); this.version = networks[networkstr].bip32privkey; - this.privkey = new Privkey({bn: BN().fromBuffer(hash.slice(0, 32))}); - this.pubkey = new Pubkey().fromPrivkey(this.privkey); + this.privkey = new PrivateKey(BN().fromBuffer(hash.slice(0, 32))); + this.pubkey = PublicKey.fromPrivateKey(this.privkey); this.hasprivkey = true; this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); @@ -105,12 +105,12 @@ BIP32.prototype.initFromBytes = function(bytes) { this.version == networks.testnet.bip32pubkey); if (isPrivate && keyBytes[0] == 0) { - this.privkey = new Privkey({bn: BN().fromBuffer(keyBytes.slice(1, 33))}); - this.pubkey = new Pubkey().fromPrivkey(this.privkey); + this.privkey = new PrivateKey(BN().fromBuffer(keyBytes.slice(1, 33))); + this.pubkey = PublicKey.fromPrivateKey(this.privkey); this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); this.hasprivkey = true; } else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) { - this.pubkey = (new Pubkey()).fromDER(keyBytes); + this.pubkey = PublicKey.fromDER(keyBytes); this.pubkeyhash = Hash.sha256ripemd160(this.pubkey.toBuffer()); this.hasprivkey = false; } else { @@ -119,7 +119,7 @@ BIP32.prototype.initFromBytes = function(bytes) { this.buildxpubkey(); this.buildxprivkey(); -} +}; BIP32.prototype.buildxpubkey = function() { this.xpubkey = new Buffer([]); @@ -153,7 +153,7 @@ BIP32.prototype.buildxpubkey = function() { this.chaincode, this.pubkey.toBuffer() ]); -} +}; BIP32.prototype.xpubkeyString = function(format) { if (format === undefined || format === 'base58') { @@ -228,7 +228,7 @@ BIP32.prototype.derive = function(path) { } return bip32; -} +}; BIP32.prototype.deriveChild = function(i) { if (typeof i !== 'number') @@ -270,8 +270,8 @@ BIP32.prototype.deriveChild = function(i) { ret = new BIP32(); ret.chaincode = ir; - ret.privkey = new Privkey({bn: k}); - ret.pubkey = new Pubkey().fromPrivkey(ret.privkey); + ret.privkey = new PrivateKey(k); + ret.pubkey = PublicKey.fromPrivateKey(ret.privkey); ret.hasprivkey = true; } else { @@ -284,8 +284,7 @@ BIP32.prototype.deriveChild = function(i) { var ilG = Point.getG().mul(il); var Kpar = this.pubkey.point; var Ki = ilG.add(Kpar); - var newpub = new Pubkey(); - newpub.point = Ki; + var newpub = PublicKey.fromPoint(Ki); ret = new BIP32(); ret.chaincode = ir; @@ -305,7 +304,7 @@ BIP32.prototype.deriveChild = function(i) { ret.buildxprivkey(); return ret; -} +}; BIP32.prototype.toString = function() { var isPrivate = diff --git a/lib/crypto/ecdsa.js b/lib/crypto/ecdsa.js index 24ae00c..842be38 100644 --- a/lib/crypto/ecdsa.js +++ b/lib/crypto/ecdsa.js @@ -3,8 +3,8 @@ var BN = require('./bn'); var Point = require('./point'); var Random = require('./random'); -var Pubkey = require('../pubkey'); -var Privkey = require('../privkey'); +var PublicKey = require('../publickey'); +var PrivateKey = require('../privatekey'); var Signature = require('../signature'); var ECDSA = function ECDSA(obj) { @@ -25,7 +25,7 @@ ECDSA.prototype.set = function(obj) { }; ECDSA.prototype.privkey2pubkey = function(){ - this.pubkey = Pubkey().fromPrivkey(this.privkey); + this.pubkey = PublicKey.fromPrivateKey(this.privkey); }; ECDSA.prototype.calci = function() { @@ -35,6 +35,7 @@ ECDSA.prototype.calci = function() { try { Qprime = this.sig2pubkey(); } catch (e) { + console.log(e); continue; } if (Qprime.point.eq(this.pubkey.point)) { @@ -52,9 +53,9 @@ ECDSA.prototype.fromString = function(str) { if (obj.hashbuf) this.hashbuf = new Buffer(obj.hashbuf, 'hex'); if (obj.pubkey) - this.pubkey = Pubkey().fromString(obj.pubkey); + this.pubkey = PublicKey.fromString(obj.pubkey); if (obj.privkey) - this.privkey = Privkey().fromString(obj.privkey); + this.privkey = PrivateKey.fromString(obj.privkey); if (obj.sig) this.sig = Signature().fromString(obj.sig); if (obj.k) @@ -114,9 +115,7 @@ ECDSA.prototype.sig2pubkey = function() { //var Q = R.multiplyTwo(s, G, eNeg).mul(rInv); var Q = R.mul(s).add(G.mul(eNeg)).mul(rInv); - var pubkey = new Pubkey({point: Q}); - pubkey.compressed = this.sig.compressed; - pubkey.validate(); + var pubkey = PublicKey.fromPoint(Q, this.sig.compressed); return pubkey; }; @@ -125,12 +124,6 @@ ECDSA.prototype.sigError = function() { if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 32) return 'hashbuf must be a 32 byte buffer'; - try { - this.pubkey.validate(); - } catch (e) { - return 'Invalid pubkey: ' + e; - } - var r = this.sig.r; var s = this.sig.s; if (!(r.gt(0) && r.lt(Point.getN())) diff --git a/lib/networks.js b/lib/networks.js index 97afe9c..7eee84e 100644 --- a/lib/networks.js +++ b/lib/networks.js @@ -5,7 +5,7 @@ exports.mainnet = { identity: 0x0f, identephem: 0x02, identpersist: 0x01, - privkey: 0x80, + privatekey: 0x80, scripthash: 0x05, bip32pubkey: 0x0488b21e, bip32privkey: 0x0488ade4, @@ -16,7 +16,7 @@ exports.testnet = { identity: 0x0f, identephem: 0x02, identpersist: 0x11, - privkey: 0xef, + privatekey: 0xef, scripthash: 0xc4, bip32pubkey: 0x043587cf, bip32privkey: 0x04358394, diff --git a/lib/privatekey.js b/lib/privatekey.js new file mode 100644 index 0000000..dee392d --- /dev/null +++ b/lib/privatekey.js @@ -0,0 +1,316 @@ +'use strict'; + +var BN = require('./crypto/bn'); +var Point = require('./crypto/point'); +var Random = require('./crypto/random'); +var networks = require('./networks'); +var base58check = require('./encoding/base58check'); +var Address = require('./address'); +var PublicKey = require('./publickey'); + +/** + * + * Bitcore PrivateKey + * + * Instantiate a PrivateKey from a BN, Buffer and WIF. + * + * @example + * + * var privateKey = new PrivateKey(); + * + * @param {String} data - The encoded data in various formats + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {Boolean} [compressed] - If the key is in compressed format + * @returns {PrivateKey} A new valid instance of an PrivateKey + */ +var PrivateKey = function PrivateKey(data, network, compressed) { + + if (!(this instanceof PrivateKey)) { + return new PrivateKey(data, network, compressed); + } + + var info = { + compressed: typeof(compressed) !== 'undefined' ? compressed : true, + network: network || 'mainnet' + }; + + // detect type of data + if (!data){ + info.bn = PrivateKey._getRandomBN(); + } else if (data instanceof BN) { + info.bn = data; + } else if (data instanceof Buffer || data instanceof Uint8Array) { + info = PrivateKey._transformBuffer(data, network, compressed); + } else if (typeof(data) === 'string'){ + info = PrivateKey._transformWIF(data, network, compressed); + } else { + throw new TypeError('First argument is an unrecognized data type.'); + } + + // validation + if (!info.bn.lt(Point.getN())) { + throw new TypeError('Number must be less than N'); + } + if (typeof(networks[info.network]) === 'undefined') { + throw new TypeError('Must specify the network ("mainnet" or "testnet")'); + } + if (typeof(info.compressed) !== 'boolean') { + throw new TypeError('Must specify whether the corresponding public key is compressed or not (true or false)'); + } + + this.bn = info.bn; + this.compressed = info.compressed; + this.network = info.network; + + return this; + +}; + +/** + * + * Internal function to get a random BN + * + * @returns {Object} An object with keys: bn, network and compressed + */ +PrivateKey._getRandomBN = function(){ + var condition; + var bn; + do { + var privbuf = Random.getRandomBuffer(32); + bn = BN().fromBuffer(privbuf); + condition = bn.lt(Point.getN()); + } while (!condition); + return bn; +}; + +/** + * + * Internal function to transform a WIF Buffer into a private key + * + * @param {Buffer} buf - An WIF string + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {String} [compressed] - If the private key is compressed + * @returns {Object} An object with keys: bn, network and compressed + */ +PrivateKey._transformBuffer = function(buf, network, compressed) { + + var info = {}; + + if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] === 1) { + info.compressed = true; + } else if (buf.length === 1 + 32) { + info.compressed = false; + } else { + throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)'); + } + + if (buf[0] === networks.mainnet.privatekey) { + info.network = 'mainnet'; + } else if (buf[0] === networks.testnet.privatekey) { + info.network = 'testnet'; + } else { + throw new Error('Invalid network'); + } + + if (network && info.network !== network){ + throw TypeError('Private key network mismatch'); + } + + if (typeof(compressed) !== 'undefined' && info.compressed !== compressed){ + throw TypeError('Private key compression mismatch'); + } + + info.bn = BN.fromBuffer(buf.slice(1, 32 + 1)); + + return info; + +}; + +/** + * + * Internal function to transform a WIF string into a private key + * + * @param {String} buf - An WIF string + * @returns {Object} An object with keys: bn, network and compressed + */ +PrivateKey._transformWIF = function(str, network, compressed) { + return PrivateKey._transformBuffer(base58check.decode(str), network, compressed); +}; + +/** + * + * Instantiate a PrivateKey from a WIF string + * + * @param {String} str - The WIF encoded private key string + * @returns {PrivateKey} A new valid instance of PrivateKey + */ +PrivateKey.fromWIF = function(str) { + var info = PrivateKey._transformWIF(str); + return new PrivateKey(info.bn, info.network, info.compressed); +}; + +/** + * + * Instantiate a PrivateKey from a WIF JSON string + * + * @param {String} str - The WIF encoded private key string + * @returns {PrivateKey} A new valid instance of PrivateKey + */ +PrivateKey.fromJSON = function(json) { + var info = PrivateKey._transformWIF(json); + return new PrivateKey(info.bn, info.network, info.compressed); +}; + +/** + * + * Instantiate a PrivateKey from random bytes + * + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {String} [compressed] - If the private key is compressed + * @returns {PrivateKey} A new valid instance of PrivateKey + */ +PrivateKey.fromRandom = function(network, compressed) { + var bn = PrivateKey._getRandomBN(); + return new PrivateKey(bn, network, compressed); +}; + +/** + * + * Instantiate a PrivateKey from a WIF string + * + * @param {String} str - The WIF encoded private key string + * @returns {PrivateKey} A new valid instance of PrivateKey + */ +PrivateKey.fromString = function(str) { + var info = PrivateKey._transformWIF(str); + return new PrivateKey(info.bn, info.network, info.compressed); +}; + +/** + * + * Check if there would be any errors when initializing a PrivateKey + * + * @param {String} data - The encoded data in various formats + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {String} [compressed] - If the private key is compressed + * @returns {null|Error} An error if exists + */ + +PrivateKey.getValidationError = function(data, network, compressed) { + var error; + try { + new PrivateKey(data, network, compressed); + } catch (e) { + error = e; + } + return error; +}; + +/** + * + * Check if the parameters are valid + * + * @param {String} data - The encoded data in various formats + * @param {String} [network] - Either "mainnet" or "testnet" + * @param {String} [compressed] - If the private key is compressed + * @returns {Boolean} If the private key is would be valid + */ +PrivateKey.isValid = function(data, network, compressed){ + return !PrivateKey.getValidationError(data, network, compressed); +}; + +/** + * + * Will output the PrivateKey to a WIF string + * + * @returns {String} A WIP representation of the private key + */ +PrivateKey.prototype.toWIF = function() { + var network = this.network; + var compressed = this.compressed; + + var buf; + if (compressed) { + buf = Buffer.concat([new Buffer([networks[network].privatekey]), + this.bn.toBuffer({size: 32}), + new Buffer([0x01])]); + } else { + buf = Buffer.concat([new Buffer([networks[network].privatekey]), + this.bn.toBuffer({size: 32})]); + } + + return base58check.encode(buf); +}; + +/** + * + * Will return the private key as a BN instance + * + * @returns {BN} A BN instance of the private key + */ +PrivateKey.prototype.toBigNumber = function(){ + return this.bn; +}; + +/** + * + * Will return the private key as a BN buffer + * + * @returns {Buffer} A buffer of the private key + */ +PrivateKey.prototype.toBuffer = function(){ + return this.bn.toBuffer(); +}; + +/** + * + * Will return the corresponding public key + * + * @returns {PublicKey} A public key generated from the private key + */ +PrivateKey.prototype.toPublicKey = function(){ + return PublicKey.fromPrivateKey(this); +}; + +/** + * + * Will return an address for the private key + * + * @returns {Address} An address generated from the private key + */ +PrivateKey.prototype.toAddress = function() { + var pubkey = this.toPublicKey(); + return Address.fromPublicKey(pubkey, this.network); +}; + +/** + * + * Will output the PrivateKey to a WIF string + * + * @returns {String} A WIF representation of the private key + */ +PrivateKey.prototype.toJSON = function() { + return this.toString(); +}; + +/** + * + * Will output the PrivateKey to a WIF string + * + * @returns {String} A WIF representation of the private key + */ +PrivateKey.prototype.toString = function() { + return this.toWIF(); +}; + +/** + * + * Will return a string formatted for the console + * + * @returns {String} Private key + */ +PrivateKey.prototype.inspect = function() { + return ''; +}; + +module.exports = PrivateKey; diff --git a/lib/privkey.js b/lib/privkey.js deleted file mode 100644 index 5f024a8..0000000 --- a/lib/privkey.js +++ /dev/null @@ -1,111 +0,0 @@ -'use strict'; - -var BN = require('./crypto/bn'); -var Point = require('./crypto/point'); -var Random = require('./crypto/random'); -var networks = require('./networks'); -var base58check = require('./encoding/base58check'); -var Pubkey = require('./pubkey'); - -var Privkey = function Privkey(bn) { - if (!(this instanceof Privkey)) - return new Privkey(bn); - if (bn instanceof BN) - this.bn = bn; - else if (bn) { - var obj = bn; - this.set(obj); - } -}; - -Privkey.prototype.set = function(obj) { - this.bn = obj.bn || this.bn; - this.networkstr = obj.networkstr || this.networkstr; - this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; - return this; -}; - -Privkey.prototype.fromJSON = function(json) { - this.fromString(json); - return this; -}; - -Privkey.prototype.toJSON = function() { - return this.toString(); -}; - -Privkey.prototype.fromRandom = function() { - do { - var privbuf = Random.getRandomBuffer(32); - var bn = BN().fromBuffer(privbuf); - var condition = bn.lt(Point.getN()); - } while (!condition); - this.set({ - bn: bn, - networkstr: 'mainnet', - compressed: true - }); - return this; -}; - -Privkey.prototype.validate = function() { - if (!this.bn.lt(Point.getN())) - throw new Error('Number must be less than N'); - if (typeof networks[this.networkstr] === undefined) - throw new Error('Must specify the networkstr ("mainnet" or "testnet")'); - if (typeof this.compressed !== 'boolean') - throw new Error('Must specify whether the corresponding public key is compressed or not (true or false)'); -}; - -Privkey.prototype.toWIF = function() { - var networkstr = this.networkstr; - var compressed = this.compressed; - - if (typeof this.networkstr === 'undefined') - networkstr = 'mainnet'; - if (typeof this.compressed === 'undefined') - compressed = true; - - var privbuf = this.bn.toBuffer({size: 32}); - var buf; - if (compressed) - buf = Buffer.concat([new Buffer([networks[networkstr].privkey]), this.bn.toBuffer({size: 32}), new Buffer([0x01])]); - else - buf = Buffer.concat([new Buffer([networks[networkstr].privkey]), this.bn.toBuffer({size: 32})]); - - return base58check.encode(buf); -}; - -Privkey.prototype.fromWIF = function(str) { - var buf = base58check.decode(str); - - if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] == 1) - this.compressed = true; - else if (buf.length === 1 + 32) - this.compressed = false; - else - throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)'); - - if (buf[0] === networks.mainnet.privkey) - this.networkstr = 'mainnet'; - else if (buf[0] === networks.testnet.privkey) - this.networkstr = 'testnet'; - else - throw new Error('Invalid networkstr'); - - this.bn = BN.fromBuffer(buf.slice(1, 32 + 1)); -}; - -Privkey.prototype.toString = function() { - return this.toWIF(); -}; - -Privkey.prototype.toPubkey = function() { - return new Pubkey().fromPrivkey(this); -} - -Privkey.prototype.fromString = function(str) { - this.fromWIF(str); -}; - -module.exports = Privkey; diff --git a/lib/pubkey.js b/lib/pubkey.js deleted file mode 100644 index d9062a4..0000000 --- a/lib/pubkey.js +++ /dev/null @@ -1,127 +0,0 @@ -'use strict'; - -var Point = require('./crypto/point'); -var bn = require('./crypto/bn'); - -var Pubkey = function Pubkey(point) { - if (!(this instanceof Pubkey)) - return new Pubkey(point); - if (point instanceof Point) - this.point = point; - else if (point) { - var obj = point; - this.set(obj); - } -}; - -Pubkey.prototype.set = function(obj) { - if (obj.point && !obj.point.getX() && !obj.point.getY()) - throw new Error('Invalid point'); - this.point = obj.point || this.point; - this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; - return this; -}; - -Pubkey.prototype.fromJSON = function(json) { - this.fromBuffer(new Buffer(json, 'hex')); - return this; -}; - -Pubkey.prototype.toJSON = function() { - return this.toBuffer().toString('hex'); -}; - -Pubkey.prototype.fromPrivkey = function(privkey) { - this.set({ - point: Point.getG().mul(privkey.bn), - compressed: privkey.compressed} - ); - return this; -}; - -Pubkey.prototype.fromBuffer = function(buf) { - return this.fromDER(buf); -}; - -Pubkey.prototype.fromDER = function(buf) { - if (buf[0] == 0x04) { - var xbuf = buf.slice(1, 33); - var ybuf = buf.slice(33, 65); - if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) - throw new Error('Length of x and y must be 32 bytes'); - var x = bn(xbuf); - var y = bn(ybuf); - this.point = Point(x, y); - this.compressed = false; - } else if (buf[0] == 0x03) { - var xbuf = buf.slice(1); - var x = bn(xbuf); - this.fromX(true, x); - this.compressed = true; - } else if (buf[0] == 0x02) { - var xbuf = buf.slice(1); - var x = bn(xbuf); - this.fromX(false, x); - this.compressed = true; - } else { - throw new Error('Invalid DER format pubkey'); - } - return this; -}; - -Pubkey.prototype.fromString = function( str , encoding ) { - var encoding = encoding || 'hex'; - this.fromDER( new Buffer(str, encoding ) ); -}; - -Pubkey.prototype.fromX = function(odd, x) { - if (typeof odd !== 'boolean') - throw new Error('Must specify whether y is odd or not (true or false)'); - this.point = Point.fromX(odd, x); -}; - -Pubkey.prototype.toBuffer = function() { - var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; - return this.toDER(compressed); -}; - -Pubkey.prototype.toDER = function(compressed) { - compressed = typeof this.compressed === 'undefined' ? compressed : this.compressed; - if (typeof compressed !== 'boolean') - throw new Error('Must specify whether the public key is compressed or not (true or false)'); - - var x = this.point.getX(); - var y = this.point.getY(); - - var xbuf = x.toBuffer({size: 32}); - var ybuf = y.toBuffer({size: 32}); - - if (!compressed) { - var prefix = new Buffer([0x04]); - return Buffer.concat([prefix, xbuf, ybuf]); - } else { - var odd = ybuf[ybuf.length - 1] % 2; - if (odd) - var prefix = new Buffer([0x03]); - else - var prefix = new Buffer([0x02]); - return Buffer.concat([prefix, xbuf]); - } -}; - -Pubkey.prototype.toString = function() { - var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; - return this.toDER(compressed).toString('hex'); -}; - -//https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf -Pubkey.prototype.validate = function() { - if (this.point.isInfinity()) - throw new Error('point: Point cannot be equal to Infinity'); - if (this.point.eq(Point(bn(0), bn(0)))) - throw new Error('point: Point cannot be equal to 0, 0'); - this.point.validate(); - return this; -}; - -module.exports = Pubkey; diff --git a/lib/publickey.js b/lib/publickey.js new file mode 100644 index 0000000..71501fd --- /dev/null +++ b/lib/publickey.js @@ -0,0 +1,341 @@ +'use strict'; + +var Point = require('./crypto/point'); +var BN = require('./crypto/bn'); + +/** + * + * Bitcore PublicKey + * + * Instantiate a PublicKey from a 'PrivateKey', 'Point', 'string', 'Buffer'. + * + * @example + * + * var publicKey = new PublicKey(privkey, true); + * + * @param {String} data - The encoded data in various formats + * @param {String} [compressed] - If the public key is compressed + * @returns {PublicKey} A new valid instance of an PublicKey + */ +var PublicKey = function PublicKey(data, compressed) { + + if (!(this instanceof PublicKey)) { + return new PublicKey(data, compressed); + } + + if (!data) { + throw new TypeError('First argument is required, please include public key data.'); + } + + var info = { + compressed: typeof(compressed) !== 'undefined' ? compressed : true + }; + + // detect type of data + if (data instanceof Point) { + info.point = data; + } else if (typeof(data) === 'string'){ + info = PublicKey._transformDER(new Buffer(data, 'hex' )); + } else if (data instanceof Buffer || data instanceof Uint8Array){ + info = PublicKey._transformDER(data); + } else if (data.constructor && (data.constructor.name && + data.constructor.name === 'PrivateKey')) { + info = PublicKey._transformPrivateKey(data); + } else { + throw new TypeError('First argument is an unrecognized data format.'); + } + + // validation + if (info.point.isInfinity()){ + throw new Error('Point cannot be equal to Infinity'); + } + if (info.point.eq(Point(BN(0), BN(0)))){ + throw new Error('Point cannot be equal to 0, 0'); + } + + //https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf + info.point.validate(); + + this.point = info.point; + this.compressed = info.compressed; + + return this; + +}; + +/** + * + * 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 + */ +PublicKey._transformPrivateKey = function(privkey) { + var info = {}; + if (!privkey.constructor || + (privkey.constructor.name && privkey.constructor.name !== 'PrivateKey')) { + throw new TypeError('Must be an instance of PrivateKey'); + } + info.point = Point.getG().mul(privkey.bn); + info.compressed = privkey.compressed; + return info; +}; + +/** + * + * Internal function to transform DER into a public key point + * + * @param {Buffer} buf - An hex encoded buffer + * @returns {Object} An object with keys: point and compressed + */ +PublicKey._transformDER = function(buf){ + var info = {}; + if (!(buf instanceof Buffer) && !(buf instanceof Uint8Array)){ + throw new TypeError('Must be a hex buffer of DER encoded public key'); + } + + var x; + var y; + var xbuf; + var ybuf; + + if (buf[0] === 0x04) { + 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 = BN(xbuf); + y = BN(ybuf); + info.point = Point(x, y); + info.compressed = false; + } else if (buf[0] === 0x03) { + xbuf = buf.slice(1); + x = BN(xbuf); + info = PublicKey._transformX(true, x); + info.compressed = true; + } else if (buf[0] == 0x02) { + xbuf = buf.slice(1); + x = 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 + */ +PublicKey._transformX = function(odd, x){ + var info = {}; + if (typeof odd !== 'boolean') { + throw new TypeError('Must specify whether y is odd or not (true or false)'); + } + info.point = Point.fromX(odd, x); + return info; +}; + +/** + * + * Instantiate a PublicKey from JSON + * + * @param {String} json - A JSON string of DER encoded public key + * @returns {PublicKey} A new valid instance of PublicKey + */ +PublicKey.fromJSON = function(json) { + var buf = new Buffer(json, 'hex'); + var info = PublicKey._transformDER(buf); + return new PublicKey(info.point, info.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) { + var info = PublicKey._transformPrivateKey(privkey); + return new PublicKey(info.point, info.compressed); +}; + +/** + * + * Instantiate a PublicKey from a Buffer + * + * @param {Buffer} buf - A DER hex buffer + * @returns {PublicKey} A new valid instance of PublicKey + */ +PublicKey.fromBuffer = function(buf) { + var info = PublicKey._transformDER(buf); + return new PublicKey(info.point, info.compressed); +}; + +/** + * + * Instantiate a PublicKey from a Point + * + * @param {Point} point - A Point instance + * @returns {PublicKey} A new valid instance of PublicKey + */ +PublicKey.fromPoint = function(point, compressed){ + if (!(point instanceof Point)) { + throw new TypeError('First argument must be an instance of Point.'); + } + return new PublicKey(point, compressed); +}; + +/** + * + * Instantiate a PublicKey from a DER Buffer + * + * @param {Buffer} buf - A DER Buffer + * @returns {PublicKey} A new valid instance of PublicKey + */ +PublicKey.fromDER = function(buf) { + var info = PublicKey._transformDER(buf); + return new PublicKey(info.point, info.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 = new Buffer(str, encoding || 'hex'); + var info = PublicKey._transformDER(buf); + return new PublicKey(info.point, 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, info.compressed); +}; + + +/** + * + * Check if there would be any errors when initializing a PublicKey + * + * @param {String} data - The encoded data in various formats + * @param {String} [compressed] - If the public key is compressed + * @returns {null|Error} An error if exists + */ +PublicKey.getValidationError = function(data, compressed) { + var error; + try { + new PublicKey(data, compressed); + } catch (e) { + error = e; + } + return error; +}; + +/** + * + * Check if the parameters are valid + * + * @param {String} data - The encoded data in various formats + * @param {String} [compressed] - If the public key is compressed + * @returns {Boolean} If the public key would be valid + */ +PublicKey.isValid = function(data, compressed) { + return !PublicKey.getValidationError(data, compressed); +}; + +/** + * + * Will output the PublicKey to JSON + * + * @returns {String} A hex encoded string + */ +PublicKey.prototype.toJSON = function() { + return this.toBuffer().toString('hex'); +}; + +/** + * + * Will output the PublicKey to a Buffer + * + * @returns {Buffer} A DER hex encoded buffer + */ +PublicKey.prototype.toBuffer = function() { + var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; + return this.toDER(compressed); +}; + +/** + * + * Will output the PublicKey to a DER Buffer + * + * @returns {Buffer} A DER hex encoded buffer + */ +PublicKey.prototype.toDER = function(compressed) { + compressed = typeof(compressed) !== 'undefined' ? compressed : this.compressed; + if (typeof compressed !== 'boolean') { + throw new TypeError('Must specify whether the public key is compressed or not (true or false)'); + } + + 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 (!compressed) { + prefix = new Buffer([0x04]); + return Buffer.concat([prefix, xbuf, ybuf]); + } else { + var odd = ybuf[ybuf.length - 1] % 2; + if (odd) { + prefix = new Buffer([0x03]); + } else { + prefix = new Buffer([0x02]); + } + return Buffer.concat([prefix, xbuf]); + } +}; + +/** + * + * Will output the PublicKey to a DER encoded hex string + * + * @returns {String} A DER hex encoded string + */ +PublicKey.prototype.toString = function() { + var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; + return this.toDER(compressed).toString('hex'); +}; + +/** + * + * Will return a string formatted for the console + * + * @returns {String} Public key + */ +PublicKey.prototype.inspect = function() { + return ''; +}; + +module.exports = PublicKey; diff --git a/lib/script.js b/lib/script.js index 95a6ac4..a33a999 100644 --- a/lib/script.js +++ b/lib/script.js @@ -190,7 +190,7 @@ Script.prototype.isOpReturn = function() { } }; -Script.prototype.isPubkeyhashOut = function() { +Script.prototype.isPublicKeyHashOut = function() { if (this.chunks[0] === Opcode('OP_DUP').toNumber() && this.chunks[1] === Opcode('OP_HASH160').toNumber() && this.chunks[2].buf @@ -202,7 +202,7 @@ Script.prototype.isPubkeyhashOut = function() { } }; -Script.prototype.isPubkeyhashIn = function() { +Script.prototype.isPublicKeyHashIn = function() { if (this.chunks.length === 2 && this.chunks[0].buf && this.chunks[1].buf) { @@ -212,7 +212,7 @@ Script.prototype.isPubkeyhashIn = function() { } }; -Script.prototype.isScripthashOut = function() { +Script.prototype.isScriptHashOut = function() { if (this.chunks.length === 3 && this.chunks[0] === Opcode('OP_HASH160').toNumber() && this.chunks[1].buf @@ -225,7 +225,7 @@ Script.prototype.isScripthashOut = function() { }; //note that these are frequently indistinguishable from pubkeyhashin -Script.prototype.isScripthashIn = function() { +Script.prototype.isScriptHashIn = function() { var allpush = this.chunks.every(function(chunk) { return Buffer.isBuffer(chunk.buf); }); diff --git a/test/address.js b/test/address.js index 6fb2019..80f363f 100644 --- a/test/address.js +++ b/test/address.js @@ -3,7 +3,7 @@ var should = require('chai').should(); var bitcore = require('..'); -var Pubkey = bitcore.Pubkey; +var PublicKey = bitcore.PublicKey; var Address = bitcore.Address; var Script = bitcore.Script; @@ -211,7 +211,7 @@ describe('Address', function() { it('should error because of incorrect format for pubkey hash', function() { (function() { - var a = new Address.fromPubkeyHash('notahash'); + var a = new Address.fromPublicKeyHash('notahash'); }).should.throw('Address supplied is not a buffer.'); }); @@ -235,8 +235,8 @@ describe('Address', function() { it('should error because of incorrect type for pubkey transform', function() { (function() { - var info = Address._transformPubkey(new Buffer(20)); - }).should.throw('Address must be an instance of Pubkey.'); + var info = Address._transformPublicKey(new Buffer(20)); + }).should.throw('Address must be an instance of PublicKey.'); }); it('should error because of incorrect type for script transform', function() { @@ -253,8 +253,8 @@ describe('Address', function() { it('should make an address from a pubkey hash buffer', function() { var hash = pubkeyhash; //use the same hash - var a = Address.fromPubkeyHash(hash).toString().should.equal(str); - var b = Address.fromPubkeyHash(hash, 'testnet'); + var a = Address.fromPublicKeyHash(hash).toString().should.equal(str); + var b = Address.fromPublicKeyHash(hash, 'testnet'); b.network.should.equal('testnet'); b.type.should.equal('pubkeyhash'); var c = new Address(hash).toString().should.equal(str); @@ -262,26 +262,19 @@ describe('Address', function() { it('should throw an error for invalid length hashBuffer', function() { (function() { - var a = Address.fromPubkeyHash(buf); + var a = Address.fromPublicKeyHash(buf); }).should.throw('Address hashbuffers must be exactly 20 bytes.'); }); - it('should make this address from a compressed pubkey object', function() { - var pubkey = new Pubkey(); - pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', - 'hex')); - var a = Address.fromPubkey(pubkey); - a.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke'); - var b = new Address(pubkey); - b.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke'); + it('should make this address from a compressed pubkey', function() { + var pubkey = PublicKey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex')); + var address = Address.fromPublicKey(pubkey); + address.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke'); }); it('should make this address from an uncompressed pubkey', function() { - var pubkey = new Pubkey(); - pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', - 'hex')); - pubkey.compressed = false; - var a = Address.fromPubkey(pubkey, 'mainnet', 'pubkeyhash'); + var pubkey = PublicKey.fromDER(new Buffer('0485e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004833fef26c8be4c4823754869ff4e46755b85d851077771c220e2610496a29d98', 'hex')); + var a = Address.fromPublicKey(pubkey, 'mainnet'); a.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); var b = new Address(pubkey, 'mainnet', 'pubkeyhash'); b.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); @@ -310,7 +303,7 @@ describe('Address', function() { var address = new Address(str); var buffer = address.toBuffer(); var slice = buffer.slice(1); - var sliceString = slice.toString('hex') + var sliceString = slice.toString('hex'); sliceString.should.equal(pubkeyhash.toString('hex')); }); diff --git a/test/crypto/ecdsa.js b/test/crypto/ecdsa.js index 48d66da..5252a5f 100644 --- a/test/crypto/ecdsa.js +++ b/test/crypto/ecdsa.js @@ -4,11 +4,10 @@ var should = require('chai').should(); var bitcore = require('../..'); var ECDSA = bitcore.crypto.ECDSA; var Hash = bitcore.crypto.Hash; -var Privkey = bitcore.Privkey; -var Pubkey = bitcore.Pubkey; +var PrivateKey = bitcore.PrivateKey; +var PublicKey = bitcore.PublicKey; var Signature = bitcore.Signature; var BN = bitcore.crypto.BN; -var point = bitcore.crypto.Point; describe('ECDSA', function() { @@ -19,11 +18,8 @@ describe('ECDSA', function() { var ecdsa = new ECDSA(); ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); - ecdsa.privkey = new Privkey({bn: BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))}); - ecdsa.pubkey = new Pubkey({ - point: point(BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), - BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex'))) - }); + ecdsa.privkey = new PrivateKey(BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))); + ecdsa.privkey2pubkey(); describe('#set', function() { @@ -47,8 +43,7 @@ describe('ECDSA', function() { var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10); var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10); var ecdsa = new ECDSA(); - ecdsa.privkey = Privkey(); - ecdsa.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test'))); + ecdsa.privkey = PrivateKey(BN().fromBuffer(Hash.sha256(new Buffer('test')))); ecdsa.privkey2pubkey(); ecdsa.hashbuf = hashbuf; ecdsa.sig = new Signature({r: r, s: s}); @@ -109,17 +104,10 @@ describe('ECDSA', function() { ecdsa.sigError().should.equal('hashbuf must be a 32 byte buffer'); }); - it('should return an error if the pubkey is invalid', function() { - var ecdsa = new ECDSA(); - ecdsa.hashbuf = Hash.sha256(new Buffer('test')); - ecdsa.sigError().indexOf("Invalid pubkey").should.equal(0); - }); - it('should return an error if r, s are invalid', function() { var ecdsa = new ECDSA(); ecdsa.hashbuf = Hash.sha256(new Buffer('test')); - var pk = new Pubkey(); - pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + var pk = PublicKey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); ecdsa.pubkey = pk; ecdsa.sig = new Signature(); ecdsa.sig.r = BN(0); diff --git a/test/privatekey.js b/test/privatekey.js new file mode 100644 index 0000000..76663d3 --- /dev/null +++ b/test/privatekey.js @@ -0,0 +1,270 @@ +'use strict'; + +var should = require('chai').should(); +var bitcore = require('..'); +var BN = bitcore.crypto.BN; +var Point = bitcore.crypto.Point; +var PrivateKey = bitcore.PrivateKey; +var base58check = bitcore.encoding.Base58Check; + +describe('PrivateKey', function() { + var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'; + var buf = new Buffer(hex, 'hex'); + var enctestnet = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; + var enctu = '92jJzK4tbURm1C7udQXxeCBvXHoHJstDXRxAMouPG1k1XUaXdsu'; + var encmainnet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy'; + var encmu = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un'; + + it('should create a new random private key', function() { + var a = new PrivateKey(); + should.exist(a); + should.exist(a.bn); + var b = PrivateKey(); + should.exist(b); + should.exist(b.bn); + }); + + it('should create a private key from WIF string', function() { + var a = new PrivateKey('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + should.exist(a); + should.exist(a.bn); + }); + + it('should create a private key from WIF buffer', function() { + var a = new PrivateKey(base58check.decode('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m')); + should.exist(a); + should.exist(a.bn); + }); + + it('should not be able to instantiate private key greater than N', function() { + (function() { + var n = Point.getN(); + var a = new PrivateKey(n); + }).should.throw('Number must be less than N'); + }); + + it('should not be able to instantiate private key because of network mismatch', function() { + (function() { + var a = new PrivateKey('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m', 'testnet'); + }).should.throw('Private key network mismatch'); + }); + + it('should not be able to instantiate private key because of compression mismatch', function() { + (function() { + var a = new PrivateKey('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m', 'mainnet', false); + }).should.throw('Private key compression mismatch'); + }); + + it('should not be able to instantiate private key WIF is too long', function() { + (function() { + var buf = base58check.decode('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + var buf2 = Buffer.concat([buf, new Buffer(0x01)]); + var a = new PrivateKey(buf2); + }).should.throw('Length of buffer must be 33 (uncompressed) or 34 (compressed'); + }); + + it('should not be able to instantiate private key WIF because of unknown network byte', function() { + (function() { + var buf = base58check.decode('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + var buf2 = Buffer.concat([new Buffer(0x01, 'hex'), buf.slice(1, 33)]); + var a = new PrivateKey(buf2); + }).should.throw('Invalid network'); + }); + + it('should not be able to instantiate because compressed is non-boolean', function() { + (function() { + var a = new PrivateKey(null, 'testnet', 'compressed'); + }).should.throw('Must specify whether the corresponding public key is compressed or not (true or false)'); + }); + + it('should not be able to instantiate because of unrecognized data', function() { + (function() { + var a = new PrivateKey(new Error()); + }).should.throw('First argument is an unrecognized data type.'); + }); + + it('should not be able to instantiate with unknown network', function() { + (function() { + var a = new PrivateKey(null, 'unknown'); + }).should.throw('Must specify the network ("mainnet" or "testnet")'); + }); + + it('should create a 0 private key with this convenience method', function() { + var bn = BN(0); + var privkey = new PrivateKey(bn); + privkey.bn.toString().should.equal(bn.toString()); + }); + + it('should create a mainnet private key', function() { + var privkey = new PrivateKey(BN.fromBuffer(buf), 'mainnet', true); + privkey.toString().should.equal(encmainnet); + }); + + it('should create an uncompressed testnet private key', function() { + var privkey = new PrivateKey(BN.fromBuffer(buf), 'testnet', false); + privkey.toString().should.equal(enctu); + }); + + it('should create an uncompressed mainnet private key', function() { + var privkey = new PrivateKey(BN.fromBuffer(buf), 'mainnet', false); + privkey.toString().should.equal(encmu); + }); + + describe('#fromJSON', function() { + + it('should input this address correctly', function() { + var privkey = PrivateKey.fromJSON(encmu); + privkey.toWIF().should.equal(encmu); + }); + + }); + + describe('#toString', function() { + + it('should output this address correctly', function() { + var privkey = PrivateKey.fromJSON(encmu); + privkey.toJSON().should.equal(encmu); + }); + + }); + + describe('#toAddress', function() { + it('should output this known mainnet address correctly', function() { + var privkey = PrivateKey.fromWIF('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + var address = privkey.toAddress(); + address.toString().should.equal('1A6ut1tWnUq1SEQLMr4ttDh24wcbJ5o9TT'); + }); + + it('should output this known testnet address correctly', function() { + var privkey = PrivateKey.fromWIF('cR4qogdN9UxLZJXCNFNwDRRZNeLRWuds9TTSuLNweFVjiaE4gPaq'); + var address = privkey.toAddress(); + address.toString().should.equal('mtX8nPZZdJ8d3QNLRJ1oJTiEi26Sj6LQXS'); + }); + + }); + + describe('#inspect', function() { + it('should output known mainnet address for console', function() { + var privkey = PrivateKey.fromWIF('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + privkey.inspect().should.equal(''); + }); + + it('should output known testnet address for console', function() { + var privkey = PrivateKey.fromWIF('cR4qogdN9UxLZJXCNFNwDRRZNeLRWuds9TTSuLNweFVjiaE4gPaq'); + privkey.inspect().should.equal(''); + }); + + }); + + describe('#getValidationError', function(){ + it('should get an error because private key greater than N', function() { + var n = Point.getN(); + var a = PrivateKey.getValidationError(n); + a.message.should.equal('Number must be less than N'); + }); + + it('should validate as false because private key greater than N', function() { + var n = Point.getN(); + var a = PrivateKey.isValid(n); + a.should.equal(false); + }); + + it('should validate as true', function() { + var a = PrivateKey.isValid('L3T1s1TYP9oyhHpXgkyLoJFGniEgkv2Jhi138d7R2yJ9F4QdDU2m'); + a.should.equal(true); + }); + + }); + + describe('#toBuffer', function() { + it('should output known buffer', function() { + var privkey = new PrivateKey(BN.fromBuffer(buf), 'mainnet', true); + var b = privkey.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + }); + + describe('#toBigNumber', function() { + it('should output known BN', function() { + var a = BN.fromBuffer(buf); + var privkey = new PrivateKey(a, 'mainnet', true); + var b = privkey.toBigNumber(); + b.toString('hex').should.equal(a.toString('hex')); + }); + }); + + describe('#fromRandom', function() { + + it('should set bn gt 0 and lt n, and should be compressed', function() { + var privkey = PrivateKey.fromRandom(); + privkey.bn.gt(BN(0)).should.equal(true); + privkey.bn.lt(Point.getN()).should.equal(true); + privkey.compressed.should.equal(true); + }); + + }); + + describe('#fromWIF', function() { + + it('should parse this compressed testnet address correctly', function() { + var privkey = PrivateKey.fromWIF(encmainnet); + privkey.toWIF().should.equal(encmainnet); + }); + + }); + + describe('#toWIF', function() { + + it('should parse this compressed testnet address correctly', function() { + var privkey = PrivateKey.fromWIF(enctestnet); + privkey.toWIF().should.equal(enctestnet); + }); + + }); + + describe('#fromString', function() { + + it('should parse this uncompressed testnet address correctly', function() { + var privkey = PrivateKey.fromString(enctu); + privkey.toWIF().should.equal(enctu); + }); + + }); + + describe('#toString', function() { + + it('should parse this uncompressed mainnet address correctly', function() { + var privkey = PrivateKey.fromString(encmu); + privkey.toString().should.equal(encmu); + }); + + }); + + describe("#toPublicKey", function() { + + it('should convert this known PrivateKey to known PublicKey', function() { + var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; + var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; + var privkey = new PrivateKey(BN(new Buffer(privhex, 'hex'))); + var pubkey = privkey.toPublicKey(); + pubkey.toString().should.equal(pubhex); + }); + + it('should convert this known PrivateKey to known PublicKey and preserve compressed=true', function() { + var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; + var privkey = new PrivateKey(BN(new Buffer(privhex, 'hex'))); + privkey.compressed = true; + var pubkey = privkey.toPublicKey(); + pubkey.compressed.should.equal(true); + }); + + it('should convert this known PrivateKey to known PublicKey and preserve compressed=true', function() { + var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; + var privkey = new PrivateKey(BN(new Buffer(privhex, 'hex'))); + privkey.compressed = false; + var pubkey = privkey.toPublicKey(); + pubkey.compressed.should.equal(false); + }); + + }); + +}); diff --git a/test/privkey.js b/test/privkey.js deleted file mode 100644 index 497fc9c..0000000 --- a/test/privkey.js +++ /dev/null @@ -1,171 +0,0 @@ -'use strict'; - -var should = require('chai').should(); -var bitcore = require('..'); -var BN = bitcore.crypto.BN; -var Point = bitcore.crypto.Point; -var Privkey = bitcore.Privkey; -var Pubkey = bitcore.Pubkey; - -describe('Privkey', function() { - var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'; - var buf = new Buffer(hex, 'hex'); - var enctestnet = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; - var enctu = '92jJzK4tbURm1C7udQXxeCBvXHoHJstDXRxAMouPG1k1XUaXdsu'; - var encmainnet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy'; - var encmu = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un'; - - it('should create an empty private key', function() { - var privkey = new Privkey(); - should.exist(privkey); - }); - - it('should create a 0 private key with this convenience method', function() { - var bn = BN(0); - var privkey = new Privkey(bn); - privkey.bn.toString().should.equal(bn.toString()); - }); - - it('should create a mainnet private key', function() { - var privkey = new Privkey({ - bn: BN.fromBuffer(buf), - networkstr: 'mainnet', - compressed: true - }); - privkey.toString().should.equal(encmainnet); - }); - - it('should create an uncompressed testnet private key', function() { - var privkey = new Privkey({ - bn: BN.fromBuffer(buf), - networkstr: 'testnet', - compressed: false - }); - privkey.toString().should.equal(enctu); - }); - - it('should create an uncompressed mainnet private key', function() { - var privkey = new Privkey({ - bn: BN.fromBuffer(buf), - networkstr: 'mainnet', - compressed: false - }); - privkey.toString().should.equal(encmu); - }); - - describe('#set', function() { - - it('should set bn', function() { - should.exist(Privkey().set({ - bn: BN.fromBuffer(buf) - }).bn); - }); - - }); - - describe('#fromJSON', function() { - - it('should input this address correctly', function() { - var privkey = new Privkey(); - privkey.fromJSON(encmu); - privkey.toWIF().should.equal(encmu); - }); - - }); - - describe('#toString', function() { - - it('should output this address correctly', function() { - var privkey = new Privkey(); - privkey.fromJSON(encmu); - privkey.toJSON().should.equal(encmu); - }); - - }); - - describe('#fromRandom', function() { - - it('should set bn gt 0 and lt n, and should be compressed', function() { - var privkey = Privkey().fromRandom(); - privkey.bn.gt(BN(0)).should.equal(true); - privkey.bn.lt(Point.getN()).should.equal(true); - privkey.compressed.should.equal(true); - }); - - }); - - describe('#fromWIF', function() { - - it('should parse this compressed testnet address correctly', function() { - var privkey = new Privkey(); - privkey.fromWIF(encmainnet); - privkey.toWIF().should.equal(encmainnet); - }); - - }); - - describe('#toWIF', function() { - - it('should parse this compressed testnet address correctly', function() { - var privkey = new Privkey(); - privkey.fromWIF(enctestnet); - privkey.toWIF().should.equal(enctestnet); - }); - - }); - - describe('#fromString', function() { - - it('should parse this uncompressed testnet address correctly', function() { - var privkey = new Privkey(); - privkey.fromString(enctu); - privkey.toWIF().should.equal(enctu); - }); - - }); - - describe('#toString', function() { - - it('should parse this uncompressed mainnet address correctly', function() { - var privkey = new Privkey(); - privkey.fromString(encmu); - privkey.toString().should.equal(encmu); - }); - - }); - - describe("#toPubkey", function() { - - it('should convert this known Privkey to known Pubkey', function() { - var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; - var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; - var privkey = new Privkey({ - bn: BN(new Buffer(privhex, 'hex')) - }); - var pubkey = privkey.toPubkey(); - pubkey.toString().should.equal(pubhex); - }); - - it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { - var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; - var privkey = new Privkey({ - bn: BN(new Buffer(privhex, 'hex')) - }); - privkey.compressed = true; - var pubkey = privkey.toPubkey(); - pubkey.compressed.should.equal(true); - }); - - it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { - var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; - var privkey = new Privkey({ - bn: BN(new Buffer(privhex, 'hex')) - }); - privkey.compressed = false; - var pubkey = privkey.toPubkey(); - pubkey.compressed.should.equal(false); - }); - - }); - -}); diff --git a/test/pubkey.js b/test/pubkey.js deleted file mode 100644 index be96a16..0000000 --- a/test/pubkey.js +++ /dev/null @@ -1,208 +0,0 @@ -'use strict'; - -var should = require('chai').should(); -var bitcore = require('..'); -var Point = bitcore.crypto.Point; -var BN = bitcore.crypto.BN; -var Pubkey = bitcore.Pubkey; -var Privkey = bitcore.Privkey; - -describe('Pubkey', function() { - - it('should create a blank public key', function() { - var pk = new Pubkey(); - should.exist(pk); - }); - - it('should create a public key with a point', function() { - var p = Point(); - var pk = new Pubkey({point: p}); - should.exist(pk.point); - }); - - it('should create a public key with a point with this convenient method', function() { - var p = Point(); - var pk = new Pubkey(p); - should.exist(pk.point); - pk.point.toString().should.equal(p.toString()); - }); - - describe('#set', function() { - - it('should make a public key from a point', function() { - should.exist(Pubkey().set({point: Point.getG()}).point); - }); - - }); - - describe('#fromJSON', function() { - - it('should input this public key', function() { - var pk = new Pubkey(); - pk.fromJSON('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - }); - - }); - - describe('#toJSON', function() { - - it('should output this pubkey', function() { - var pk = new Pubkey(); - var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'; - pk.fromJSON(hex).toJSON().should.equal(hex); - }); - - }); - - describe('#fromPrivkey', function() { - - it('should make a public key from a privkey', function() { - should.exist(Pubkey().fromPrivkey(Privkey().fromRandom())); - }); - - }); - - describe('#fromBuffer', function() { - - it('should parse this uncompressed public key', function() { - var pk = new Pubkey(); - pk.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); - pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - }); - - it('should parse this compressed public key', function() { - var pk = new Pubkey(); - pk.fromBuffer(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - }); - - it('should throw an error on this invalid public key', function() { - var pk = new Pubkey(); - (function() { - pk.fromBuffer(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - }).should.throw(); - }); - - }); - - describe('#fromDER', function() { - - it('should parse this uncompressed public key', function() { - var pk = new Pubkey(); - pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); - pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - }); - - it('should parse this compressed public key', function() { - var pk = new Pubkey(); - pk.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - }); - - it('should throw an error on this invalid public key', function() { - var pk = new Pubkey(); - (function() { - pk.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - }).should.throw(); - }); - - }); - - describe('#fromString', function() { - - it('should parse this known valid public key', function() { - var pk = new Pubkey(); - pk.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - }); - - }); - - describe('#fromX', function() { - - it('should create this known public key', function() { - var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new Pubkey(); - pk.fromX(true, x); - pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - }); - - }); - - describe('#toBuffer', function() { - - it('should return this compressed DER format', function() { - var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new Pubkey(); - pk.fromX(true, x); - pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - }); - - }); - - describe('#toDER', function() { - - it('should return this compressed DER format', function() { - var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new Pubkey(); - pk.fromX(true, x); - pk.toDER(true).toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - }); - - it('should return this uncompressed DER format', function() { - var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new Pubkey(); - pk.fromX(true, x); - pk.toDER(false).toString('hex').should.equal('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - }); - - }); - - describe('#toString', function() { - - it('should print this known public key', function() { - var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; - var pk = new Pubkey(); - pk.fromString(hex); - pk.toString().should.equal(hex); - }); - - }); - - describe('#validate', function() { - - it('should not throw an error if pubkey is valid', function() { - var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; - var pk = new Pubkey(); - pk.fromString(hex); - should.exist(pk.validate()); - }); - - it('should not throw an error if pubkey is invalid', function() { - var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a0000000000000000000000000000000000000000000000000000000000000000'; - var pk = new Pubkey(); - pk.fromString(hex); - (function() { - pk.validate(); - }).should.throw('Invalid y value of public key'); - }); - - it('should not throw an error if pubkey is infinity', function() { - var pk = new Pubkey(); - pk.point = Point.getG().mul(Point.getN()); - (function() { - pk.validate(); - }).should.throw('Point cannot be equal to Infinity'); - }); - - }); - -}); diff --git a/test/publickey.js b/test/publickey.js new file mode 100644 index 0000000..e9160f7 --- /dev/null +++ b/test/publickey.js @@ -0,0 +1,300 @@ +'use strict'; + +var should = require('chai').should(); +var bitcore = require('..'); +var Point = bitcore.crypto.Point; +var BN = bitcore.crypto.BN; +var PublicKey = bitcore.PublicKey; +var PrivateKey = bitcore.PrivateKey; + +describe('PublicKey', function() { + + it('should error because of missing data', function() { + (function() { + var pk = new PublicKey(); + }).should.throw('First argument is required, please include public key data.'); + }); + + it('should error because of an invalid point', function() { + (function() { + var pk = new PublicKey(Point()); + }).should.throw('Point cannot be equal to 0, 0'); + }); + + it('should error because of an invalid public key point, not on the secp256k1 curve', function() { + (function() { + var pk = new PublicKey(Point(1000, 1000)); + }).should.throw('Invalid y value of public key'); + }); + + it('should error because of an unrecognized data type', function() { + (function() { + var pk = new PublicKey(new Error()); + }).should.throw('First argument is an unrecognized data format.'); + }); + + it('should instantiate from a private key', function() { + var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; + var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; + var privkey = new PrivateKey(BN(new Buffer(privhex, 'hex'))); + var pk = new PublicKey(privkey); + pk.toString().should.equal(pubhex); + }); + + it('should instantiate from a hex encoded DER string', function() { + var pk = new PublicKey('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + should.exist(pk.point); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + }); + + it('should instantiate from a hex encoded DER buffer', function() { + var pk = new PublicKey(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + should.exist(pk.point); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + }); + + it('should create a public key with a point', function() { + var p = Point('86a80a5a2bfc48dddde2b0bd88bd56b0b6ddc4e6811445b175b90268924d7d48', + '3b402dfc89712cfe50963e670a0598e6b152b3cd94735001cdac6794975d3afd'); + var a = new PublicKey(p); + should.exist(a.point); + a.point.toString().should.equal(p.toString()); + var c = PublicKey(p); + should.exist(c.point); + c.point.toString().should.equal(p.toString()); + }); + + describe('#getValidationError', function(){ + it('should recieve an error message', function() { + var error = PublicKey.getValidationError(Point()); + should.exist(error); + }); + + it('should recieve a boolean as false', function() { + var valid = PublicKey.isValid(Point()); + valid.should.equal(false); + }); + + it('should recieve a boolean as true', function() { + var valid = PublicKey.isValid('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + valid.should.equal(true); + }); + + }); + + describe('#fromPoint', function() { + + it('should instantiate from a point', function() { + var p = Point('86a80a5a2bfc48dddde2b0bd88bd56b0b6ddc4e6811445b175b90268924d7d48', + '3b402dfc89712cfe50963e670a0598e6b152b3cd94735001cdac6794975d3afd'); + var b = PublicKey.fromPoint(p); + should.exist(b.point); + b.point.toString().should.equal(p.toString()); + }); + + it('should error because paramater is not a point', function() { + (function() { + PublicKey.fromPoint(new Error()); + }).should.throw('First argument must be an instance of Point.'); + }); + }); + + describe('#fromJSON', function() { + + it('should input this public key', function() { + var pk = PublicKey.fromJSON('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + }); + + describe('#toJSON', function() { + + it('should output this pubkey', function() { + var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'; + var pk = PublicKey.fromJSON(hex); + pk.toJSON().should.equal(hex); + }); + + }); + + describe('#fromPrivateKey', function() { + + it('should make a public key from a privkey', function() { + should.exist(PublicKey.fromPrivateKey(PrivateKey.fromRandom())); + }); + + it('should error because not an instance of privkey', function() { + (function() { + PublicKey.fromPrivateKey(new Error()); + }).should.throw('Must be an instance of PrivateKey'); + }); + + }); + + describe('#fromBuffer', function() { + + it('should parse this uncompressed public key', function() { + var pk = PublicKey.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + it('should parse this compressed public key', function() { + var pk = PublicKey.fromBuffer(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + it('should throw an error on this invalid public key', function() { + (function() { + PublicKey.fromBuffer(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + }).should.throw(); + }); + + it('should throw error because not a buffer', function() { + (function() { + PublicKey.fromBuffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + }).should.throw('Must be a hex buffer of DER encoded public key'); + }); + + it('should throw error because buffer is the incorrect length', function() { + (function() { + PublicKey.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a34112', 'hex')); + }).should.throw('Length of x and y must be 32 bytes'); + }); + + }); + + describe('#fromDER', function() { + + it('should parse this uncompressed public key', function() { + var pk = PublicKey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + it('should parse this compressed public key', function() { + var pk = PublicKey.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + it('should throw an error on this invalid public key', function() { + (function() { + PublicKey.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + }).should.throw(); + }); + + }); + + describe('#fromString', function() { + + it('should parse this known valid public key', function() { + var pk = PublicKey.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + }); + + describe('#fromX', function() { + + it('should create this known public key', function() { + var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = PublicKey.fromX(true, x); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + + it('should error because odd was not included as a param', function() { + var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + (function() { + var pk = PublicKey.fromX(null, x); + }).should.throw('Must specify whether y is odd or not (true or false)'); + }); + + }); + + describe('#toBuffer', function() { + + it('should return this compressed DER format', function() { + var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = PublicKey.fromX(true, x); + pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + }); + + }); + + describe('#toDER', function() { + + it('should return this compressed DER format', function() { + var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = PublicKey.fromX(true, x); + pk.toDER(true).toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + }); + + it('should return this uncompressed DER format', function() { + var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = PublicKey.fromX(true, x); + pk.toDER(false).toString('hex').should.equal('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + it('should error because compressed param is invalid', function() { + var x = BN.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = PublicKey.fromX(true, x); + (function() { + pk.toDER('false'); //string not boolean + }).should.throw('Must specify whether the public key is compressed or not (true or false)'); + }); + + }); + + describe('#toString', function() { + + it('should print this known public key', function() { + var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; + var pk = PublicKey.fromString(hex); + pk.toString().should.equal(hex); + }); + + }); + + describe('#inspect', function() { + it('should output known uncompressed pubkey for console', function() { + var pubkey = PublicKey.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pubkey.inspect().should.equal(''); + }); + + it('should output known compressed pubkey for console', function() { + var pubkey = PublicKey.fromString('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pubkey.inspect().should.equal(''); + }); + + }); + + describe('#validate', function() { + + it('should not have an error if pubkey is valid', function() { + var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; + var pk = PublicKey.fromString(hex); + }); + + it('should throw an error if pubkey is invalid', function() { + var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a0000000000000000000000000000000000000000000000000000000000000000'; + (function() { + var pk = PublicKey.fromString(hex); + }).should.throw('Invalid y value of public key'); + }); + + it('should throw an error if pubkey is infinity', function() { + (function() { + var pk = new PublicKey(Point.getG().mul(Point.getN())); + }).should.throw('Point cannot be equal to Infinity'); + }); + + }); + +}); diff --git a/test/script.js b/test/script.js index 601fdc4..b4ed14f 100644 --- a/test/script.js +++ b/test/script.js @@ -218,26 +218,26 @@ describe('Script', function() { }); - describe('#isPubkeyhashIn', function() { + describe('#isPublicKeyHashIn', function() { it('should classify this known pubkeyhashin', function() { - Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6').isPubkeyhashIn().should.equal(true); + Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6').isPublicKeyHashIn().should.equal(true); }); it('should classify this known non-pubkeyhashin', function() { - Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6 OP_CHECKSIG').isPubkeyhashIn().should.equal(false); + Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6 OP_CHECKSIG').isPublicKeyHashIn().should.equal(false); }); }); - describe('#isPubkeyhashOut', function() { + describe('#isPublicKeyHashOut', function() { it('should classify this known pubkeyhashout as pubkeyhashout', function() { - Script('OP_DUP OP_HASH160 20 0000000000000000000000000000000000000000 OP_EQUALVERIFY OP_CHECKSIG').isPubkeyhashOut().should.equal(true); + Script('OP_DUP OP_HASH160 20 0000000000000000000000000000000000000000 OP_EQUALVERIFY OP_CHECKSIG').isPublicKeyHashOut().should.equal(true); }); it('should classify this known non-pubkeyhashout as not pubkeyhashout', function() { - Script('OP_DUP OP_HASH160 20 0000000000000000000000000000000000000000').isPubkeyhashOut().should.equal(false) + Script('OP_DUP OP_HASH160 20 0000000000000000000000000000000000000000').isPublicKeyHashOut().should.equal(false) }); }); @@ -245,11 +245,11 @@ describe('Script', function() { describe('#isScripthashIn', function() { it('should classify this known scripthashin', function() { - Script('20 0000000000000000000000000000000000000000').isScripthashIn().should.equal(true); + Script('20 0000000000000000000000000000000000000000').isScriptHashIn().should.equal(true); }); it('should classify this known non-scripthashin', function() { - Script('20 0000000000000000000000000000000000000000 OP_CHECKSIG').isScripthashIn().should.equal(false); + Script('20 0000000000000000000000000000000000000000 OP_CHECKSIG').isScriptHashIn().should.equal(false); }); }); @@ -257,12 +257,12 @@ describe('Script', function() { describe('#isScripthashOut', function() { it('should classify this known pubkeyhashout as pubkeyhashout', function() { - Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL').isScripthashOut().should.equal(true); + Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL').isScriptHashOut().should.equal(true); }); it('should classify these known non-pubkeyhashout as not pubkeyhashout', function() { - Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL OP_EQUAL').isScripthashOut().should.equal(false); - Script('OP_HASH160 21 0x000000000000000000000000000000000000000000 OP_EQUAL').isScripthashOut().should.equal(false); + Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL OP_EQUAL').isScriptHashOut().should.equal(false); + Script('OP_HASH160 21 0x000000000000000000000000000000000000000000 OP_EQUAL').isScriptHashOut().should.equal(false); }); });