Daniel Cousens
11 years ago
9 changed files with 323 additions and 180 deletions
@ -1,64 +1,35 @@ |
|||
var base58 = require('./base58') |
|||
var assert = require('assert') |
|||
var base58check = require('./base58check') |
|||
var convert = require('./convert') |
|||
var bitcoin = require('./network').bitcoin.pubKeyHash |
|||
var crypto = require('./crypto') |
|||
var network = require('./network') |
|||
|
|||
function Address(bytes, version) { |
|||
if (!(this instanceof Address)) { |
|||
return new Address(bytes, version) |
|||
} |
|||
function Address(hash, version) { |
|||
assert(Buffer.isBuffer(hash)) |
|||
assert(hash.length === 20) |
|||
assert(typeof version === 'number') |
|||
|
|||
if (bytes instanceof Address) { |
|||
this.hash = bytes.hash |
|||
this.version = bytes.version |
|||
} |
|||
else if (typeof bytes === 'string') { |
|||
if (bytes.length <= 35) { |
|||
var decode = base58check.decode(bytes) |
|||
|
|||
this.hash = decode.payload |
|||
this.version = decode.version |
|||
} |
|||
else if (bytes.length <= 40) { |
|||
this.hash = convert.hexToBytes(bytes) |
|||
this.version = version || bitcoin |
|||
} |
|||
else { |
|||
throw new Error('Invalid or unrecognized input') |
|||
} |
|||
} |
|||
else { |
|||
this.hash = bytes |
|||
this.version = version || bitcoin |
|||
} |
|||
this.hash = hash |
|||
this.version = version |
|||
} |
|||
|
|||
/** |
|||
* Serialize this object as a standard Bitcoin address. |
|||
* Returns the address as a base58-encoded string in the standardized format. |
|||
*/ |
|||
Address.prototype.toString = function () { |
|||
return base58check.encode(this.hash.slice(0), this.version) |
|||
// Static constructors
|
|||
Address.fromBase58Check = function(string) { |
|||
var decode = base58check.decode(string) |
|||
|
|||
return new Address(decode.payload, decode.version) |
|||
} |
|||
|
|||
/** |
|||
* Returns the version of an address, e.g. if the address belongs to the main |
|||
* net or the test net. |
|||
*/ |
|||
Address.getVersion = function (address) { |
|||
return base58.decode(address)[0] |
|||
Address.fromPubKey = function(pubKey, version) { |
|||
version = version || network.bitcoin.pubKeyHash |
|||
|
|||
var hash = crypto.hash160(pubKey.toBuffer()) |
|||
return new Address(hash, version) |
|||
} |
|||
|
|||
/** |
|||
* Returns true if a bitcoin address is a valid address, otherwise false. |
|||
*/ |
|||
Address.validate = function (address) { |
|||
try { |
|||
base58check.decode(address) |
|||
return true |
|||
} catch (e) { |
|||
return false |
|||
} |
|||
// Export functions
|
|||
Address.prototype.toBase58Check = function () { |
|||
return base58check.encode(this.hash, this.version) |
|||
} |
|||
Address.prototype.toString = Address.prototype.toBase58Check |
|||
|
|||
module.exports = Address |
|||
|
@ -1,111 +1,65 @@ |
|||
var assert = require('assert') |
|||
var Address = require('../src/address') |
|||
var network = require('../src/network') |
|||
var base58 = require('../src/base58') |
|||
var base58check = require('../src/base58check') |
|||
var bitcoin = network.bitcoin.pubKeyHash |
|||
var testnet = network.testnet.pubKeyHash |
|||
|
|||
describe('Address', function() { |
|||
var testnetAddress, bitcoinAddress |
|||
var testnetP2shAddress, bitcoinP2shAddress |
|||
var Address = require('..').Address |
|||
var ECPubKey = require('..').ECPubKey |
|||
var base58check = require('..').base58check |
|||
|
|||
beforeEach(function(){ |
|||
bitcoinAddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' |
|||
testnetAddress = 'mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhef' |
|||
bitcoinP2shAddress = '3NJZLcZEEYBpxYEUGewU4knsQRn1WM5Fkt' |
|||
testnetP2shAddress = '2MxKEf2su6FGAUfCEAHreGFQvEYrfYNHvL7' |
|||
}) |
|||
var fixtures = require('./fixtures/address') |
|||
|
|||
describe('parsing', function() { |
|||
it('works with Address object', function() { |
|||
var addr = new Address(new Address('mwrB4fgT1KSBCqELaWv7o7tsExuQzW3NY3', network.testnet.pubKeyHash)) |
|||
describe('Address', function() { |
|||
var bothVectors = fixtures.pubKeyHash.concat(fixtures.scriptHash) |
|||
|
|||
assert.equal(addr.toString(), 'mwrB4fgT1KSBCqELaWv7o7tsExuQzW3NY3') |
|||
assert.equal(addr.version, network.testnet.pubKeyHash) |
|||
}) |
|||
describe('Constructor', function() { |
|||
it('matches the test vectors', function() { |
|||
bothVectors.forEach(function(f) { |
|||
var hash = new Buffer(f.hex, 'hex') |
|||
var addr = new Address(hash, f.version) |
|||
|
|||
it('works with hex', function() { |
|||
var addr = new Address('13483382d3c3d43fc9d7b52e652b6bbb70e8b667') |
|||
assert.equal(addr.toString(), '12kxLGqrnnchwN9bHHNV2fWDtJGwxKTcJS') |
|||
assert.equal(addr.version, f.version) |
|||
assert.equal(addr.hash.toString('hex'), f.hex) |
|||
}) |
|||
|
|||
it('throws error for invalid or unrecognized input', function() { |
|||
assert.throws(function() { |
|||
new Address('beepboopbeepboopbeepboopbeepboopbeepboopbeep') |
|||
}, Error) |
|||
}) |
|||
|
|||
it('works for byte input', function() { |
|||
var hash = base58check.decode(bitcoinAddress) |
|||
var addr = new Address(hash.payload) |
|||
assert.equal(addr.hash, hash.payload) |
|||
assert.equal(network.bitcoin.pubKeyHash, hash.version) |
|||
|
|||
var hash = base58check.decode(testnetAddress) |
|||
var addr = new Address(hash.payload) |
|||
assert.equal(addr.hash, hash.payload) |
|||
assert.equal(network.testnet.pubKeyHash, hash.version) |
|||
}) |
|||
|
|||
it('fails for bad input', function() { |
|||
describe('fromBase58Check', function() { |
|||
it('throws on invalid base58check', function() { |
|||
fixtures.malformed.forEach(function(f) { |
|||
assert.throws(function() { |
|||
new Address('foo') |
|||
}, Error) |
|||
Address.fromBase58Check(f.base58check) |
|||
}) |
|||
}) |
|||
|
|||
describe('getVersion', function() { |
|||
it('returns the proper address version', function() { |
|||
assert.equal(Address.getVersion(bitcoinAddress), network.bitcoin.pubKeyHash) |
|||
assert.equal(Address.getVersion(testnetAddress), network.testnet.pubKeyHash) |
|||
}) |
|||
}) |
|||
|
|||
describe('toString', function() { |
|||
it('defaults to base58', function() { |
|||
var addr = '18fN1QTGWmHWCA9r2dyDH6FbMEyc7XHmQQ' |
|||
assert.equal((new Address(addr)).toString(), addr) |
|||
}) |
|||
}) |
|||
it('output matches the test vectors', function() { |
|||
bothVectors.forEach(function(f) { |
|||
var addr = Address.fromBase58Check(f.base58check) |
|||
|
|||
describe('Constructor', function(){ |
|||
it('resolves version correctly', function(){ |
|||
assert.equal((new Address(testnetAddress)).version, testnet) |
|||
assert.equal((new Address(bitcoinAddress)).version, bitcoin) |
|||
assert.equal((new Address(testnetP2shAddress)).version, network.testnet.scriptHash) |
|||
assert.equal((new Address(bitcoinP2shAddress)).version, network.bitcoin.scriptHash) |
|||
assert.equal(addr.version, f.version) |
|||
assert.equal(addr.hash.toString('hex'), f.hex) |
|||
}) |
|||
}) |
|||
}) |
|||
|
|||
describe('validate', function() { |
|||
it('validates known good addresses', function() { |
|||
function validate(addr, expectedVersion) { |
|||
assert.ok(Address.validate(addr)) |
|||
} |
|||
|
|||
validate(testnetAddress) |
|||
validate(bitcoinAddress) |
|||
validate('12KYrjTdVGjFMtaxERSk3gphreJ5US8aUP') |
|||
validate('12QeMLzSrB8XH8FvEzPMVoRxVAzTr5XM2y') |
|||
validate('1oNLrsHnBcR6dpaBpwz3LSwutbUNkNSjs') |
|||
validate('1SQHtwR5oJRKLfiWQ2APsAd9miUc4k2ez') |
|||
validate('116CGDLddrZhMrTwhCVJXtXQpxygTT1kHd') |
|||
describe('fromPubKey', function() { |
|||
it('output matches the test vectors', function() { |
|||
fixtures.pubKeyHash.forEach(function(f) { |
|||
var pub = ECPubKey.fromBuffer(new Buffer(f.pubKey, 'hex')) |
|||
var addr = Address.fromPubKey(pub, f.version) |
|||
|
|||
// p2sh addresses
|
|||
validate(testnetP2shAddress) |
|||
validate(bitcoinP2shAddress) |
|||
assert.equal(addr.version, f.version) |
|||
assert.equal(addr.hash.toString('hex'), f.hex) |
|||
}) |
|||
}) |
|||
}) |
|||
|
|||
it('does not validate illegal examples', function() { |
|||
function invalid(addr) { |
|||
assert.ok(!Address.validate(addr)) |
|||
} |
|||
describe('toBase58Check', function() { |
|||
it('output matches the test vectors', function() { |
|||
bothVectors.forEach(function(f) { |
|||
var addr = Address.fromBase58Check(f.base58check) |
|||
var base58check = addr.toBase58Check() |
|||
|
|||
invalid(''); //empty should be invalid
|
|||
invalid('%%@'); // invalid base58 string
|
|||
invalid('1A1zP1eP5QGefi2DzPTf2L5SLmv7DivfNz'); // bad address (doesn't checksum)
|
|||
invalid('mzBc4XEFSdzCDcTxAgf6EZXgsZWpztRhe'); // bad address (doesn't checksum)
|
|||
assert.equal(base58check, f.base58check) |
|||
}) |
|||
}) |
|||
}) |
|||
}) |
|||
|
@ -0,0 +1,198 @@ |
|||
module.exports = { |
|||
pubKeyHash: [ |
|||
{ |
|||
version: 0, |
|||
pubKey: '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', |
|||
hex: '91b24bf9f5288532960ac687abb035127b1d28a5', |
|||
base58check: '1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', |
|||
hex: '751e76e8199196d454941c45d1b3a323f1433bd6', |
|||
base58check: '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '04750578aed9dd59b43d0f3fbbcf26ed132bed71d202183674e6f0f01b71ad25eea9c42714b1f602b87164f76ef169477a7db9591e10755783006ebfa9c11946c3', |
|||
hex: 'c4fa066a855f0e2739f21393595ca3d7ff422fb6', |
|||
base58check: '1JxWxePiX2GHN444g5V5JpBLEi3LV8cHGS' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '03750578aed9dd59b43d0f3fbbcf26ed132bed71d202183674e6f0f01b71ad25ee', |
|||
hex: '6b420cc35afb4d96ea118dbf1fc8ef4b0990798c', |
|||
base58check: '1An8RkWzqVF9GvpQFVX5AM4jpFe43eoPfe' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777', |
|||
hex: 'bec08011c9e76dcc42e739a2d7752c2e3ac86e6e', |
|||
base58check: '1JPbzbsAx1HyaDQoLMapWGoqf9pD5uha5m' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', |
|||
hex: 'adde4c73c7b9cee17da6c7b3e2b2eea1a0dcbe67', |
|||
base58check: '1GrLCmVQXoyJXaPJQdqssNqwxvha1eUo2E' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '04cfd1c01397fc8d81fc6330d1a938e93551118a6f6db2e470ee353ffc1a51798073ab8fdf1fa8ffabce209ffe00538aa67d6ea819ddaa9410264faa34397306d7', |
|||
hex: 'e95895d8f1c5755b94d87bc42528ed37956971c0', |
|||
base58check: '1NGpZiQe9BDt6QEErh7cQxoaxns5fV1u9m' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '03cfd1c01397fc8d81fc6330d1a938e93551118a6f6db2e470ee353ffc1a517980', |
|||
hex: '53991cac832df8180da4c97e77734a8fbce31fdf', |
|||
base58check: '18d2WLBU9YnrdpjYVS5xBpmk5KcUPwEbYU' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '0422c39cb05bb33be292ee678c590a11ebcc75850944c8937a93bebddac4fb66cb9a473714030a262a19db874c781aa37a0ab8b7f519c2a71e94767e50f5f8900b', |
|||
hex: 'e6942137944f168785f8551956ae9a3fbbd12dc1', |
|||
base58check: '1N2BsMh9XktPCo2WxadsTV8Eni5AJhF7WG' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '0322c39cb05bb33be292ee678c590a11ebcc75850944c8937a93bebddac4fb66cb', |
|||
hex: '02e70da0f47e5178ce258e6c9f220d17a538f7a6', |
|||
base58check: '1GM9F9uX4U9AgQzitsLQXghD7fb6cWNCX' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '04c0520eb902f43b6fe91fea74eb6cab3fd5c54a6646a7e012fd244eac7864e08e276506e819cced089947254d7ed7b9a502cc77421b8d48c3dd00820f82971c85', |
|||
hex: '2ac31bc5ced6f167ff5aa4b93043590c8d6fc59c', |
|||
base58check: '14u77S4ysnoiaQXaVWBokFYwhVuPTPf2Tk' |
|||
}, |
|||
{ |
|||
version: 0, |
|||
pubKey: '03c0520eb902f43b6fe91fea74eb6cab3fd5c54a6646a7e012fd244eac7864e08e', |
|||
hex: 'e522cb50376421b2034ff2770421881f8b82ceaa', |
|||
base58check: '1MtZRUP1nB1wJ7ftUeLibBM6WXEksTUtiS' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', |
|||
hex: '91b24bf9f5288532960ac687abb035127b1d28a5', |
|||
base58check: 'mtoKs9V381UAhUia3d7Vb9GNak8Qvmcsme' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', |
|||
hex: '751e76e8199196d454941c45d1b3a323f1433bd6', |
|||
base58check: 'mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '04750578aed9dd59b43d0f3fbbcf26ed132bed71d202183674e6f0f01b71ad25eea9c42714b1f602b87164f76ef169477a7db9591e10755783006ebfa9c11946c3', |
|||
hex: 'c4fa066a855f0e2739f21393595ca3d7ff422fb6', |
|||
base58check: 'myUUFhUhL3hY9AXgPeTT8jPf6he3Ti4vtG' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '03750578aed9dd59b43d0f3fbbcf26ed132bed71d202183674e6f0f01b71ad25ee', |
|||
hex: '6b420cc35afb4d96ea118dbf1fc8ef4b0990798c', |
|||
base58check: 'mqJ5iobyeWgQ43J1y4VSzGH4gFEkxw2dFV' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777', |
|||
hex: 'bec08011c9e76dcc42e739a2d7752c2e3ac86e6e', |
|||
base58check: 'mxuZHex9m2jEMKtR3vZCLC2AX9QuyLzj7L' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', |
|||
hex: 'adde4c73c7b9cee17da6c7b3e2b2eea1a0dcbe67', |
|||
base58check: 'mwNHVpaPLqQZJgrv8CpFhJ4GpvJGumskXi' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '04cfd1c01397fc8d81fc6330d1a938e93551118a6f6db2e470ee353ffc1a51798073ab8fdf1fa8ffabce209ffe00538aa67d6ea819ddaa9410264faa34397306d7', |
|||
hex: 'e95895d8f1c5755b94d87bc42528ed37956971c0', |
|||
base58check: 'n2nmrmVcxCf8sWhraG5zEt1upnTncBNXUE' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '03cfd1c01397fc8d81fc6330d1a938e93551118a6f6db2e470ee353ffc1a517980', |
|||
hex: '53991cac832df8180da4c97e77734a8fbce31fdf', |
|||
base58check: 'mo8yoPGSxaE7QwDAD14L1jz4wKDBG3e5bV' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '0422c39cb05bb33be292ee678c590a11ebcc75850944c8937a93bebddac4fb66cb9a473714030a262a19db874c781aa37a0ab8b7f519c2a71e94767e50f5f8900b', |
|||
hex: 'e6942137944f168785f8551956ae9a3fbbd12dc1', |
|||
base58check: 'n2Y9AQn8LnKdyuW8g9cFHQLZehfsDf33fp' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '0322c39cb05bb33be292ee678c590a11ebcc75850944c8937a93bebddac4fb66cb', |
|||
hex: '02e70da0f47e5178ce258e6c9f220d17a538f7a6', |
|||
base58check: 'mfnJSJEtL5uPwntcSTqiESu257GHwWoA52' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '04c0520eb902f43b6fe91fea74eb6cab3fd5c54a6646a7e012fd244eac7864e08e276506e819cced089947254d7ed7b9a502cc77421b8d48c3dd00820f82971c85', |
|||
hex: '2ac31bc5ced6f167ff5aa4b93043590c8d6fc59c', |
|||
base58check: 'mjR4QV9xgpEyMX1CD5ABaAmGZVW6MBcEML' |
|||
}, |
|||
{ |
|||
version: 111, |
|||
pubKey: '03c0520eb902f43b6fe91fea74eb6cab3fd5c54a6646a7e012fd244eac7864e08e', |
|||
hex: 'e522cb50376421b2034ff2770421881f8b82ceaa', |
|||
base58check: 'n2QWiXTzbCTC5E9WCDK6R6ZRNWqTq1o9RY' |
|||
} |
|||
], |
|||
scriptHash: [ |
|||
{ |
|||
version: 5, |
|||
hex: '30e22c5ffe8d09ab93c9cfd719cb4a0b524bb298', |
|||
base58check: '369VJYu1PiVLvnQT72MakjzGUKwzgtuLrN' |
|||
}, |
|||
{ |
|||
version: 5, |
|||
hex: '16b9b33baa6e597a60f62e57775e8c9e76e215c2', |
|||
base58check: '33mBFSCFsQ29FUNW1dZGfDm1DSfcdHwJjD' |
|||
}, |
|||
{ |
|||
version: 5, |
|||
hex: '57fa71c2ada72b1656fb8e44c8c2635d4f76296f', |
|||
base58check: '39iCjQvovdxeDFbfFGTF7Fc7uxECKcuasG' |
|||
}, |
|||
{ |
|||
version: 5, |
|||
hex: 'f6a9a87cd6b20cf56c5a9947e04c7fbeb9d4004a', |
|||
base58check: '3QBFPnjSz6NT9fcp6Rw7jffWBM3e6uD3Rp' |
|||
}, |
|||
{ |
|||
version: 196, |
|||
hex: 'fc50013ecd7e130bb33dbe0bda5c0c4bda2278e6', |
|||
base58check: '2NGFL7Tfur3FshijLTq9xrp5EWMPrHHy4N1' |
|||
}, |
|||
{ |
|||
version: 196, |
|||
hex: '81fad5e4b0f4a74d50f645e02a1929de3a434dee', |
|||
base58check: '2N56VePS9LHTxxKkDSYpHVT3tTBUzXzydDR' |
|||
}, |
|||
{ |
|||
version: 196, |
|||
hex: 'f7bd4199de4b0a1e85fc7d79c6a90c108a4d789f', |
|||
base58check: '2NFq9cRNwPxerxpgk5ySL87JvJi8yvZPpP9' |
|||
}, |
|||
{ |
|||
version: 196, |
|||
hex: 'b43c431f0b5d3ccb638d5e4358b08129faf5355d', |
|||
base58check: '2N9gDouPBrsvZoX5bKGFBNDoApKfsLpMVJf' |
|||
} |
|||
], |
|||
malformed: [ |
|||
'nQyyFDwTJW5K', |
|||
'NEqYWpmWNgjM', |
|||
'Bv6FiH8KsiJj9aZkvCcCEv', |
|||
'39yrcy7Q82EnCdetJzZfLhQj6spq', |
|||
'wVtEiHQZekSnDHC512mwT3g7Ahr', |
|||
'3GfCH4v3ZjfVMy7raFPPaE9fkBbh' |
|||
] |
|||
} |
Loading…
Reference in new issue