From 90341f2ab6791b49f8ed77f45444049830f28d1e Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 18 Nov 2014 21:18:07 -0500 Subject: [PATCH 1/7] Address: Modified interface so that any instance of Address can be assumed to be valid. --- lib/address.js | 382 ++++++++++++++++++++++++++++++++++++++---------- test/address.js | 353 ++++++++++++++++++++++++++++---------------- 2 files changed, 531 insertions(+), 204 deletions(-) diff --git a/lib/address.js b/lib/address.js index 39b993d..51bf621 100644 --- a/lib/address.js +++ b/lib/address.js @@ -4,117 +4,341 @@ var base58check = require('./encoding/base58check'); var networks = require('./networks'); var Hash = require('./crypto/hash'); -function Address(buf) { - if (!(this instanceof Address)) - return new Address(buf); - if (Buffer.isBuffer(buf)) { - this.fromBuffer(buf); - } else if (typeof buf === 'string') { - var str = buf; - this.fromString(str); - } else if (buf) { - var obj = buf; - this.set(obj); +/** + * + * 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. + * + * @example + * + * var address = new Address(keypair.pubkey, 'testnet').toString(); + * + * @param {String} data - The encoded data in various formats + * @param {String} [network] - The network: 'mainnet' or 'testnet' + * @param {String} [type] - The type of address: 'script' or 'pubkey' + * @returns {Address} A new valid and frozen instance of an Address + */ +function Address(data, network, type) { + + if (!data) { + throw Error('Please include address data'); } -} -Address.prototype.set = function(obj) { - this.hashbuf = obj.hashbuf || this.hashbuf || null; - this.networkstr = obj.networkstr || this.networkstr || 'mainnet'; - this.typestr = obj.typestr || this.typestr || 'pubkeyhash'; - return this; -}; + if (network && (network !== 'mainnet' && network !== 'testnet')) { + throw new Error('Network must be "mainnet" or "testnet"'); + } -Address.prototype.fromBuffer = function(buf) { - if (buf.length !== 1 + 20) - throw new Error('Address buffers must be exactly 21 bytes'); - var version = buf[0]; - if (version === networks['mainnet']['pubkeyhash']) { - this.networkstr = 'mainnet'; - this.typestr = 'pubkeyhash'; - } else if (version === networks['mainnet']['scripthash']) { - this.networkstr = 'mainnet'; - this.typestr = 'scripthash'; - } else if (version === networks['testnet']['pubkeyhash']) { - this.networkstr = 'testnet'; - this.typestr = 'pubkeyhash'; - } else if (version === networks['testnet']['scripthash']) { - this.networkstr = 'testnet'; - this.typestr = 'scripthash'; + if (type && (type !== 'pubkeyhash' && type !== 'scripthash')) { + throw new Error('Type must be "pubkeyhash" or "scripthash"'); + } + + var info; + + // transform and validate input data + if (data instanceof Buffer && data.length === 20) { + info = Address._transformHash(data); + } else if (data instanceof Buffer && 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 === 'Script')) { + info = Address._transformScript(data); + } else if (typeof(data) === 'string') { + info = Address._transformString(data, network, type); } else { - this.networkstr = 'unknown'; - this.typestr = 'unknown'; + throw new Error('Unrecognized data format'); } - this.hashbuf = buf.slice(1); + // set defaults is not set + info.network = info.network || network || 'mainnet'; + info.type = info.type || type || 'pubkeyhash'; + + // set the validated values + this.hashBuffer = info.hashBuffer; + this.network = info.network; + this.type = info.type; return this; + +} + +/** + * + * Internal function to transform a hash buffer + * + * @param {Buffer} hash - An instance of a hash Buffer + * @returns {Object} An object with keys: hashBuffer + */ +Address._transformHash = function(hash){ + var info = {}; + if (!hash instanceof Buffer) { + throw new Error('Address supplied is not a buffer'); + } + if (hash.length !== 20) { + throw new Error('Address hashbuffers must be exactly a 20 bytes'); + } + info.hashBuffer = hash; + return info; +} + +/** + * + * Internal function to transform a bitcoin address buffer + * + * @param {Buffer} buffer - An instance of a hex encoded address Buffer + * @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 + */ +Address._transformBuffer = function(buffer, network, type){ + var info = {}; + if (!buffer instanceof Buffer) { + throw new Error('Address supplied is not a buffer'); + } + if (buffer.length !== 1 + 20) { + throw new Error('Address buffers must be exactly 21 bytes'); + } + + var bufNetwork = false; + var bufType = false; + + switch(buffer[0]){ // the version byte + case networks.mainnet.pubkeyhash: + bufNetwork = 'mainnet'; + bufType = 'pubkeyhash'; + break; + + case networks.mainnet.scripthash: + bufNetwork = 'mainnet'; + bufType = 'scripthash'; + break; + + case networks.testnet.pubkeyhash: + bufNetwork = 'testnet'; + bufType = 'pubkeyhash'; + break; + + case networks.testnet.scripthash: + bufNetwork = 'testnet'; + bufType = 'scripthash'; + break; + } + + if (!bufNetwork || (network && network !== bufNetwork)) { + throw new Error('Address has mismatched network type'); + } + + if (!bufType || ( type && type !== bufType )) { + throw new Error('Address has mismatched type'); + } + + info.hashBuffer = buffer.slice(1); + info.network = bufNetwork; + info.type = bufType; + return info; +} + +/** + * + * Internal function to transform a Pubkey + * + * @param {Pubkey} pubkey - An instance of Pubkey + * @returns {Object} An object with keys: hashBuffer, type + */ +Address._transformPubkey = function(pubkey){ + var info = {}; + if (!pubkey.constructor || (pubkey.constructor.name && pubkey.constructor.name !== 'Pubkey')) { + throw new Error('Address must be an instance of Pubkey'); + } + info.hashBuffer = Hash.sha256ripemd160(pubkey.toBuffer()); + info.type = 'pubkeyhash'; + return info; +} + +/** + * + * Internal function to transform a Script + * + * @param {Script} script - An instance of Script + * @returns {Object} An object with keys: hashBuffer, type + */ +Address._transformScript = function(script){ + var info = {}; + if (!script.constructor || (script.constructor.name && script.constructor.name !== 'Script')) { + throw new Error('Address must be an instance of Script'); + } + info.hashBuffer = Hash.sha256ripemd160(script.toBuffer()); + info.type = 'scripthash'; + return info; +} + +/** + * + * Internal function to transform a bitcoin address string + * + * @param {String} data - An instance of Pubkey + * @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 + */ +Address._transformString = function(data, network, type){ + if( typeof(data) !== 'string' ) { + throw Error('Address supplied is not a string'); + } + var addressBuffer = base58check.decode(data); + var info = Address._transformBuffer(addressBuffer, network, type); + return info; +} + +/** + * + * Instantiate an address from a Pubkey instance + * + * @param {String} data - An instance of Pubkey + * @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); + return new Address(info.hashBuffer, network, info.type); }; -Address.prototype.fromHashbuf = function(hashbuf, networkstr, typestr) { - if (hashbuf.length !== 20) - throw new Error('hashbuf must be exactly 20 bytes'); - this.hashbuf = hashbuf; - this.networkstr = networkstr || 'mainnet'; - this.typestr = typestr || 'pubkeyhash'; - return this; +/** + * + * Instantiate an address from a ripemd160 public key hash + * + * @param {Buffer} hash - An instance of buffer of the hash + * @param {String} network - The network: 'mainnet' or 'testnet' + * @returns {Address} A new valid and frozen instance of an Address + */ +Address.fromPubkeyHash = function(hash, network) { + var info = Address._transformHash(hash); + return new Address(info.hashBuffer, network, 'pubkeyhash'); }; -Address.prototype.fromPubkey = function(pubkey, networkstr) { - this.hashbuf = Hash.sha256ripemd160(pubkey.toBuffer()); - this.networkstr = networkstr || 'mainnet'; - this.typestr = 'pubkeyhash'; - return this; +/** + * + * Instantiate an address from a ripemd160 script hash + * + * @param {Buffer} hash - An instance of buffer of the hash + * @param {String} network - The network: 'mainnet' or 'testnet' + * @returns {Address} A new valid and frozen instance of an Address + */ +Address.fromScriptHash = function(hash, network) { + var info = Address._transformHash(hash); + return new Address(info.hashBuffer, network, 'scripthash'); }; -Address.prototype.fromScript = function(script, networkstr) { - this.hashbuf = Hash.sha256ripemd160(script.toBuffer()); - this.networkstr = networkstr || 'mainnet'; - this.typestr = 'scripthash'; - return this; +/** + * + * Instantiate an address from a Script + * + * @param {Script} script - An instance of Script + * @param {String} network - The network: 'mainnet' or 'testnet' + * @returns {Address} A new valid and frozen instance of an Address + */ +Address.fromScript = function(script, network) { + var info = Address._transformScript(script); + return new Address(info.hashBuffer, network, info.type); }; -Address.prototype.fromString = function(str) { - var buf = base58check.decode(str); - return this.fromBuffer(buf); -} +/** + * + * Instantiate an address from a buffer of the address + * + * @param {Buffer} buffer - An instance of buffer of the address + * @param {String} [network] - The network: 'mainnet' or 'testnet' + * @param {String} [type] - The type of address: 'script' or 'pubkey' + * @returns {Address} A new valid and frozen instance of an Address + */ +Address.fromBuffer = function(buffer, network, type) { + var info = Address._transformBuffer(buffer, network, type); + return new Address(info.hashBuffer, info.network, info.type); +}; -Address.isValid = function(addrstr) { +/** + * + * Instantiate an address from an address string + * + * @param {String} str - An string of the bitcoin address + * @param {String} [network] - The network: 'mainnet' or 'testnet' + * @param {String} [type] - The type of address: 'script' or 'pubkey' + * @returns {Address} A new valid and frozen instance of an Address + */ +Address.fromString = function(str, network, type) { + var info = Address._transformString(str, network, type); + return new Address(info.hashBuffer, info.network, info.type); +}; + +/** + * + * Will return a validation error if exists + * + * @example + * + * var error = Address.getValidationError('15vkcKf7gB23wLAnZLmbVuMiiVDc1Nm4a2', 'testnet'); + * // a network mismatch error + * + * @param {String} data - The encoded data + * @param {String} network - The network: 'mainnet' or 'testnet' + * @param {String} type - The type of address: 'script' or 'pubkey' + * @returns {null|Error} The corresponding error message + */ +Address.getValidationError = function(data, network, type) { + var error; try { - var address = new Address().fromString(addrstr); + new Address(data, network, type); } catch (e) { - return false; + error = e; } - return address.isValid(); + return error; }; -Address.prototype.isValid = function() { - try { - this.validate(); - return true; - } catch (e) { +/** + * + * Will return a boolean if an address is valid + * + * @example + * + * var valid = Address.isValid('15vkcKf7gB23wLAnZLmbVuMiiVDc1Nm4a2', 'mainnet'); + * // true + * + * @param {String} data - The encoded data + * @param {String} network - The network: 'mainnet' or 'testnet' + * @param {String} type - The type of address: 'script' or 'pubkey' + * @returns {null|Error} The corresponding error message + */ +Address.isValid = function(data, network, type) { + var error = Address.getValidationError(data, network, type); + if (error) { return false; + } else { + return true; } }; +/** + * + * Will return a buffer representation of the address + * + * @returns {Buffer} Bitcoin address buffer + */ Address.prototype.toBuffer = function() { - var version = new Buffer([networks[this.networkstr][this.typestr]]); - var buf = Buffer.concat([version, this.hashbuf]); + var version = new Buffer([networks[this.network][this.type]]); + var buf = Buffer.concat([version, this.hashBuffer]); return buf; }; +/** + * + * Will return a the string representation of the address + * + * @returns {String} Bitcoin address + */ Address.prototype.toString = function() { return base58check.encode(this.toBuffer()); }; -Address.prototype.validate = function() { - if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 20) - throw new Error('hash must be a buffer of 20 bytes'); - if (this.networkstr !== 'mainnet' && this.networkstr !== 'testnet') - throw new Error('networkstr must be "mainnet" or "testnet"'); - if (this.typestr !== 'pubkeyhash' && this.typestr !== 'scripthash') - throw new Error('typestr must be "pubkeyhash" or "scripthash"'); - return this; -}; - module.exports = Address; diff --git a/test/address.js b/test/address.js index 7eecc10..66b3324 100644 --- a/test/address.js +++ b/test/address.js @@ -8,149 +8,267 @@ var Address = bitcore.Address; var Script = bitcore.Script; describe('Address', function() { + var pubkeyhash = new Buffer('3c3fa3d4adcaf8f52d5b1843975e122548269937', 'hex'); var buf = Buffer.concat([new Buffer([0]), pubkeyhash]); var str = '16VZnHwRhwrExfeHFHGjwrgEMq8VcYPs9r'; + var strTest = 'n28S35tqEMbt6vNad7A5K3mZ7vdn8dZ86X'; + + it('should throw an error because of missing data', function() { + (function() { + var a = new Address(); + }).should.throw('Please include address data'); + }); - it('should create a new address object', function() { - var address = new Address(); - should.exist(address); - address = new Address(buf); - should.exist(address); - address = new Address(str); - should.exist(address); + it('should throw an error because of bad network param', function() { + (function(){ + var a = new Address(validAddresses[0], 'main', 'pubkeyhash'); + }).should.throw('Network must be "mainnet" or "testnet"'); }); - describe('@isValid', function() { + it('should throw an error because of bad type param', function() { + (function() { + var a = new Address(validAddresses[0], 'mainnet', 'pubkey'); + }).should.throw('Type must be "pubkeyhash" or "scripthash"'); + }); - it('should validate this valid address string', function() { - Address.isValid(str).should.equal(true); + + // mainnet valid + var validAddresses = [ + '15vkcKf7gB23wLAnZLmbVuMiiVDc1Nm4a2', + '1A6ut1tWnUq1SEQLMr4ttDh24wcbJ5o9TT', + '1BpbpfLdY7oBS9gK7aDXgvMgr1DPvNhEB2', + '1Jz2yCRd5ST1p2gUqFB5wsSQfdm3jaFfg7' + ]; + + // mainnet p2sh + var validp2shAddresses = [ + '342ftSRCvFHfCeFFBuz4xwbeqnDw6BGUey', + '33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk', + '37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3', + '3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy' + ]; + + // testnet p2sh + var testValidp2shAddresses = [ + '2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C', + '2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda', + '2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN', + '2NB72XtkjpnATMggui83aEtPawyyKvnbX2o' + ]; + + //mainnet bad checksums + var badChecksums = [ + '15vkcKf7gB23wLAnZLmbVuMiiVDc3nq4a2', + '1A6ut1tWnUq1SEQLMr4ttDh24wcbj4w2TT', + '1BpbpfLdY7oBS9gK7aDXgvMgr1DpvNH3B2', + '1Jz2yCRd5ST1p2gUqFB5wsSQfdmEJaffg7' + ]; + + //mainnet non-base58 + var nonBase58 = [ + '15vkcKf7g#23wLAnZLmb$uMiiVDc3nq4a2', + '1A601ttWnUq1SEQLMr4ttDh24wcbj4w2TT', + '1BpbpfLdY7oBS9gK7aIXgvMgr1DpvNH3B2', + '1Jz2yCRdOST1p2gUqFB5wsSQfdmEJaffg7' + ]; + + //testnet valid + var testValidAddresses = [ + 'n28S35tqEMbt6vNad7A5K3mZ7vdn8dZ86X', + 'n45x3R2w2jaSC62BMa9MeJCd3TXxgvDEmm', + 'mursDVxqNQmmwWHACpM9VHwVVSfTddGsEM', + 'mtX8nPZZdJ8d3QNLRJ1oJTiEi26Sj6LQXS' + ]; + + describe('validation', function() { + + it('should describe this mainnet address as an invalid testnet address', function() { + var error = Address.getValidationError('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo', 'testnet'); + should.exist(error); }); - it('should invalidate this valid address string', function() { - Address.isValid(str.substr(1)).should.equal(false); + it('should validate addresses', function() { + for(var i=0;i Date: Fri, 21 Nov 2014 14:31:34 -0500 Subject: [PATCH 2/7] Address: Add missing semicolons --- lib/address.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/address.js b/lib/address.js index 51bf621..15c74e4 100644 --- a/lib/address.js +++ b/lib/address.js @@ -81,7 +81,7 @@ Address._transformHash = function(hash){ } info.hashBuffer = hash; return info; -} +}; /** * @@ -138,7 +138,7 @@ Address._transformBuffer = function(buffer, network, type){ info.network = bufNetwork; info.type = bufType; return info; -} +}; /** * @@ -155,7 +155,7 @@ Address._transformPubkey = function(pubkey){ info.hashBuffer = Hash.sha256ripemd160(pubkey.toBuffer()); info.type = 'pubkeyhash'; return info; -} +}; /** * @@ -172,7 +172,7 @@ Address._transformScript = function(script){ info.hashBuffer = Hash.sha256ripemd160(script.toBuffer()); info.type = 'scripthash'; return info; -} +}; /** * @@ -190,7 +190,7 @@ Address._transformString = function(data, network, type){ var addressBuffer = base58check.decode(data); var info = Address._transformBuffer(addressBuffer, network, type); return info; -} +}; /** * From 0df97a42fe91e20a90dd14fe13b07e31aef68687 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 21 Nov 2014 19:02:58 -0500 Subject: [PATCH 3/7] Address: Fix indentation, typos and simplify isValid --- lib/address.js | 46 +++++++++++++++++++++------------------------- test/address.js | 10 ++++++++++ 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/lib/address.js b/lib/address.js index 15c74e4..5304a7f 100644 --- a/lib/address.js +++ b/lib/address.js @@ -4,6 +4,7 @@ var base58check = require('./encoding/base58check'); var networks = require('./networks'); var Hash = require('./crypto/hash'); + /** * * Bitcore Address @@ -51,7 +52,7 @@ function Address(data, network, type) { throw new Error('Unrecognized data format'); } - // set defaults is not set + // set defaults if not set info.network = info.network || network || 'mainnet'; info.type = info.type || type || 'pubkeyhash'; @@ -105,25 +106,25 @@ Address._transformBuffer = function(buffer, network, type){ var bufType = false; switch(buffer[0]){ // the version byte - case networks.mainnet.pubkeyhash: - bufNetwork = 'mainnet'; - bufType = 'pubkeyhash'; - break; - - case networks.mainnet.scripthash: - bufNetwork = 'mainnet'; - bufType = 'scripthash'; - break; + case networks.mainnet.pubkeyhash: + bufNetwork = 'mainnet'; + bufType = 'pubkeyhash'; + break; + + case networks.mainnet.scripthash: + bufNetwork = 'mainnet'; + bufType = 'scripthash'; + break; - case networks.testnet.pubkeyhash: - bufNetwork = 'testnet'; - bufType = 'pubkeyhash'; - break; - - case networks.testnet.scripthash: - bufNetwork = 'testnet'; - bufType = 'scripthash'; - break; + case networks.testnet.pubkeyhash: + bufNetwork = 'testnet'; + bufType = 'pubkeyhash'; + break; + + case networks.testnet.scripthash: + bufNetwork = 'testnet'; + bufType = 'scripthash'; + break; } if (!bufNetwork || (network && network !== bufNetwork)) { @@ -311,12 +312,7 @@ Address.getValidationError = function(data, network, type) { * @returns {null|Error} The corresponding error message */ Address.isValid = function(data, network, type) { - var error = Address.getValidationError(data, network, type); - if (error) { - return false; - } else { - return true; - } + return !Address.getValidationError(data, network, type); }; /** diff --git a/test/address.js b/test/address.js index 66b3324..62ceec5 100644 --- a/test/address.js +++ b/test/address.js @@ -88,6 +88,16 @@ describe('Address', function() { should.exist(error); }); + it('should should return a true boolean', function(){ + var valid = Address.isValid('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo', 'mainnet'); + valid.should.equal(true); + }); + + it('should should return a false boolean', function(){ + var valid = Address.isValid('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo', 'testnet'); + valid.should.equal(false); + }); + it('should validate addresses', function() { for(var i=0;i Date: Fri, 21 Nov 2014 19:58:47 -0500 Subject: [PATCH 4/7] Address: Fix Buffer data recognition in browsers --- lib/address.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/address.js b/lib/address.js index 5304a7f..64a1b9d 100644 --- a/lib/address.js +++ b/lib/address.js @@ -4,7 +4,6 @@ var base58check = require('./encoding/base58check'); var networks = require('./networks'); var Hash = require('./crypto/hash'); - /** * * Bitcore Address @@ -38,9 +37,9 @@ function Address(data, network, type) { var info; // transform and validate input data - if (data instanceof Buffer && data.length === 20) { + if ((data instanceof Buffer || data instanceof Uint8Array) && data.length === 20) { info = Address._transformHash(data); - } else if (data instanceof Buffer && data.length === 21) { + } 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); @@ -74,7 +73,7 @@ function Address(data, network, type) { */ Address._transformHash = function(hash){ var info = {}; - if (!hash instanceof Buffer) { + if (!(hash instanceof Buffer) && !(hash instanceof Uint8Array)) { throw new Error('Address supplied is not a buffer'); } if (hash.length !== 20) { @@ -95,7 +94,7 @@ Address._transformHash = function(hash){ */ Address._transformBuffer = function(buffer, network, type){ var info = {}; - if (!buffer instanceof Buffer) { + if (!(buffer instanceof Buffer) && !(buffer instanceof Uint8Array)) { throw new Error('Address supplied is not a buffer'); } if (buffer.length !== 1 + 20) { From 3c9cc23501b1903e4a7b746dd2587c259c710597 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 21 Nov 2014 22:18:28 -0500 Subject: [PATCH 5/7] Address: Improved error messages and type, and added inspect prototype for console output. --- lib/address.js | 37 ++++++++++++++++++++++++------------- test/address.js | 14 +++++++------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/address.js b/lib/address.js index 64a1b9d..82fd86b 100644 --- a/lib/address.js +++ b/lib/address.js @@ -23,15 +23,15 @@ var Hash = require('./crypto/hash'); function Address(data, network, type) { if (!data) { - throw Error('Please include address data'); + throw new TypeError('First argument is required, please include address data.'); } if (network && (network !== 'mainnet' && network !== 'testnet')) { - throw new Error('Network must be "mainnet" or "testnet"'); + throw new TypeError('Second argument must be "mainnet" or "testnet".'); } if (type && (type !== 'pubkeyhash' && type !== 'scripthash')) { - throw new Error('Type must be "pubkeyhash" or "scripthash"'); + throw new TypeError('Third argument must be "pubkeyhash" or "scripthash".'); } var info; @@ -48,7 +48,7 @@ function Address(data, network, type) { } else if (typeof(data) === 'string') { info = Address._transformString(data, network, type); } else { - throw new Error('Unrecognized data format'); + throw new TypeError('First argument is an unrecognized data format.'); } // set defaults if not set @@ -74,10 +74,10 @@ function Address(data, network, type) { Address._transformHash = function(hash){ var info = {}; if (!(hash instanceof Buffer) && !(hash instanceof Uint8Array)) { - throw new Error('Address supplied is not a buffer'); + throw new TypeError('Address supplied is not a buffer.'); } if (hash.length !== 20) { - throw new Error('Address hashbuffers must be exactly a 20 bytes'); + throw new TypeError('Address hashbuffers must be exactly a 20 bytes.'); } info.hashBuffer = hash; return info; @@ -95,10 +95,10 @@ Address._transformHash = function(hash){ Address._transformBuffer = function(buffer, network, type){ var info = {}; if (!(buffer instanceof Buffer) && !(buffer instanceof Uint8Array)) { - throw new Error('Address supplied is not a buffer'); + throw new TypeError('Address supplied is not a buffer.'); } if (buffer.length !== 1 + 20) { - throw new Error('Address buffers must be exactly 21 bytes'); + throw new TypeError('Address buffers must be exactly 21 bytes.'); } var bufNetwork = false; @@ -127,11 +127,11 @@ Address._transformBuffer = function(buffer, network, type){ } if (!bufNetwork || (network && network !== bufNetwork)) { - throw new Error('Address has mismatched network type'); + throw new TypeError('Address has mismatched network type.'); } if (!bufType || ( type && type !== bufType )) { - throw new Error('Address has mismatched type'); + throw new TypeError('Address has mismatched type.'); } info.hashBuffer = buffer.slice(1); @@ -150,7 +150,7 @@ Address._transformBuffer = function(buffer, network, type){ Address._transformPubkey = function(pubkey){ var info = {}; if (!pubkey.constructor || (pubkey.constructor.name && pubkey.constructor.name !== 'Pubkey')) { - throw new Error('Address must be an instance of Pubkey'); + throw new TypeError('Address must be an instance of Pubkey.'); } info.hashBuffer = Hash.sha256ripemd160(pubkey.toBuffer()); info.type = 'pubkeyhash'; @@ -167,7 +167,7 @@ Address._transformPubkey = function(pubkey){ Address._transformScript = function(script){ var info = {}; if (!script.constructor || (script.constructor.name && script.constructor.name !== 'Script')) { - throw new Error('Address must be an instance of Script'); + throw new TypeError('Address must be an instance of Script.'); } info.hashBuffer = Hash.sha256ripemd160(script.toBuffer()); info.type = 'scripthash'; @@ -185,7 +185,7 @@ Address._transformScript = function(script){ */ Address._transformString = function(data, network, type){ if( typeof(data) !== 'string' ) { - throw Error('Address supplied is not a string'); + throw new TypeError('Address supplied is not a string.'); } var addressBuffer = base58check.decode(data); var info = Address._transformBuffer(addressBuffer, network, type); @@ -336,4 +336,15 @@ Address.prototype.toString = function() { return base58check.encode(this.toBuffer()); }; +/** + * + * Will return a string formatted for the console + * + * @returns {String} Bitcoin address + */ +Address.prototype.inspect = function() { + return ''; +} + + module.exports = Address; diff --git a/test/address.js b/test/address.js index 62ceec5..b74c757 100644 --- a/test/address.js +++ b/test/address.js @@ -17,19 +17,19 @@ describe('Address', function() { it('should throw an error because of missing data', function() { (function() { var a = new Address(); - }).should.throw('Please include address data'); + }).should.throw('First argument is required, please include address data.'); }); it('should throw an error because of bad network param', function() { (function(){ var a = new Address(validAddresses[0], 'main', 'pubkeyhash'); - }).should.throw('Network must be "mainnet" or "testnet"'); + }).should.throw('Second argument must be "mainnet" or "testnet".'); }); it('should throw an error because of bad type param', function() { (function() { var a = new Address(validAddresses[0], 'mainnet', 'pubkey'); - }).should.throw('Type must be "pubkeyhash" or "scripthash"'); + }).should.throw('Third argument must be "pubkeyhash" or "scripthash"'); }); @@ -145,7 +145,7 @@ describe('Address', function() { for(var i=0;i Date: Sun, 23 Nov 2014 15:34:19 -0500 Subject: [PATCH 6/7] Address: Increase test coverage --- lib/address.js | 2 +- test/address.js | 63 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/lib/address.js b/lib/address.js index 82fd86b..603047f 100644 --- a/lib/address.js +++ b/lib/address.js @@ -77,7 +77,7 @@ Address._transformHash = function(hash){ throw new TypeError('Address supplied is not a buffer.'); } if (hash.length !== 20) { - throw new TypeError('Address hashbuffers must be exactly a 20 bytes.'); + throw new TypeError('Address hashbuffers must be exactly 20 bytes.'); } info.hashBuffer = hash; return info; diff --git a/test/address.js b/test/address.js index b74c757..f803e9b 100644 --- a/test/address.js +++ b/test/address.js @@ -202,6 +202,54 @@ describe('Address', function() { var b = new Address(str).toString().should.equal(str); }); + it('should error because of unrecognized data format', function() { + (function() { + var a = new Address(new Error()); + }).should.throw('First argument is an unrecognized data format.'); + }); + + it('should error because of incorrect format for pubkey hash', function() { + (function() { + var a = new Address.fromPubkeyHash('notahash'); + }).should.throw('Address supplied is not a buffer.'); + }); + + it('should error because of incorrect format for script hash', function() { + (function() { + var a = new Address.fromScriptHash('notascript'); + }).should.throw('Address supplied is not a buffer.'); + }); + + it('should error because of incorrect type for transform buffer', function() { + (function() { + var info = Address._transformBuffer('notabuffer'); + }).should.throw('Address supplied is not a buffer.'); + }); + + it('should error because of incorrect length buffer for transform buffer', function() { + (function() { + var info = Address._transformBuffer(new Buffer(20)); + }).should.throw('Address buffers must be exactly 21 bytes.'); + }); + + 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.'); + }); + + it('should error because of incorrect type for script transform', function() { + (function() { + var info = Address._transformScript(new Buffer(20)); + }).should.throw('Address must be an instance of Script.'); + }); + + it('should error because of incorrect type for string transform', function() { + (function() { + var info = Address._transformString(new Buffer(20)); + }).should.throw('Address supplied is not a string.'); + }); + 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); @@ -214,7 +262,7 @@ describe('Address', function() { it('should throw an error for invalid length hashBuffer', function() { (function() { var a = Address.fromPubkeyHash(buf); - }).should.throw('Address hashbuffers must be exactly a 20 bytes.'); + }).should.throw('Address hashbuffers must be exactly 20 bytes.'); }); it('should make this address from a compressed pubkey object', function() { @@ -240,10 +288,13 @@ describe('Address', function() { it('should make this address from a script', function() { var s = Script().fromString("OP_CHECKMULTISIG"); + var buf = s.toBuffer(); var a = Address.fromScript(s); a.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); var b = new Address(s); b.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); + var c = Address.fromScriptHash(bitcore.crypto.Hash.sha256ripemd160(buf)); + c.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); }); it('should make this address from other script', function() { @@ -316,4 +367,14 @@ describe('Address', function() { }); + describe('#inspect', function() { + + it('should output formatted output correctly', function() { + var address = new Address(str); + var output = ''; + address.inspect().should.equal(output); + }); + + }); + }); From deb54c5a20264a576b5d7d80aecab8fa92416fa7 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 24 Nov 2014 15:43:00 -0500 Subject: [PATCH 7/7] Address: Return new instance if `this` isn't already instantiated. --- lib/address.js | 5 ++++- test/address.js | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/address.js b/lib/address.js index 603047f..5cf22a4 100644 --- a/lib/address.js +++ b/lib/address.js @@ -22,6 +22,10 @@ var Hash = require('./crypto/hash'); */ function Address(data, network, type) { + if (!(this instanceof Address)) { + return new Address(data, network, type); + } + if (!data) { throw new TypeError('First argument is required, please include address data.'); } @@ -346,5 +350,4 @@ Address.prototype.inspect = function() { return ''; } - module.exports = Address; diff --git a/test/address.js b/test/address.js index f803e9b..6fb2019 100644 --- a/test/address.js +++ b/test/address.js @@ -195,6 +195,7 @@ describe('Address', function() { it('should make an address from a buffer', function() { var a = Address.fromBuffer(buf).toString().should.equal(str); var b = new Address(buf).toString().should.equal(str); + var c = Address(buf).toString().should.equal(str); }); it('should make an address from a string', function() {