From d205b3e1a56c93ac043884ee532c7a199704c697 Mon Sep 17 00:00:00 2001 From: sidazhang Date: Wed, 12 Feb 2014 18:13:57 -0800 Subject: [PATCH 1/2] Setting default network and supporting scripthash (p2sh) addresses --- lib/address.js | 106 ++++++++++++++++++++----------------------- test/address.test.js | 67 ++++++++++++++++++++++++--- 2 files changed, 109 insertions(+), 64 deletions(-) diff --git a/lib/address.js b/lib/address.js index f30b89f..abe197e 100644 --- a/lib/address.js +++ b/lib/address.js @@ -1,26 +1,40 @@ var sha256 = require('crypto-hashing').sha256; var base58 = require('bs58'); - -module.exports = Address; -var address_types = { - prod: 0, - testnet: 111 +module.exports = Address; +Address.defaultNetwork = 'mainnet'; + +var addressTypes = { + Pubkeyhash: { + mainnet: 0, + testnet: 111 + }, + Scripthash: { + mainnet: 5, + testnet: 196 + } }; -var p2sh_types = { - prod: 5, - testnet: 196 +var versionBytes = { + mainnet: { + 0: 'Pubkeyhash', + 5: 'Scripthash' + }, + testnet: { + 111: 'Pubkeyhash', + 196: 'Scripthash' + } }; -function Address(bytes, address_type) { +function Address(bytes, network, addressType) { if (typeof bytes === 'string') { - this.decodeString(bytes, address_type); + this.decodeString(bytes, network, addressType); } else { this.hash = bytes; - this.version = address_types[address_type || 'prod']; + // default to pubkeyhash + this.version = addressTypes[addressType || 'Pubkeyhash'][network || Address.defaultNetwork]; } -}; +} /** * Serialize this object as a standard Bitcoin address. @@ -34,68 +48,41 @@ Address.prototype.toString = function() { // Version hash.unshift(this.version); - //var checksum = sha256(sha256(hash, {asBytes: true}), {asBytes: true}); - var checksum = sha256.x2(hash, {in: 'bytes', out: 'bytes'}); + var checksum = sha256.x2(hash, { in : 'bytes', + out: 'bytes' + }); var bytes = hash.concat(checksum.slice(0, 4)); return base58.encode(bytes); }; -Address.validateType = function(version, type) { - if (type) { - return (version === address_types[type] || version === p2sh_types[type]); - } - for (var type in address_types) { - if (version == address_types[type]) { - return true; - } - } - for (var type in p2sh_types) { - if (version == p2sh_types[type]) { - return true; - } - } - return false; -}; +Address.validateType = function(version, network, addressType) { + return (version === addressTypes[addressType][network || Address.defaultNetwork]) +} -// TODO(shtylman) isValid? -Address.validate = function(string, type) { +Address.validate = function(string, network, addressType) { try { - var bytes = base58.decode(string); - } catch (e) { - return false; - } - - var hash = bytes.slice(0, 21); - - //var checksum = sha256(sha256(hash, {asBytes: true}), {asBytes: true}); - var checksum = sha256.x2(hash, {in: 'bytes', out: 'bytes'}); - - if (checksum[0] != bytes[21] || - checksum[1] != bytes[22] || - checksum[2] != bytes[23] || - checksum[3] != bytes[24]) { - return false; - } - - if (!Address.validateType(hash[0], type)) { - return false; + new Address(string, network, addressType) + } catch (err) { + return false } - - return true; + return true }; /** * Parse a Bitcoin address contained in a string. */ -Address.prototype.decodeString = function(string, address_type) { +// network is optional +Address.prototype.decodeString = function(string, network) { var bytes = base58.decode(string); var hash = bytes.slice(0, 21); //var checksum = sha256(sha256(hash, {asBytes: true}), {asBytes: true}); - var checksum = sha256.x2(hash, {in: 'bytes', out: 'bytes'}); + var checksum = sha256.x2(hash, { in : 'bytes', + out: 'bytes' + }); if (checksum[0] != bytes[21] || checksum[1] != bytes[22] || @@ -105,13 +92,16 @@ Address.prototype.decodeString = function(string, address_type) { } var version = hash.shift(); - if (!Address.validateType(version, address_type)) { + var addressType = versionBytes[network || Address.defaultNetwork][version] + if (!Address.validateType(version, network, addressType)) { throw new Error('Address version (' + version + ') not supported: ' + string + - ' for ' + address_type); + ' for ' + addressType); } this.hash = hash; this.version = version; }; - +Address.prototype.getType = function(network) { + return versionBytes[network || Address.defaultNetwork][this.version] +} diff --git a/test/address.test.js b/test/address.test.js index 3c9aca3..8e98d33 100644 --- a/test/address.test.js +++ b/test/address.test.js @@ -1,18 +1,73 @@ -var Address = require('../lib/address') - , conv = require('convert-hex') +var Address = require('../lib/address'), + binConv = require('binstring'); -require('terst') +require('terst'); describe('Address', function() { describe('> when a the result of the sha256 ripemd160 is input', function() { it('should create the bitcoin address', function() { var hash160 = "3c176e659bea0f29a3e9bf7880c112b1b31b4dc8" - var address = new Address(conv.hexToBytes(hash160), 'prod') //'testnet is also valid' - EQ (address.toString(), "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS") + var address = new Address(binConv(hash160, { in : 'hex', + out: 'bytes' + }), 'mainnet', 'Pubkeyhash') //'testnet is also valid' + EQ(address.toString(), "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS") + }) + }) + + describe(' - Address.validate()', function() { + it(' > supports pubkey hash addresses', function() { + // default to mainnet + // addresses for hash160: 0000000000000000000000000000000000000000 + T(Address.validate('1111111111111111111114oLvT2')) + F(Address.validate('1111111111111111111114oLvT2', 'testnet')) + + F(Address.validate('mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8')) + T(Address.validate('mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8', 'testnet')) + }) + + it(' > supports Scripthash addresses', function() { + // default to mainnet + // addresses for hash160: 0000000000000000000000000000000000000000 + T(Address.validate('31h1vYVSYuKP6AhS86fbRdMw9XHieotbST')) + F(Address.validate('31h1vYVSYuKP6AhS86fbRdMw9XHieotbST', 'testnet')) + + F(Address.validate('2MsFDzHRUAMpjHxKyoEHU3aMCMsVtMqs1PV')) + T(Address.validate('2MsFDzHRUAMpjHxKyoEHU3aMCMsVtMqs1PV', 'testnet')) }) }) -}) + describe(' - Address.getType()', function() { + it(' > supports pubkeyhash addresses', function() { + var address = new Address('1111111111111111111114oLvT2') + EQ(address.getType(), 'Pubkeyhash') + var address = new Address('mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8', 'testnet') + EQ(address.getType('testnet'), 'Pubkeyhash') + }) + it(' > supports Scripthash addresses', function() { + var address = new Address('31h1vYVSYuKP6AhS86fbRdMw9XHieotbST') + EQ(address.getType(), 'Scripthash') + var address = new Address('2MsFDzHRUAMpjHxKyoEHU3aMCMsVtMqs1PV', 'testnet') + EQ(address.getType('testnet'), 'Scripthash') + }) + }) + describe(' - Setting DefaultNetwork', function() { + it(' > returns the right address given default newtork', function() { + var hash160 = '0000000000000000000000000000000000000000' + var bytes = binConv(hash160, { in : 'hex', + out: 'bytes' + }) + Address.defaultNetwork = 'mainnet'; + var address = new Address(bytes) + // https://blockchain.info/address/1111111111111111111114oLvT2 + EQ(address.toString(), '1111111111111111111114oLvT2') + + Address.defaultNetwork = 'testnet'; + var address = new Address(bytes) + // https://helloblock.io/testnet/addresses/mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8 + EQ(address.toString(), 'mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8') + }); + }) +}) From 3a4c046590574714468af9b3d401bd86328bc55a Mon Sep 17 00:00:00 2001 From: sidazhang Date: Wed, 12 Feb 2014 18:23:44 -0800 Subject: [PATCH 2/2] Changing types to lower case to be consistent with bitcoind --- lib/address.js | 14 +++++++------- test/address.test.js | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/address.js b/lib/address.js index abe197e..6f3564c 100644 --- a/lib/address.js +++ b/lib/address.js @@ -5,11 +5,11 @@ module.exports = Address; Address.defaultNetwork = 'mainnet'; var addressTypes = { - Pubkeyhash: { + pubkeyhash: { mainnet: 0, testnet: 111 }, - Scripthash: { + scripthash: { mainnet: 5, testnet: 196 } @@ -17,12 +17,12 @@ var addressTypes = { var versionBytes = { mainnet: { - 0: 'Pubkeyhash', - 5: 'Scripthash' + 0: 'pubkeyhash', + 5: 'scripthash' }, testnet: { - 111: 'Pubkeyhash', - 196: 'Scripthash' + 111: 'pubkeyhash', + 196: 'scripthash' } }; @@ -32,7 +32,7 @@ function Address(bytes, network, addressType) { } else { this.hash = bytes; // default to pubkeyhash - this.version = addressTypes[addressType || 'Pubkeyhash'][network || Address.defaultNetwork]; + this.version = addressTypes[addressType || 'pubkeyhash'][network || Address.defaultNetwork]; } } diff --git a/test/address.test.js b/test/address.test.js index 8e98d33..17c7971 100644 --- a/test/address.test.js +++ b/test/address.test.js @@ -9,7 +9,7 @@ describe('Address', function() { var hash160 = "3c176e659bea0f29a3e9bf7880c112b1b31b4dc8" var address = new Address(binConv(hash160, { in : 'hex', out: 'bytes' - }), 'mainnet', 'Pubkeyhash') //'testnet is also valid' + }), 'mainnet', 'pubkeyhash') //'testnet is also valid' EQ(address.toString(), "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS") }) }) @@ -39,16 +39,16 @@ describe('Address', function() { describe(' - Address.getType()', function() { it(' > supports pubkeyhash addresses', function() { var address = new Address('1111111111111111111114oLvT2') - EQ(address.getType(), 'Pubkeyhash') + EQ(address.getType(), 'pubkeyhash') var address = new Address('mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8', 'testnet') - EQ(address.getType('testnet'), 'Pubkeyhash') + EQ(address.getType('testnet'), 'pubkeyhash') }) it(' > supports Scripthash addresses', function() { var address = new Address('31h1vYVSYuKP6AhS86fbRdMw9XHieotbST') - EQ(address.getType(), 'Scripthash') + EQ(address.getType(), 'scripthash') var address = new Address('2MsFDzHRUAMpjHxKyoEHU3aMCMsVtMqs1PV', 'testnet') - EQ(address.getType('testnet'), 'Scripthash') + EQ(address.getType('testnet'), 'scripthash') }) })