Browse Source
address, base58, base58check, hash all working with tests. base58check code taken from bitcore.patch-2
Ryan X. Charles
11 years ago
commit
862235e57e
11 changed files with 522 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||
|
*.swp |
||||
|
coverage |
||||
|
node_modules |
@ -0,0 +1,28 @@ |
|||||
|
var privsec = module.exports; |
||||
|
|
||||
|
privsec.deps = {}; |
||||
|
privsec.deps.bnjs = require('bn.js'); |
||||
|
privsec.deps.bs58 = require('bs58'); |
||||
|
privsec.deps.elliptic = require('elliptic'); |
||||
|
privsec.deps.hashjs = require('hash.js'); |
||||
|
privsec.deps.sha512 = require('sha512'); |
||||
|
privsec.deps.ripemd160 = require('ripemd160'); |
||||
|
|
||||
|
privsec.address = require('./lib/address'); |
||||
|
privsec.base58 = require('./lib/base58'); |
||||
|
privsec.base58check = require('./lib/base58check'); |
||||
|
privsec.constants = require('./lib/constants'); |
||||
|
privsec.hash = require('./lib/hash'); |
||||
|
|
||||
|
//privsec.bn = require('lib/bn');
|
||||
|
//privsec.key = require('lib/key');
|
||||
|
//privsec.point = require('lib/point');
|
||||
|
//privsec.privkey = require('lib/privkey');
|
||||
|
//privsec.pubkey = require('lib/pubkey');
|
||||
|
//privsec.script = require('lib/script');
|
||||
|
//privsec.scriptexec = require('lib/scriptexec');
|
||||
|
//privsec.tx = require('lib/tx');
|
||||
|
//privsec.txpartial = require('lib/txpartial');
|
||||
|
|
||||
|
//privsec.bip32 = require('lib/bip32');
|
||||
|
//privsec.bip70 = require('lib/bip70');
|
@ -0,0 +1,78 @@ |
|||||
|
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]); |
||||
|
}; |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
Address.prototype.toString = function() { |
||||
|
return base58check.encode(this.buf); |
||||
|
}; |
||||
|
|
||||
|
module.exports = Address; |
@ -0,0 +1,2 @@ |
|||||
|
var bs58 = require('bs58'); |
||||
|
module.exports = bs58; |
@ -0,0 +1,35 @@ |
|||||
|
var base58 = require('./base58'); |
||||
|
var sha256sha256 = require('./hash').sha256sha256; |
||||
|
|
||||
|
var base58check = module.exports; |
||||
|
|
||||
|
base58check.encode = function(buf) { |
||||
|
if (!Buffer.isBuffer(buf)) |
||||
|
throw new Error('base58check: Input must be a buffer'); |
||||
|
var checkedBuf = new Buffer(buf.length + 4); |
||||
|
var hash = sha256sha256(buf); |
||||
|
buf.copy(checkedBuf); |
||||
|
hash.copy(checkedBuf, buf.length); |
||||
|
return base58.encode(checkedBuf); |
||||
|
}; |
||||
|
|
||||
|
base58check.decode = function(s) { |
||||
|
if (typeof s !== 'string') |
||||
|
throw new Error('base58check: Input must be a string'); |
||||
|
|
||||
|
var buf = base58.decode(s); |
||||
|
|
||||
|
if (buf.length < 4) |
||||
|
throw new Error("base58check: Input string too short"); |
||||
|
|
||||
|
var data = buf.slice(0, -4); |
||||
|
var csum = buf.slice(-4); |
||||
|
|
||||
|
var hash = sha256sha256(data); |
||||
|
var hash4 = hash.slice(0, 4); |
||||
|
|
||||
|
if (csum.toString('hex') !== hash4.toString('hex')) |
||||
|
throw new Error("base58check: Checksum mismatch"); |
||||
|
|
||||
|
return data; |
||||
|
}; |
@ -0,0 +1,15 @@ |
|||||
|
exports.mainnet = { |
||||
|
pubkeyHash: 0x00, |
||||
|
privkey: 0x80, |
||||
|
p2sh: 0x05, |
||||
|
bip32pubkey: 0x0488b21e, |
||||
|
bip32privkey: 0x0488ade4, |
||||
|
}; |
||||
|
|
||||
|
exports.testnet = { |
||||
|
pubkeyHash: 0x6f, |
||||
|
privkey: 0xef, |
||||
|
p2sh: 0xc4, |
||||
|
bip32pubkey: 0x043587cf, |
||||
|
bip32privkey: 0x04358394, |
||||
|
}; |
@ -0,0 +1,42 @@ |
|||||
|
var hashjs = require('hash.js'); |
||||
|
var sha512 = require('sha512'); |
||||
|
var ripemd160 = require('ripemd160'); |
||||
|
|
||||
|
var Hash = module.exports; |
||||
|
|
||||
|
Hash.sha256 = function(buf) { |
||||
|
if (!Buffer.isBuffer(buf)) |
||||
|
throw new Error('sha256 hash must be of a buffer'); |
||||
|
var hash = (new hashjs.sha256()).update(buf).digest(); |
||||
|
return new Buffer(hash); |
||||
|
}; |
||||
|
|
||||
|
Hash.sha256sha256 = function(buf) { |
||||
|
try { |
||||
|
return Hash.sha256(Hash.sha256(buf)); |
||||
|
} catch (e) { |
||||
|
throw new Error('sha256sha256 hash must be of a buffer'); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Hash.ripemd160 = function(buf) { |
||||
|
if (!Buffer.isBuffer(buf)) |
||||
|
throw new Error('ripemd160 hash must be of a buffer'); |
||||
|
var hash = ripemd160(buf); |
||||
|
return new Buffer(hash); |
||||
|
}; |
||||
|
|
||||
|
Hash.sha256ripemd160 = function(buf) { |
||||
|
try { |
||||
|
return Hash.ripemd160(Hash.sha256(buf)); |
||||
|
} catch (e) { |
||||
|
throw new Error('sha256ripemd160 hash must be of a buffer'); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Hash.sha512 = function(buf) { |
||||
|
if (!Buffer.isBuffer(buf)) |
||||
|
throw new Error('sha512 hash must be of a buffer'); |
||||
|
var hash = sha512(buf); |
||||
|
return new Buffer(hash); |
||||
|
}; |
@ -0,0 +1,32 @@ |
|||||
|
{ |
||||
|
"name": "privsec", |
||||
|
"version": "0.0.0", |
||||
|
"description": "A bitcoin wallet prioritizing privacy and security.", |
||||
|
"main": "index.js", |
||||
|
"scripts": { |
||||
|
"test": "mocha" |
||||
|
}, |
||||
|
"keywords": [ |
||||
|
"bitcoin", |
||||
|
"bip32", |
||||
|
"bip37", |
||||
|
"bip70", |
||||
|
"stealth", |
||||
|
"merge", |
||||
|
"multisig" |
||||
|
], |
||||
|
"dependencies": { |
||||
|
"bn.js": "=0.13.3", |
||||
|
"bs58": "=1.2.1", |
||||
|
"elliptic": "=0.15.7", |
||||
|
"hash.js": "=0.3.1", |
||||
|
"ripemd160": "=0.2.0", |
||||
|
"sha512": "=0.0.1" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"chai": "~1.9.1", |
||||
|
"mocha": "~1.21.0" |
||||
|
}, |
||||
|
"author": "Ryan X. Charles <ryanxcharles@gmail.com>", |
||||
|
"license": "MIT" |
||||
|
} |
@ -0,0 +1,152 @@ |
|||||
|
var should = require('chai').should(); |
||||
|
var constants = require('../lib/constants'); |
||||
|
var Address = require('../lib/address'); |
||||
|
|
||||
|
describe('Address', function() { |
||||
|
var pubkeyhash = new Buffer('3c3fa3d4adcaf8f52d5b1843975e122548269937', 'hex'); |
||||
|
var str = '1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6'; |
||||
|
|
||||
|
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() { |
||||
|
|
||||
|
it('should return mainnet for pubkeyhash', function() { |
||||
|
var address = new Address(); |
||||
|
address.buf = Buffer.concat([new Buffer([constants.mainnet.pubkeyHash]), pubkeyhash]); |
||||
|
address.getNetwork().should.equal('mainnet'); |
||||
|
}); |
||||
|
|
||||
|
it('should return mainnet for p2sh', 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.toString().should.equal('mm1X5M2QWyHVjn7txrF7mmtZDpjCXzoa98'); |
||||
|
}); |
||||
|
|
||||
|
it('should convert this pubkeyhash on testnet and type p2sh to known address', function() { |
||||
|
var address = new Address(); |
||||
|
address.setBuf(pubkeyhash, 'testnet', 'p2sh'); |
||||
|
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() { |
||||
|
|
||||
|
it('should decode 1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6 correctly', function() { |
||||
|
var addr = new Address(); |
||||
|
addr.fromString('1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6'); |
||||
|
addr.getHash().toString('hex').should.equal('82248027cfb0fe085b750f359fd1e43234e46c7f'); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
describe('#toString', function() { |
||||
|
|
||||
|
it('should return 1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6', function() { |
||||
|
var addr = new Address(); |
||||
|
addr.fromString('1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6'); |
||||
|
addr.toString().should.equal('1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6'); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}); |
@ -0,0 +1,52 @@ |
|||||
|
var should = require('chai').should(); |
||||
|
var base58check = require('../lib/base58check'); |
||||
|
var base58 = require('../lib/base58'); |
||||
|
|
||||
|
describe('base58check', function() { |
||||
|
var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); |
||||
|
var enc = "14HV44ipwoaqfg"; |
||||
|
|
||||
|
describe('#encode', function() { |
||||
|
|
||||
|
it('should encode the buffer accurately', function() { |
||||
|
base58check.encode(buf).should.equal(enc); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an error when the input is not a buffer', function() { |
||||
|
(function() { |
||||
|
base58check.encode("string") |
||||
|
}).should.throw('base58check: Input must be a buffer'); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
describe('#decode', function() { |
||||
|
|
||||
|
it('should decode this encoded value correctly', function() { |
||||
|
base58check.decode(enc).toString('hex').should.equal(buf.toString('hex')); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an error when input is not a string', function() { |
||||
|
(function() { |
||||
|
base58check.decode(5); |
||||
|
}).should.throw('base58check: Input must be a string'); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an error when input is too short', function() { |
||||
|
(function() { |
||||
|
base58check.decode(enc.slice(0, 1)); |
||||
|
}).should.throw('base58check: Input string too short'); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an error when there is a checksum mismatch', function() { |
||||
|
var buf2 = base58.decode(enc); |
||||
|
buf2[0] = buf2[0] + 1; |
||||
|
var enc2 = base58.encode(buf2); |
||||
|
(function() { |
||||
|
base58check.decode(enc2); |
||||
|
}).should.throw('base58check: Checksum mismatch'); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}); |
@ -0,0 +1,83 @@ |
|||||
|
var should = require('chai').should(); |
||||
|
var Hash = require('../lib/hash'); |
||||
|
|
||||
|
describe('hash', function() { |
||||
|
var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); |
||||
|
var str = "test string"; |
||||
|
|
||||
|
describe('#sha256', function() { |
||||
|
|
||||
|
it('should calculate the hash of this buffer correctly', function() { |
||||
|
var hash = Hash.sha256(buf); |
||||
|
hash.toString('hex').should.equal('6f2c7b22fd1626998287b3636089087961091de80311b9279c4033ec678a83e8'); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an error when the input is not a buffer', function() { |
||||
|
(function() { |
||||
|
Hash.sha256(str); |
||||
|
}).should.throw('sha256 hash must be of a buffer'); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
describe('#sha256sha256', function() { |
||||
|
|
||||
|
it('should calculate the hash of this buffer correctly', function() { |
||||
|
var hash = Hash.sha256sha256(buf); |
||||
|
hash.toString('hex').should.equal('be586c8b20dee549bdd66018c7a79e2b67bb88b7c7d428fa4c970976d2bec5ba'); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an error when the input is not a buffer', function() { |
||||
|
(function() { |
||||
|
Hash.sha256sha256(str); |
||||
|
}).should.throw('sha256sha256 hash must be of a buffer'); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
describe('#sha256ripemd160', function() { |
||||
|
|
||||
|
it('should calculate the hash of this buffer correctly', function() { |
||||
|
var hash = Hash.sha256ripemd160(buf); |
||||
|
hash.toString('hex').should.equal('7322e2bd8535e476c092934e16a6169ca9b707ec'); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an error when the input is not a buffer', function() { |
||||
|
(function() { |
||||
|
Hash.sha256ripemd160(str); |
||||
|
}).should.throw('sha256ripemd160 hash must be of a buffer'); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
describe('#ripemd160', function() { |
||||
|
|
||||
|
it('should calculate the hash of this buffer correctly', function() { |
||||
|
var hash = Hash.ripemd160(buf); |
||||
|
hash.toString('hex').should.equal('fa0f4565ff776fee0034c713cbf48b5ec06b7f5c'); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an error when the input is not a buffer', function() { |
||||
|
(function() { |
||||
|
Hash.ripemd160(str); |
||||
|
}).should.throw('ripemd160 hash must be of a buffer'); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
describe('#sha512', function() { |
||||
|
|
||||
|
it('should calculate the hash of this buffer correctly', function() { |
||||
|
var hash = Hash.sha512(buf); |
||||
|
hash.toString('hex').should.equal('c0530aa32048f4904ae162bc14b9eb535eab6c465e960130005feddb71613e7d62aea75f7d3333ba06e805fc8e45681454524e3f8050969fe5a5f7f2392e31d0'); |
||||
|
}); |
||||
|
|
||||
|
it('should throw an error when the input is not a buffer', function() { |
||||
|
(function() { |
||||
|
Hash.sha512(str); |
||||
|
}).should.throw('sha512 hash must be of a buffer'); |
||||
|
}); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}); |
Loading…
Reference in new issue