From 3871e43dd84d5cfda5d982e7e634bbccdeca710b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 20:26:03 -0700 Subject: [PATCH] make address more consistent with rest of lib --- lib/address.js | 114 +++++++++++-------------- lib/constants.js | 4 +- test/test.address.js | 198 ++++++++++++++++++------------------------- 3 files changed, 136 insertions(+), 180 deletions(-) diff --git a/lib/address.js b/lib/address.js index 720bb88..798d993 100644 --- a/lib/address.js +++ b/lib/address.js @@ -1,78 +1,64 @@ var base58check = require('./base58check'); var constants = require('./constants'); -function Address(str) { - if (!str) { - this.buf = undefined; - return; - } - if (typeof str !== 'string') - throw new Error('address: Input must be a string, or undefined'); - this.fromString(str); -}; - -Address.prototype.getNetwork = function() { - if (this.buf[0] === constants.mainnet.pubkeyHash || this.buf[0] === constants.mainnet.p2sh) - return 'mainnet'; - else if (this.buf[0] === constants.testnet.pubkeyHash || this.buf[0] === constants.testnet.p2sh) - return 'testnet'; - else - return 'unknown'; -}; - -Address.prototype.getHash = function() { - var pubkeyHash = this.buf.slice(1); - if (pubkeyHash.length === 20) - return pubkeyHash; - else - throw new Error('address: Hash must be exactly 20 bytes'); -}; - -Address.prototype.getType = function() { - if (this.buf[0] === constants.mainnet.pubkeyHash || this.buf[0] === constants.testnet.pubkeyHash) - return 'pubkeyHash'; - else if (this.buf[0] === constants.mainnet.p2sh || this.buf[0] === constants.testnet.p2sh) - return 'p2sh'; - else - return 'unknown'; -}; - -Address.prototype.isValid = function() { - if (Buffer.isBuffer(this.buf) && this.buf.length === 1 + 20) - return true; - else - return false; -}; - -Address.prototype.setBuf = function(buf, network, type) { - var version; - if (!Buffer.isBuffer(buf)) - throw new Error('address: buf must be a buffer'); - if (buf.length !== 20) - throw new Error('address: buf must be 20 bytes'); - if (typeof network === 'undefined') - throw new Error('address: Must specify network ("mainnet" or "testnet")'); - if (typeof type === 'undefined') - throw new Error('address: Must specify type ("pubkeyHash" or "p2sh")'); - if (network !== 'mainnet' && network !== 'testnet') - throw new Error('address: Unknown network'); - if (type !== 'pubkeyHash' && type !== 'p2sh') - throw new Error('address: Unknown type'); - - version = new Buffer([constants[network][type]]); - - this.buf = Buffer.concat([version, buf]); +function Address(hash, network, type) { + this.hash = hash; + this.network = network; + this.type = type; }; Address.prototype.fromString = function(str) { var buf = base58check.decode(str); if (buf.length !== 1 + 20) - throw new Error('address: Addresses must be exactly 21 bytes'); - this.buf = buf; + throw new Error('address: Address buffers must be exactly 21 bytes'); + var version = buf[0]; + if (version === constants['mainnet']['pubkeyhash']) { + this.network = 'mainnet'; + this.type = 'pubkeyhash'; + } else if (version === constants['mainnet']['p2sh']) { + this.network = 'mainnet'; + this.type = 'p2sh'; + } else if (version === constants['testnet']['pubkeyhash']) { + this.network = 'testnet'; + this.type = 'pubkeyhash'; + } else if (version === constants['testnet']['p2sh']) { + this.network = 'testnet'; + this.type = 'p2sh'; + } else { + this.network = 'unknown'; + this.type = 'unknown'; + } + + this.hash = buf.slice(1); } +Address.prototype.isValid = function() { + try { + this.validate(); + return true; + } catch (e) { + return false; + } +}; + +Address.prototype.toBuffer = function() { + version = new Buffer([constants[this.network][this.type]]); + var buf = Buffer.concat([version, this.hash]); + return buf; +}; + Address.prototype.toString = function() { - return base58check.encode(this.buf); + return base58check.encode(this.toBuffer()); +}; + +Address.prototype.validate = function() { + if (!Buffer.isBuffer(this.hash) || this.hash.length !== 20) + throw new Error('address: hash must be a buffer of 20 bytes'); + if (this.network !== 'mainnet' && this.network !== 'testnet') + throw new Error('address: network must be "mainnet" or "testnet"'); + if (this.type !== 'pubkeyhash' && this.type !== 'p2sh') + throw new Error('address: type must be "pubkeyhash" or "p2sh"'); + return this; }; module.exports = Address; diff --git a/lib/constants.js b/lib/constants.js index 5b90914..6191987 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,5 +1,5 @@ exports.mainnet = { - pubkeyHash: 0x00, + pubkeyhash: 0x00, privkey: 0x80, p2sh: 0x05, bip32pubkey: 0x0488b21e, @@ -7,7 +7,7 @@ exports.mainnet = { }; exports.testnet = { - pubkeyHash: 0x6f, + pubkeyhash: 0x6f, privkey: 0xef, p2sh: 0xc4, bip32pubkey: 0x043587cf, diff --git a/test/test.address.js b/test/test.address.js index 545e264..2bd7002 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -4,147 +4,117 @@ var Address = require('../lib/address'); describe('address', function() { var pubkeyhash = new Buffer('3c3fa3d4adcaf8f52d5b1843975e122548269937', 'hex'); - var str = '1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6'; + var str = '16VZnHwRhwrExfeHFHGjwrgEMq8VcYPs9r'; it('should create a new address object', function() { var address = new Address(); should.exist(address); }); - it('should throw an error when input is not a string', function() { - (function() { - var address = new Address(5); - }).should.throw('address: Input must be a string, or undefined'); - }); - - describe('#getNetwork', function() { + describe('#fromString', function() { - it('should return mainnet for pubkeyhash', function() { + it('should derive from this known address string mainnet', function() { var address = new Address(); - address.buf = Buffer.concat([new Buffer([constants.mainnet.pubkeyHash]), pubkeyhash]); - address.getNetwork().should.equal('mainnet'); + address.fromString(str); + address.toBuffer().slice(1).toString('hex').should.equal(pubkeyhash.toString('hex')); }); - it('should return mainnet for p2sh', function() { + it('should derive from this known address string testnet', function() { var address = new Address(); - address.buf = Buffer.concat([new Buffer([constants.mainnet.p2sh]), pubkeyhash]); - address.getNetwork().should.equal('mainnet'); - }); - - it('should return testnet for pubkeyhash', function() { - var address = new Address(); - address.buf = Buffer.concat([new Buffer([constants.testnet.pubkeyHash]), pubkeyhash]); - address.getNetwork().should.equal('testnet'); - }); - - it('should return testnet for p2sh', function() { - var address = new Address(); - address.buf = Buffer.concat([new Buffer([constants.testnet.p2sh]), pubkeyhash]); - address.getNetwork().should.equal('testnet'); - }); - - it('should return unknown', function() { - var address = new Address(); - address.buf = Buffer.concat([new Buffer([0x55]), pubkeyhash]); - address.getNetwork().should.equal('unknown'); - }); - - it('should throw an error if there is no buffer', function() { - var address = new Address(); - (function() { - address.getNetwork(); - }).should.throw(); - }); - - }); - - describe('#getHash', function() { - - it('should return the hash', function() { - var address = new Address(); - address.buf = Buffer.concat([new Buffer([0x00]), pubkeyhash]); - address.getHash().toString('hex').should.equal(pubkeyhash.toString('hex')); - }); - - it('should throw an error if the buffer is an invalid length', function() { - var address = new Address(); - address.buf = Buffer.concat([new Buffer([0x00]), pubkeyhash.slice(-1)]); - (function() { - address.getHash(); - }).should.throw('address: Hash must be exactly 20 bytes'); - }); - - }); - - describe('#getType', function() { - - it('should get the type of 2MxjnmaMtsJfyFcyG3WZCzS2RihdNuWqeX4 correctly', function() { - var addr = new Address(); - addr.fromString('2MxjnmaMtsJfyFcyG3WZCzS2RihdNuWqeX4'); - addr.getNetwork().should.equal('testnet'); - addr.getType().should.equal('p2sh'); - }); - - }); - - describe('#setBuf', function() { - - it('should convert this pubkeyhash on mainnet and type pubkeyHash to known address', function() { - var address = new Address(); - address.setBuf(pubkeyhash, 'mainnet', 'pubkeyHash'); - address.toString().should.equal('16VZnHwRhwrExfeHFHGjwrgEMq8VcYPs9r'); - }); - - it('should convert this pubkeyhash on mainnet and type p2sh to known address', function() { - var address = new Address(); - address.setBuf(pubkeyhash, 'mainnet', 'p2sh'); - address.toString().should.equal('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); - }); - - it('should convert this pubkeyhash on testnet and type pubkeyHash to known address', function() { - var address = new Address(); - address.setBuf(pubkeyhash, 'testnet', 'pubkeyHash'); + address.fromString(str); + address.network = 'testnet'; + address.fromString(address.toString()); address.toString().should.equal('mm1X5M2QWyHVjn7txrF7mmtZDpjCXzoa98'); }); - it('should convert this pubkeyhash on testnet and type p2sh to known address', function() { + it('should derive from this known address string mainnet p2sh', function() { var address = new Address(); - address.setBuf(pubkeyhash, 'testnet', 'p2sh'); + address.fromString(str); + address.network = 'mainnet'; + address.type = 'p2sh'; + address.fromString(address.toString()); + address.toString().should.equal('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); + }); + + it('should derive from this known address string testnet p2sh', function() { + var address = new Address(); + address.fromString(str); + address.network = 'testnet'; + address.type = 'p2sh'; + address.fromString(address.toString()); address.toString().should.equal('2MxjnmaMtsJfyFcyG3WZCzS2RihdNuWqeX4'); }); - it('should throw an error for an unknown type', function() { - var address = new Address(); - (function() { - address.setBuf(pubkeyhash, 'testnet', 'p2sh2'); - }).should.throw(); - }); - - it('should throw an error for an unknown network', function() { - var address = new Address(); - (function() { - address.setBuf(pubkeyhash, 'testnet2', 'p2sh'); - }).should.throw(); - }); - }); - describe('#fromString', function() { + describe('#isValid', function() { + + it('should describe this valid address as valid', function() { + var address = new Address(); + address.fromString('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); + address.isValid().should.equal(true); + }); - it('should decode 1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6 correctly', function() { - var addr = new Address(); - addr.fromString('1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6'); - addr.getHash().toString('hex').should.equal('82248027cfb0fe085b750f359fd1e43234e46c7f'); + it('should describe this address with unknown network as invalid', function() { + var address = new Address(); + address.fromString('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); + address.network = 'unknown'; + address.isValid().should.equal(false); + }); + + it('should describe this address with unknown type as invalid', function() { + var address = new Address(); + address.fromString('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); + address.type = 'unknown'; + address.isValid().should.equal(false); + }); + + }); + + describe('#toBuffer', function() { + + it('should output this known hash', function() { + var address = new Address(); + address.fromString(str); + address.toBuffer().slice(1).toString('hex').should.equal(pubkeyhash.toString('hex')); }); }); describe('#toString', function() { + + it('should output the same thing that was input', function() { + var address = new Address(); + address.fromString(str); + address.toString().should.equal(str); + }); - it('should return 1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6', function() { - var addr = new Address(); - addr.fromString('1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6'); - addr.toString().should.equal('1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6'); + }); + + describe('#validate', function() { + + it('should not throw an error on this valid address', function() { + var address = new Address(); + address.fromString(str); + should.exist(address.validate()); + }); + + it('should throw an error on this invalid network', function() { + var address = new Address(); + address.fromString(str); + address.network = 'unknown'; + (function() { + address.validate(); + }).should.throw('address: network must be "mainnet" or "testnet"'); + }); + + it('should throw an error on this invalid type', function() { + var address = new Address(); + address.fromString(str); + address.type = 'unknown'; + (function() { + address.validate(); + }).should.throw('address: type must be "pubkeyhash" or "p2sh"'); }); });