Skip to content

Commit

Permalink
Merge pull request #2 from cryptocoinjs/feature-network-versioning
Browse files Browse the repository at this point in the history
Setting default network and supporting scripthash (p2sh) addresses
  • Loading branch information
jprichardson committed Feb 13, 2014
2 parents a69b430 + 3a4c046 commit 399a6c7
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 64 deletions.
106 changes: 48 additions & 58 deletions 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.
Expand All @@ -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] ||
Expand All @@ -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]
}
67 changes: 61 additions & 6 deletions 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')
});
})
})

0 comments on commit 399a6c7

Please sign in to comment.