diff --git a/docs/Address.md b/docs/Address.md index a5642b1b9..09b7cbdf3 100644 --- a/docs/Address.md +++ b/docs/Address.md @@ -4,6 +4,44 @@ Represents a bitcoin Address. Addresses became the most popular way to make bitcoin transactions. See [the official Bitcoin Wiki](https://en.bitcoin.it/wiki/Address) for more information. + +## Instantiate an Address + +To be able to receive bitcoin an address is needed, here is how to create an +address from a new private key. Please see the [`PrivateKey`](PrivateKey.md) docs +for more information about exporting and saving a key. + +```javascript + +var PrivateKey = require('bitcore/lib/privatekey'); +var privateKey = new PrivateKey(); +var address = privateKey.toAddress(); + +``` + +You can also instantiate an address from a String or PublicKey. + +```javascript + +var Address = require('bitcore/lib/address'); +var PublicKey = require('bitcore/lib/publickey'); +var Networks = require('bitcore/lib/networks'); + +// from a string +var address = Address.fromString('mwkXG8NnB2snbqWTcpNiK6qqGHm1LebHDc'); + +// a default network address from a public key +var publicKey = PublicKey(privateKey); +var address = Address.fromPublicKey(publicKey); + +// a testnet address from a public key +var publicKey = PublicKey(privateKey); +var address = Address.fromPublicKey(publicKey, Networks.testnet); + +``` + +## Validating an Address + The main use that we expect you'll have for the `Address` class in bitcore is validating that an address is a valid one, what type of address it is (you may be interested on knowning if the address is a simple "pay to public key hash" @@ -13,18 +51,27 @@ belong to. The code to do these validations looks like this: ```javascript -var address = new bitcore.Address('1BitcoinAddress...'); -assert(address.network === bitcore.network.livenet); -// Detect the kind of the address... -assert(address.scriptType === bitcore.Address.Pay2PubKeyHash); -``` -There are also static methods for this that work very similarly: +// validate an address +if (Address.isValid(input){ + ... +} + +// validate that an input field is a valid testnet address +if (Address.isValid(input, Networks.testnet){ + ... +} + +// validate that an input field is a valid livenet pubkeyhash +if (Address.isValid(input, Networks.livenet, Address.Pay2PubKeyHash){ + ... +} + +// get the specific validation error that can occurred +var error = Address.getValidationError(input, Networks.testnet); + if (error) { + // handle the error + } +} -```javascript -var address = new bitcore.Address(); -assert(bitcore.Address.isValid('1BitcoinAddress...')); -assert(bitcore.Address.network('1BitcoinAddress...') === bitcore.network.livenet); -assert(bitcore.Address.scriptType('1BitcoinAddress...') !== bitcore.Address.Pay2ScriptHash); -assert(bitcore.Address.scriptType('3MultisigP2SH...') === bitcore.Address.Pay2ScriptHash); ``` diff --git a/lib/address.js b/lib/address.js index 2699fcf7a..2dade141e 100644 --- a/lib/address.js +++ b/lib/address.js @@ -1,11 +1,10 @@ 'use strict'; var base58check = require('./encoding/base58check'); -var networks = require('./networks'); +var Networks = require('./networks'); var Hash = require('./crypto/hash'); /** - * * Instantiate an address from an address String or Buffer, a public key or script hash Buffer, * or an instance of PublicKey or Script. * @@ -40,7 +39,7 @@ function Address(data, network, type) { throw new TypeError('First argument is required, please include address data.'); } - if (network && !networks.get(network)) { + if (network && !Networks.get(network)) { throw new TypeError('Second argument must be "livenet" or "testnet".'); } @@ -68,7 +67,7 @@ function Address(data, network, type) { } // set defaults if not set - info.network = info.network || network || networks.defaultNetwork.name; + info.network = info.network || Networks.get(network) || Networks.defaultNetwork; info.type = info.type || type || Address.PayToPublicKeyHash; Object.defineProperty(this, 'hashBuffer', { @@ -93,7 +92,6 @@ Address.PayToPublicKeyHash = 'pubkeyhash'; Address.PayToScriptHash = 'scripthash'; /** - * * Internal function to transform a hash buffer * * @param {Buffer} hash - An instance of a hash Buffer @@ -113,7 +111,39 @@ Address._transformHash = function(hash){ }; /** + * Internal function to discover the network and type * + * @param {Buffer} buffer - An instance of a hex encoded address Buffer + * @returns {Object} An object with keys: network and type + * @private + */ +Address._classifyFromVersion = function(buffer){ + var version = {}; + switch(buffer[0]){ // the version byte + case Networks.livenet.pubkeyhash: + version.network = Networks.livenet; + version.type = Address.PayToPublicKeyHash; + break; + + case Networks.livenet.scripthash: + version.network = Networks.livenet; + version.type = Address.PayToScriptHash; + break; + + case Networks.testnet.pubkeyhash: + version.network = Networks.testnet; + version.type = Address.PayToPublicKeyHash; + break; + + case Networks.testnet.scripthash: + version.network = Networks.testnet; + version.type = Address.PayToScriptHash; + break; + } + return version; +}; + +/** * Internal function to transform a bitcoin address buffer * * @param {Buffer} buffer - An instance of a hex encoded address Buffer @@ -131,47 +161,24 @@ Address._transformBuffer = function(buffer, network, type){ throw new TypeError('Address buffers must be exactly 21 bytes.'); } - var bufNetwork = false; - var bufType = false; + network = Networks.get(network); + var bufferVersion = Address._classifyFromVersion(buffer); - switch(buffer[0]){ // the version byte - case networks.livenet.pubkeyhash: - bufNetwork = 'livenet'; - bufType = 'pubkeyhash'; - break; - - case networks.livenet.scripthash: - bufNetwork = 'livenet'; - 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)) { + if (!bufferVersion.network || (network && network !== bufferVersion.network)) { throw new TypeError('Address has mismatched network type.'); } - if (!bufType || ( type && type !== bufType )) { + if (!bufferVersion.type || ( type && type !== bufferVersion.type )) { throw new TypeError('Address has mismatched type.'); } info.hashBuffer = buffer.slice(1); - info.network = bufNetwork; - info.type = bufType; + info.network = bufferVersion.network; + info.type = bufferVersion.type; return info; }; /** - * * Internal function to transform a PublicKey * * @param {PublicKey} pubkey - An instance of PublicKey @@ -189,7 +196,6 @@ Address._transformPublicKey = function(pubkey){ }; /** - * * Internal function to transform a Script * * @param {Script} script - An instance of Script @@ -207,7 +213,6 @@ Address._transformScript = function(script){ }; /** - * * Internal function to transform a bitcoin address string * * @param {String} data - An instance of PublicKey @@ -226,7 +231,6 @@ Address._transformString = function(data, network, type){ }; /** - * * Instantiate an address from a PublicKey instance * * @param {PublicKey} data - An instance of PublicKey @@ -235,12 +239,11 @@ Address._transformString = function(data, network, type){ */ Address.fromPublicKey = function(data, network){ var info = Address._transformPublicKey(data); - network = network || networks.defaultNetwork.name; + network = network || Networks.defaultNetwork; return new Address(info.hashBuffer, network, info.type); }; /** - * * Instantiate an address from a ripemd160 public key hash * * @param {Buffer} hash - An instance of buffer of the hash @@ -253,7 +256,6 @@ Address.fromPublicKeyHash = function(hash, network) { }; /** - * * Instantiate an address from a ripemd160 script hash * * @param {Buffer} hash - An instance of buffer of the hash @@ -266,7 +268,6 @@ Address.fromScriptHash = function(hash, network) { }; /** - * * Instantiate an address from a Script * * @param {Script} script - An instance of Script @@ -279,7 +280,6 @@ Address.fromScript = function(script, network) { }; /** - * * Instantiate an address from a buffer of the address * * @param {Buffer} buffer - An instance of buffer of the address @@ -293,7 +293,6 @@ Address.fromBuffer = function(buffer, network, type) { }; /** - * * Instantiate an address from an address string * * @param {String} str - An string of the bitcoin address @@ -307,7 +306,6 @@ Address.fromString = function(str, network, type) { }; /** - * * Will return a validation error if exists * * @example @@ -331,7 +329,6 @@ Address.getValidationError = function(data, network, type) { }; /** - * * Will return a boolean if an address is valid * * @example @@ -365,19 +362,17 @@ Address.prototype.isPayToScriptHash = function() { }; /** - * * Will return a buffer representation of the address * * @returns {Buffer} Bitcoin address buffer */ Address.prototype.toBuffer = function() { - var version = new Buffer([networks[this.network][this.type]]); + var version = new Buffer([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 @@ -387,7 +382,6 @@ Address.prototype.toString = function() { }; /** - * * Will return a string formatted for the console * * @returns {String} Bitcoin address diff --git a/lib/networks.js b/lib/networks.js index fff435f93..5e11d2614 100644 --- a/lib/networks.js +++ b/lib/networks.js @@ -9,6 +9,10 @@ var _ = require('lodash'); */ function Network() {} +Network.prototype.toString = function toString() { + return this.name; +}; + /** * @instance * @member Network#livenet diff --git a/test/address.js b/test/address.js index 6f224b216..919bdfa53 100644 --- a/test/address.js +++ b/test/address.js @@ -8,7 +8,7 @@ var bitcore = require('..'); var PublicKey = bitcore.PublicKey; var Address = bitcore.Address; var Script = bitcore.Script; -var networks = bitcore.Networks; +var Networks = bitcore.Networks; describe('Address', function() { @@ -205,6 +205,10 @@ describe('Address', function() { new Address(str).toString().should.equal(str); }); + it('should make an address using a non-string network', function() { + Address.fromString(str, Networks.livenet).toString().should.equal(str); + }); + it('should error because of unrecognized data format', function() { (function() { return new Address(new Error()); @@ -257,7 +261,7 @@ describe('Address', function() { var hash = pubkeyhash; //use the same hash Address.fromPublicKeyHash(hash).toString().should.equal(str); var b = Address.fromPublicKeyHash(hash, 'testnet'); - b.network.should.equal('testnet'); + b.network.should.equal(Networks.testnet); b.type.should.equal('pubkeyhash'); new Address(hash).toString().should.equal(str); }); @@ -265,13 +269,13 @@ describe('Address', function() { it('should make an address using the default network', function() { var hash = pubkeyhash; //use the same hash var a = Address.fromPublicKeyHash(hash); - a.network.should.equal('livenet'); + a.network.should.equal(Networks.livenet); // change the default - networks.defaultNetwork = networks.testnet; + Networks.defaultNetwork = Networks.testnet; var b = Address.fromPublicKeyHash(hash); - b.network.should.equal('testnet'); + b.network.should.equal(Networks.testnet); // restore the default - networks.defaultNetwork = networks.livenet; + Networks.defaultNetwork = Networks.livenet; }); it('should throw an error for invalid length hashBuffer', function() { @@ -325,7 +329,7 @@ describe('Address', function() { var a = new Address(PKHTestnet[0], 'testnet'); var b = new Address(a.toString()); b.toString().should.equal(PKHTestnet[0]); - b.network.should.equal('testnet'); + b.network.should.equal(Networks.testnet); }); it('should derive from this known address string livenet scripthash', function() { diff --git a/test/uri.js b/test/uri.js index da0b9cd2c..21ff08f92 100644 --- a/test/uri.js +++ b/test/uri.js @@ -1,9 +1,10 @@ 'use strict'; var chai = chai || require('chai'); -var should = chai.should(); -var expect = chai.expect; var bitcore = require('..'); +var expect = chai.expect; +var Networks = bitcore.Networks; +var should = chai.should(); var URI = bitcore.URI; describe('URI', function() { @@ -62,11 +63,11 @@ describe('URI', function() { uri = new URI('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj'); uri.address.should.be.instanceof(bitcore.Address); - uri.network.should.equal('livenet'); + uri.network.should.equal(Networks.livenet); uri = new URI('bitcoin:mkYY5NRvikVBY1EPtaq9fAFgquesdjqECw'); uri.address.should.be.instanceof(bitcore.Address); - uri.network.should.equal('testnet'); + uri.network.should.equal(Networks.testnet); uri = new URI('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=1.2&other=param'); uri.address.should.be.instanceof(bitcore.Address); @@ -92,14 +93,13 @@ describe('URI', function() { address: '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj' }); uri.address.should.be.instanceof(bitcore.Address); - uri.network.should.equal('livenet'); + uri.network.should.equal(Networks.livenet); uri = new URI({ address: 'mkYY5NRvikVBY1EPtaq9fAFgquesdjqECw' }); uri.address.should.be.instanceof(bitcore.Address); - uri.network.should.equal('testnet'); - + uri.network.should.equal(Networks.testnet); uri = new URI({ address: '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj',