From 862235e57eb1a5ea04226a8406f61bbd320bd7f0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 6 Aug 2014 18:25:45 -0700 Subject: [PATCH 001/280] initial commit address, base58, base58check, hash all working with tests. base58check code taken from bitcore. --- .gitignore | 3 + index.js | 28 ++++++++ lib/address.js | 78 ++++++++++++++++++++ lib/base58.js | 2 + lib/base58check.js | 35 +++++++++ lib/constants.js | 15 ++++ lib/hash.js | 42 +++++++++++ package.json | 32 +++++++++ test/test.address.js | 152 +++++++++++++++++++++++++++++++++++++++ test/test.base58check.js | 52 ++++++++++++++ test/test.hash.js | 83 +++++++++++++++++++++ 11 files changed, 522 insertions(+) create mode 100644 .gitignore create mode 100644 index.js create mode 100644 lib/address.js create mode 100644 lib/base58.js create mode 100644 lib/base58check.js create mode 100644 lib/constants.js create mode 100644 lib/hash.js create mode 100644 package.json create mode 100644 test/test.address.js create mode 100644 test/test.base58check.js create mode 100644 test/test.hash.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2716af2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.swp +coverage +node_modules diff --git a/index.js b/index.js new file mode 100644 index 0000000..e0e45e8 --- /dev/null +++ b/index.js @@ -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'); diff --git a/lib/address.js b/lib/address.js new file mode 100644 index 0000000..720bb88 --- /dev/null +++ b/lib/address.js @@ -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; diff --git a/lib/base58.js b/lib/base58.js new file mode 100644 index 0000000..e7516a7 --- /dev/null +++ b/lib/base58.js @@ -0,0 +1,2 @@ +var bs58 = require('bs58'); +module.exports = bs58; diff --git a/lib/base58check.js b/lib/base58check.js new file mode 100644 index 0000000..15313d8 --- /dev/null +++ b/lib/base58check.js @@ -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; +}; diff --git a/lib/constants.js b/lib/constants.js new file mode 100644 index 0000000..5b90914 --- /dev/null +++ b/lib/constants.js @@ -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, +}; diff --git a/lib/hash.js b/lib/hash.js new file mode 100644 index 0000000..4219f86 --- /dev/null +++ b/lib/hash.js @@ -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); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..7b8b67a --- /dev/null +++ b/package.json @@ -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 ", + "license": "MIT" +} diff --git a/test/test.address.js b/test/test.address.js new file mode 100644 index 0000000..53b3178 --- /dev/null +++ b/test/test.address.js @@ -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'); + }); + + }); + +}); diff --git a/test/test.base58check.js b/test/test.base58check.js new file mode 100644 index 0000000..8b50813 --- /dev/null +++ b/test/test.base58check.js @@ -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'); + }); + + }); + +}); diff --git a/test/test.hash.js b/test/test.hash.js new file mode 100644 index 0000000..eea3517 --- /dev/null +++ b/test/test.hash.js @@ -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'); + }); + + }); + +}); From e0deb0407c9404ed7f1510042d1915c8cc520795 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 6 Aug 2014 18:36:30 -0700 Subject: [PATCH 002/280] add big number support Extend bn.js with some convenience methods. Extension code taken from bitcore. --- index.js | 2 +- lib/bn.js | 109 ++++++++++++++++++++++++++++++++++++++++++++++ test/test.bn.js | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 lib/bn.js create mode 100644 test/test.bn.js diff --git a/index.js b/index.js index e0e45e8..614b384 100644 --- a/index.js +++ b/index.js @@ -11,10 +11,10 @@ privsec.deps.ripemd160 = require('ripemd160'); privsec.address = require('./lib/address'); privsec.base58 = require('./lib/base58'); privsec.base58check = require('./lib/base58check'); +privsec.bn = require('./lib/bn'); 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'); diff --git a/lib/bn.js b/lib/bn.js new file mode 100644 index 0000000..ad1ba05 --- /dev/null +++ b/lib/bn.js @@ -0,0 +1,109 @@ +var _bnjs = require('bn.js'); + +var bnjs = function bnjs_extended(n) { + if (!(this instanceof bnjs_extended)) { + return new bnjs(n); + } + arguments[0] = n; + return _bnjs.apply(this, arguments); +}; + +module.exports = bnjs; + +bnjs.prototype = _bnjs.prototype; + +var reversebuf = function(buf, nbuf) { + for (var i = 0; i < buf.length; i++) { + nbuf[i] = buf[buf.length-1-i]; + } +}; + +bnjs.fromBuffer = function(buf, opts) { + if (typeof opts !== 'undefined' && opts.endian === 'little') { + var nbuf = new Buffer(buf.length); + reversebuf(buf, nbuf); + buf = nbuf; + } + var hex = buf.toString('hex'); + if (hex.length % 2) + hex = "0" + hex; + var bn = new bnjs(hex, 16); + return bn; +}; + +bnjs.prototype.toBuffer = function(opts) { + var buf; + if (opts && opts.size) { + var hex = this.toString(16); + if (hex.length % 2) + hex = "0" + hex; + var natlen = hex.length/2; + buf = new Buffer(hex, 'hex'); + + if (natlen == opts.size) + buf = buf; + + else if (natlen > opts.size) { + buf = buf.slice(natlen - buf.length, buf.length); + } + + else if (natlen < opts.size) { + var rbuf = new Buffer(opts.size); + //rbuf.fill(0); + for (var i = 0; i < buf.length; i++) + rbuf[rbuf.length-1-i] = buf[buf.length-1-i]; + for (var i = 0; i < opts.size - natlen; i++) + rbuf[i] = 0; + buf = rbuf; + } + } + else { + var hex = this.toString(16); + if (hex.length % 2) + hex = "0" + hex; + buf = new Buffer(hex, 'hex'); + } + + if (typeof opts !== 'undefined' && opts.endian === 'little') { + var nbuf = new Buffer(buf.length); + reversebuf(buf, nbuf); + buf = nbuf; + } + + return buf; +}; + +function decorate(name) { + bnjs.prototype['_' + name] = _bnjs.prototype[name]; + var f = function(b) { + if (typeof b === 'string') + b = new _bnjs(b); + else if (typeof b === 'number') + b = new _bnjs(b.toString()); + return this['_' + name](b); + }; + bnjs.prototype[name] = f; +}; + +_bnjs.prototype.gt = function(b) { + return this.cmp(b) > 0; +}; + +_bnjs.prototype.lt = function(b) { + return this.cmp(b) < 0; +}; + +decorate('add'); +decorate('sub'); +decorate('mul'); +decorate('mod'); +decorate('div'); +decorate('cmp'); +decorate('gt'); +decorate('lt'); + +bnjs.prototype.toNumber = function() { + return parseInt(this['toString'](10), 10); +}; + +module.exports = bnjs; diff --git a/test/test.bn.js b/test/test.bn.js new file mode 100644 index 0000000..1e96362 --- /dev/null +++ b/test/test.bn.js @@ -0,0 +1,112 @@ +var chai = chai || require('chai'); +var should = chai.should(); +var assert = chai.assert; +var BN = require('../lib/bn'); + +describe('BN', function() { + it('should create a bn', function() { + var bn = new BN(50); + should.exist(bn); + bn.toString().should.equal('50'); + }); + + it('should parse this number', function() { + var bn = new BN(999970000); + bn.toString().should.equal('999970000'); + }); + + it('should parse numbers below and at bn.js internal word size', function() { + var bn = new BN(Math.pow(2, 26) - 1); + bn.toString().should.equal((Math.pow(2, 26) - 1).toString()); + var bn = new BN(Math.pow(2, 26)); + bn.toString().should.equal((Math.pow(2, 26)).toString()); + }); + + describe('#add', function() { + + it('should add two small numbers together', function() { + var bn1 = new BN(50); + var bn2 = new BN(75); + var bn3 = bn1.add(bn2); + bn3.toString().should.equal('125'); + }); + + }); + + describe('#sub', function() { + + it('should subtract a small number', function() { + var bn1 = new BN(50); + var bn2 = new BN(25); + var bn3 = bn1.sub(bn2); + bn3.toString().should.equal('25'); + }); + + }); + + describe('#gt', function() { + + it('should say 1 is greater than 0', function() { + var bn1 = new BN(1); + var bn0 = new BN(0); + bn1.gt(bn0).should.equal(true); + }); + + it('should say a big number is greater than a small big number', function() { + var bn1 = new BN('24023452345398529485723980457'); + var bn0 = new BN('34098234283412341234049357'); + bn1.gt(bn0).should.equal(true); + }); + + it('should say a big number is great than a standard number', function() { + var bn1 = new BN('24023452345398529485723980457'); + var bn0 = new BN(5); + bn1.gt(bn0).should.equal(true); + }); + + }); + + describe('#fromBuffer', function() { + + it('should work with big endian', function() { + var bn = BN.fromBuffer(new Buffer('0001', 'hex'), {endian: 'big'}); + bn.toString().should.equal('1'); + }); + + it('should work with big endian 256', function() { + var bn = BN.fromBuffer(new Buffer('0100', 'hex'), {endian: 'big'}); + bn.toString().should.equal('256'); + }); + + it('should work with little endian if we specify the size', function() { + var bn = BN.fromBuffer(new Buffer('0100', 'hex'), {size: 2, endian: 'little'}); + bn.toString().should.equal('1'); + }); + + }); + + describe('#toBuffer', function() { + + it('should create a 4 byte buffer', function() { + var bn = new BN(1); + bn.toBuffer({size: 4}).toString('hex').should.equal('00000001'); + }); + + it('should create a 4 byte buffer in little endian', function() { + var bn = new BN(1); + bn.toBuffer({size: 4, endian: 'little'}).toString('hex').should.equal('01000000'); + }); + + it('should create a 2 byte buffer even if you ask for a 1 byte', function() { + var bn = new BN('ff00', 16); + bn.toBuffer({size: 1}).toString('hex').should.equal('ff00'); + }); + + it('should create a 4 byte buffer even if you ask for a 1 byte', function() { + var bn = new BN('ffffff00', 16); + bn.toBuffer({size: 4}).toString('hex').should.equal('ffffff00'); + }); + + }); + +}); From ca4131ab05bbdb1cb17fd744345cb81973fc1202 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 6 Aug 2014 19:06:32 -0700 Subject: [PATCH 003/280] remove ripemd160 dep., replace with hash.js --- index.js | 1 - lib/hash.js | 3 +-- package.json | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/index.js b/index.js index 614b384..6a2b336 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,6 @@ 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'); diff --git a/lib/hash.js b/lib/hash.js index 4219f86..f02d570 100644 --- a/lib/hash.js +++ b/lib/hash.js @@ -1,6 +1,5 @@ var hashjs = require('hash.js'); var sha512 = require('sha512'); -var ripemd160 = require('ripemd160'); var Hash = module.exports; @@ -22,7 +21,7 @@ Hash.sha256sha256 = function(buf) { Hash.ripemd160 = function(buf) { if (!Buffer.isBuffer(buf)) throw new Error('ripemd160 hash must be of a buffer'); - var hash = ripemd160(buf); + var hash = (new hashjs.ripemd160()).update(buf).digest(); return new Buffer(hash); }; diff --git a/package.json b/package.json index 7b8b67a..7d56d18 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "bs58": "=1.2.1", "elliptic": "=0.15.7", "hash.js": "=0.3.1", - "ripemd160": "=0.2.0", "sha512": "=0.0.1" }, "devDependencies": { From 657f992e7b3a473257183b14e9206efa74752bfa Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 6 Aug 2014 21:02:42 -0700 Subject: [PATCH 004/280] point --- index.js | 2 +- lib/point.js | 32 ++++++++++++++++ test/test.point.js | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 lib/point.js create mode 100644 test/test.point.js diff --git a/index.js b/index.js index 6a2b336..3eadd93 100644 --- a/index.js +++ b/index.js @@ -13,9 +13,9 @@ privsec.base58check = require('./lib/base58check'); privsec.bn = require('./lib/bn'); privsec.constants = require('./lib/constants'); privsec.hash = require('./lib/hash'); +privsec.point = require('./lib/point'); //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'); diff --git a/lib/point.js b/lib/point.js new file mode 100644 index 0000000..2275495 --- /dev/null +++ b/lib/point.js @@ -0,0 +1,32 @@ +var bn = require('./bn'); +var elliptic = require('elliptic'); + +var ec = elliptic.curves.secp256k1; +var Point = ec.curve.point.bind(ec.curve) +var p = ec.curve.point(); +var Curve = Object.getPrototypeOf(ec.curve); +Point.prototype = Object.getPrototypeOf(p); + +Point.pointFromX = ec.curve.pointFromX.bind(ec.curve); + +Point.getG = function() { + var p = Point(ec.curve.g.getX(), ec.curve.g.getY()); + return p; +}; + +Point.getN = function() { + return bn(ec.curve.n.toArray()); +}; + +Point.prototype._getX = Point.prototype.getX; +Point.prototype.getX = function() { + var n = bn(this._getX().toArray()); + return bn(this._getX().toArray()); +}; + +Point.prototype._getY = Point.prototype.getY; +Point.prototype.getY = function() { + return bn(this._getY().toArray()); +}; + +module.exports = Point; diff --git a/test/test.point.js b/test/test.point.js new file mode 100644 index 0000000..71efd5b --- /dev/null +++ b/test/test.point.js @@ -0,0 +1,92 @@ +var should = require('chai').should(); +var point = require('../lib/point'); +var bn = require('../lib/bn'); + +describe('point', function() { + + it('should create a point', function() { + var p = point(); + should.exist(p); + }); + + describe('#getX', function() { + + it('should return 0', function() { + var p = point(); + p.getX().toString().should.equal('0'); + }); + + it('should be convertable to a buffer', function() { + var p = point(); + p.getX().toBuffer({size: 32}).length.should.equal(32); + }); + + }); + + describe('#getY', function() { + + it('should return 0', function() { + var p = point(); + p.getY().toString().should.equal('0'); + }); + + it('should be convertable to a buffer', function() { + var p = point(); + p.getY().toBuffer({size: 32}).length.should.equal(32); + }); + + }); + + describe('#add', function() { + + it('should accurately add g to itself', function() { + var p1 = point.getG(); + var p2 = point.getG(); + var p3 = p1.add(p2); + p3.getX().toString().should.equal('89565891926547004231252920425935692360644145829622209833684329913297188986597'); + p3.getY().toString().should.equal('12158399299693830322967808612713398636155367887041628176798871954788371653930'); + }); + + }); + + describe('#mul', function() { + + it('should accurately multiply g by 2', function() { + var g = point.getG(); + var b = g.mul(bn(2)); + b.getX().toString().should.equal('89565891926547004231252920425935692360644145829622209833684329913297188986597'); + b.getY().toString().should.equal('12158399299693830322967808612713398636155367887041628176798871954788371653930'); + }); + + it('should accurately multiply g by n-1', function() { + var g = point.getG(); + var n = point.getN(); + var b = g.mul(n.sub(1)); + b.getX().toString().should.equal('55066263022277343669578718895168534326250603453777594175500187360389116729240'); + b.getY().toString().should.equal('83121579216557378445487899878180864668798711284981320763518679672151497189239'); + }); + + //not sure if this is technically accurate or not... + //normally, you should always multiply g by something less than n + //but it is the same result in OpenSSL + it('should accurately multiply g by n+1', function() { + var g = point.getG(); + var n = point.getN(); + var b = g.mul(n.add(1)); + b.getX().toString().should.equal('55066263022277343669578718895168534326250603453777594175500187360389116729240'); + b.getY().toString().should.equal('32670510020758816978083085130507043184471273380659243275938904335757337482424'); + }); + + }); + + describe('#pointFromX', function() { + + it('should return g', function() { + var g = point.getG(); + var p = point.pointFromX(false, g.getX()); + g.eq(p).should.equal(true); + }); + + }); + +}); From 8106bed6d04dea49cf84a70d7fe74bfdaac4fe25 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 6 Aug 2014 21:17:27 -0700 Subject: [PATCH 005/280] expose buffer --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 3eadd93..9a711a5 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,7 @@ privsec.address = require('./lib/address'); privsec.base58 = require('./lib/base58'); privsec.base58check = require('./lib/base58check'); privsec.bn = require('./lib/bn'); +privsec.buffer = Buffer; privsec.constants = require('./lib/constants'); privsec.hash = require('./lib/hash'); privsec.point = require('./lib/point'); From 9f7e01b8caa4fe0876fa5dfb2943079038069d48 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 6 Aug 2014 21:39:25 -0700 Subject: [PATCH 006/280] rename redundant point.pointFromX to point.fromX --- lib/point.js | 2 +- test/test.point.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/point.js b/lib/point.js index 2275495..32e41e6 100644 --- a/lib/point.js +++ b/lib/point.js @@ -7,7 +7,7 @@ var p = ec.curve.point(); var Curve = Object.getPrototypeOf(ec.curve); Point.prototype = Object.getPrototypeOf(p); -Point.pointFromX = ec.curve.pointFromX.bind(ec.curve); +Point.fromX = ec.curve.pointFromX.bind(ec.curve); Point.getG = function() { var p = Point(ec.curve.g.getX(), ec.curve.g.getY()); diff --git a/test/test.point.js b/test/test.point.js index 71efd5b..01fbc31 100644 --- a/test/test.point.js +++ b/test/test.point.js @@ -79,11 +79,11 @@ describe('point', function() { }); - describe('#pointFromX', function() { + describe('#fromX', function() { it('should return g', function() { var g = point.getG(); - var p = point.pointFromX(false, g.getX()); + var p = point.fromX(false, g.getX()); g.eq(p).should.equal(true); }); From 3df3d4143404e9ca789550700aa8d2a69aa321cd Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 6 Aug 2014 22:47:10 -0700 Subject: [PATCH 007/280] privkey --- index.js | 2 +- lib/privkey.js | 77 ++++++++++++++++++++++++++++++++++++++++++++ test/test.privkey.js | 74 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 lib/privkey.js create mode 100644 test/test.privkey.js diff --git a/index.js b/index.js index 9a711a5..7371c59 100644 --- a/index.js +++ b/index.js @@ -15,9 +15,9 @@ privsec.buffer = Buffer; privsec.constants = require('./lib/constants'); privsec.hash = require('./lib/hash'); privsec.point = require('./lib/point'); +privsec.privkey = require('./lib/privkey'); //privsec.key = require('lib/key'); -//privsec.privkey = require('lib/privkey'); //privsec.pubkey = require('lib/pubkey'); //privsec.script = require('lib/script'); //privsec.scriptexec = require('lib/scriptexec'); diff --git a/lib/privkey.js b/lib/privkey.js new file mode 100644 index 0000000..ecb7493 --- /dev/null +++ b/lib/privkey.js @@ -0,0 +1,77 @@ +var bn = require('./bn'); +var point = require('./point'); +var constants = require('./constants'); +var base58check = require('./base58check'); + +var Privkey = function(n, network, compressed) { + if (typeof n === 'undefined') + return; + this.setNumber(n); + this.setNetwork(network); + this.setCompressed(compressed); +}; + +Privkey.prototype.setNumber = function(n) { + if (!n.lt(point.getN())) + throw new Error('privkey: Number must be less than N'); + this.n = n; +}; + +Privkey.prototype.setNetwork = function(network) { + if (typeof constants[network] === undefined) + throw new Error('privkey: Must specify the network ("mainnet" or "testnet")'); + this.network = network; +}; + +Privkey.prototype.setCompressed = function(compressed) { + if (typeof compressed !== 'boolean') + throw new Error('privkey: Must specify whether the corresponding public key is compressed or not (true or false)'); + this.compressed = compressed; +}; + +Privkey.prototype.toWIF = function() { + this.setNetwork(this.network); + this.setCompressed(this.compressed); + + var network = this.network; + var compressed = this.compressed; + + var privbuf = this.n.toBuffer({size: 32}); + var buf; + if (compressed) + buf = Buffer.concat([new Buffer([constants[network].privkey]), this.n.toBuffer({size: 32}), new Buffer([0x01])]); + else + buf = Buffer.concat([new Buffer([constants[network].privkey]), this.n.toBuffer({size: 32})]); + + return base58check.encode(buf); +}; + +Privkey.prototype.fromWIF = function(str) { + var buf = base58check.decode(str); + + if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] == 1) + this.compressed = true; + else if (buf.length === 1 + 32) + this.compressed = false; + else + throw new Error('privkey: Length of buffer must be 33 (uncompressed) or 34 (compressed)'); + + if (buf[0] === constants.mainnet.privkey) + this.network = 'mainnet'; + else if (buf[0] === constants.testnet.privkey) + this.network = 'testnet'; + else + throw new Error('privkey: Invalid network'); + + this.n = bn.fromBuffer(buf.slice(1, 32 + 1)); +}; + +Privkey.prototype.toString = function() { + return this.toWIF(); +}; + +Privkey.prototype.fromString = function(str) { + this.fromWIF(str); +}; + +module.exports = Privkey; diff --git a/test/test.privkey.js b/test/test.privkey.js new file mode 100644 index 0000000..db271f9 --- /dev/null +++ b/test/test.privkey.js @@ -0,0 +1,74 @@ +var Privkey = require('../lib/privkey'); +var base58check = require('../lib/base58check'); +var bn = require('../lib/bn'); +var should = require('chai').should(); + +describe('#privkey', function() { + var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'; + var buf = new Buffer(hex, 'hex'); + var enctestnet = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; + var enctu = '92jJzK4tbURm1C7udQXxeCBvXHoHJstDXRxAMouPG1k1XUaXdsu'; + var encmainnet = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy'; + var encmu = '5JxgQaFM1FMd38cd14e3mbdxsdSa9iM2BV6DHBYsvGzxkTNQ7Un'; + + it('should create an empty private key', function() { + var privkey = new Privkey(); + should.exist(privkey); + }); + + it('should create a mainnet private key', function() { + var privkey = new Privkey(bn.fromBuffer(buf), 'mainnet', true); + privkey.toString().should.equal(encmainnet); + }); + + it('should create an uncompressed testnet private key', function() { + var privkey = new Privkey(bn.fromBuffer(buf), 'testnet', false); + privkey.toString().should.equal(enctu); + }); + + it('should create an uncompressed mainnet private key', function() { + var privkey = new Privkey(bn.fromBuffer(buf), 'mainnet', false); + privkey.toString().should.equal(encmu); + }); + + describe('#fromWIF', function() { + + it('should parse this compressed testnet address correctly', function() { + var privkey = new Privkey(); + privkey.fromWIF(encmainnet); + privkey.toWIF().should.equal(encmainnet); + }); + + }); + + describe('#toWIF', function() { + + it('should parse this compressed testnet address correctly', function() { + var privkey = new Privkey(); + privkey.fromWIF(enctestnet); + privkey.toWIF().should.equal(enctestnet); + }); + + }); + + describe('#fromString', function() { + + it('should parse this uncompressed testnet address correctly', function() { + var privkey = new Privkey(); + privkey.fromString(enctu); + privkey.toWIF().should.equal(enctu); + }); + + }); + + describe('#toString', function() { + + it('should parse this uncompressed mainnet address correctly', function() { + var privkey = new Privkey(); + privkey.fromString(encmu); + privkey.toString().should.equal(encmu); + }); + + }); + +}); From 448f532738c49af9a8ae2a767028471f69d5ec7b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 7 Aug 2014 16:18:17 -0700 Subject: [PATCH 008/280] pubkey --- index.js | 2 +- lib/pubkey.js | 67 +++++++++++++++++++++++++++++++ test/test.pubkey.js | 96 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 lib/pubkey.js create mode 100644 test/test.pubkey.js diff --git a/index.js b/index.js index 7371c59..4c943e9 100644 --- a/index.js +++ b/index.js @@ -16,9 +16,9 @@ privsec.constants = require('./lib/constants'); privsec.hash = require('./lib/hash'); privsec.point = require('./lib/point'); privsec.privkey = require('./lib/privkey'); +privsec.pubkey = require('./lib/pubkey'); //privsec.key = require('lib/key'); -//privsec.pubkey = require('lib/pubkey'); //privsec.script = require('lib/script'); //privsec.scriptexec = require('lib/scriptexec'); //privsec.tx = require('lib/tx'); diff --git a/lib/pubkey.js b/lib/pubkey.js new file mode 100644 index 0000000..8da4b91 --- /dev/null +++ b/lib/pubkey.js @@ -0,0 +1,67 @@ +var point = require('./point'); +var bn = require('./bn'); + +var Pubkey = function(p) { + this.p = p; +}; + +Pubkey.prototype.fromDER = function(buf) { + if (buf[0] == 0x04) { + var xbuf = buf.slice(1, 33); + var ybuf = buf.slice(33, 65); + if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) + throw new Error('pubkey: Length of x and y must be 32 bytes'); + var x = bn(xbuf); + var y = bn(ybuf); + this.p = point(x, y); + } else if (buf[0] == 0x03) { + var xbuf = buf.slice(1); + var x = bn(xbuf); + this.fromX(true, x); + } else if (buf[0] == 0x02) { + var xbuf = buf.slice(1); + var x = bn(xbuf); + this.fromX(false, x); + } else { + throw new Error('pubkey: Invalid DER format pubkey'); + } +}; + +Pubkey.prototype.fromString = function(str) { + this.fromDER(new Buffer(str, 'hex')); +}; + +Pubkey.prototype.fromX = function(odd, x) { + if (typeof odd !== 'boolean') + throw new Error('pubkey: Must specify whether y is odd or not (true or false)'); + this.p = point.fromX(odd, x); +}; + +Pubkey.prototype.toDER = function(compressed) { + if (typeof compressed !== 'boolean') + throw new Error('pubkey: Must specify whether the public key is compressed or not (true or false)'); + + var x = this.p.getX(); + var y = this.p.getY(); + + var xbuf = x.toBuffer({size: 32}); + var ybuf = y.toBuffer({size: 32}); + + if (!compressed) { + var prefix = new Buffer([0x04]); + return Buffer.concat([prefix, xbuf, ybuf]); + } else { + var odd = ybuf[ybuf.length - 1] % 2; + if (odd) + var prefix = new Buffer([0x03]); + else + var prefix = new Buffer([0x02]); + return Buffer.concat([prefix, xbuf]); + } +}; + +Pubkey.prototype.toString = function() { + return this.toDER(true).toString('hex'); +}; + +module.exports = Pubkey; diff --git a/test/test.pubkey.js b/test/test.pubkey.js new file mode 100644 index 0000000..e362a27 --- /dev/null +++ b/test/test.pubkey.js @@ -0,0 +1,96 @@ +var should = require('chai').should(); +var pubkey = require('../lib/pubkey'); +var point = require('../lib/point'); +var bn = require('../lib/bn'); + +describe('pubkey', function() { + + it('should create a blank public key', function() { + var pk = new pubkey(); + should.exist(pk); + }); + + it('should create a public key with a point', function() { + var p = point(); + var pk = new pubkey(p); + should.exist(pk.p); + }); + + describe('#fromDER', function() { + + it('should parse this uncompressed public key', function() { + var pk = new pubkey(); + pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + pk.p.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.p.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + it('should parse this compressed public key', function() { + var pk = new pubkey(); + pk.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + pk.p.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.p.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + it('should throw an error on this invalid public key', function() { + var pk = new pubkey(); + (function() { + pk.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + }).should.throw(); + }); + + }); + + describe('#fromString', function() { + + it('should parse this known valid public key', function() { + pk = new pubkey(); + pk.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pk.p.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.p.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + }); + + describe('#fromX', function() { + + it('should create this known public key', function() { + var x = bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = new pubkey(); + pk.fromX(true, x); + pk.p.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.p.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + }); + + describe('#toDER', function() { + + it('should return this compressed DER format', function() { + var x = bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = new pubkey(); + pk.fromX(true, x); + pk.toDER(true).toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + }); + + it('should return this uncompressed DER format', function() { + var x = bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = new pubkey(); + pk.fromX(true, x); + pk.toDER(false).toString('hex').should.equal('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + }); + + describe('#toString', function() { + + it('should print this known public key', function() { + var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; + var pk = new pubkey(); + pk.fromString(hex); + pk.toString().should.equal(hex); + }); + + }); + +}); From e43c7e3c3a48e7f4bad70f95b6f097211811f5d1 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 7 Aug 2014 16:51:24 -0700 Subject: [PATCH 009/280] random ...code from bitcore --- index.js | 1 + lib/random.js | 54 +++++++++++++++++++++++++++++++++++++++ test/test.random.js | 61 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 lib/random.js create mode 100644 test/test.random.js diff --git a/index.js b/index.js index 4c943e9..0b0eef9 100644 --- a/index.js +++ b/index.js @@ -17,6 +17,7 @@ privsec.hash = require('./lib/hash'); privsec.point = require('./lib/point'); privsec.privkey = require('./lib/privkey'); privsec.pubkey = require('./lib/pubkey'); +privsec.random = require('./lib/random'); //privsec.key = require('lib/key'); //privsec.script = require('lib/script'); diff --git a/lib/random.js b/lib/random.js new file mode 100644 index 0000000..eb34636 --- /dev/null +++ b/lib/random.js @@ -0,0 +1,54 @@ +function Random() { +}; + +/* secure random bytes that sometimes throws an error due to lack of entropy */ +Random.getRandomBuffer = function(size) { + if (typeof process === 'undefined' || process.browser) + return Random.getRandomBufferBrowser(size); + else + return Random.getRandomBufferNode(size); +}; + +Random.getRandomBufferNode = function(size) { + var crypto = require('crypto'); + return crypto.randomBytes(size); +} + +Random.getRandomBufferBrowser = function(size) { + if (!window.crypto && !window.msCrypto) + throw new Error('random: window.crypto not available'); + + if (window.crypto && window.crypto.getRandomValues) + var crypto = window.crypto; + else if (window.msCrypto && window.msCrypto.getRandomValues) //internet explorer + var crypto = window.msCrypto; + else + throw new Error('random: window.crypto.getRandomValues not available'); + + var bbuf = new Uint8Array(size); + crypto.getRandomValues(bbuf); + var buf = new Buffer(bbuf); + + return buf; +}; + +/* insecure random bytes, but it never fails */ +Random.getPseudoRandomBuffer = function(size) { + var b32 = 0x100000000; + var b = new Buffer(size); + + for (var i = 0; i <= size; i++) { + var j = Math.floor(i / 4); + var k = i - j * 4; + if (k == 0) { + r = Math.random() * b32; + b[i] = r & 0xff; + } else { + b[i] = (r = r >>> 8) & 0xff; + } + } + + return b; +}; + +module.exports = Random; diff --git a/test/test.random.js b/test/test.random.js new file mode 100644 index 0000000..5c8c3ec --- /dev/null +++ b/test/test.random.js @@ -0,0 +1,61 @@ +var should = require('chai').should(); +var Random = require('../lib/random'); + +describe('Random', function() { + + describe('getRandomBuffer', function() { + + it('should return a buffer', function() { + var bytes = Random.getRandomBuffer(8); + bytes.length.should.equal(8); + Buffer.isBuffer(bytes).should.equal(true); + }); + + it('should not equate two 256 bit random buffers', function() { + var bytes1 = Random.getRandomBuffer(32); + var bytes2 = Random.getRandomBuffer(32); + bytes1.toString('hex').should.not.equal(bytes2.toString('hex')); + }); + + it('should generate 1000 8 byte buffers in a row that are not equal', function() { + var bufs = []; + for (var i = 0; i < 100; i++) + bufs[i] = Random.getRandomBuffer(8); + for (var i = 0; i < 100; i++) + for (var j = i + 1; j < 100; j++) + bufs[i].toString('hex').should.not.equal(bufs[j].toString('hex')); + }); + + }); + + describe('getPseudoRandomBuffer', function() { + + it('should generate 7 random bytes', function() { + var buf = Random.getPseudoRandomBuffer(7); + buf.length.should.equal(7); + }); + + it('should generate 8 random bytes', function() { + var buf = Random.getPseudoRandomBuffer(8); + buf.length.should.equal(8); + }); + + it('should generate 9 random bytes', function() { + var buf = Random.getPseudoRandomBuffer(9); + buf.length.should.equal(9); + }); + + it('should generate 90 random bytes', function() { + var buf = Random.getPseudoRandomBuffer(90); + buf.length.should.equal(90); + }); + + it('should generate two 8 byte buffers that are not equal', function() { + var buf1 = Random.getPseudoRandomBuffer(8); + var buf2 = Random.getPseudoRandomBuffer(8); + buf1.toString('hex').should.not.equal(buf2.toString('hex')); + }); + + }); + +}); From c00f1ebeead734106413b4736e2f6f4b5610750b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 7 Aug 2014 20:08:50 -0700 Subject: [PATCH 010/280] standardize capitalization in test describes --- test/test.address.js | 2 +- test/test.bn.js | 2 +- test/test.privkey.js | 2 +- test/test.random.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test.address.js b/test/test.address.js index 53b3178..545e264 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -2,7 +2,7 @@ var should = require('chai').should(); var constants = require('../lib/constants'); var Address = require('../lib/address'); -describe('Address', function() { +describe('address', function() { var pubkeyhash = new Buffer('3c3fa3d4adcaf8f52d5b1843975e122548269937', 'hex'); var str = '1Cs8a3b7R5n4G9c8Cgbp9iW8BXbhv3SFt6'; diff --git a/test/test.bn.js b/test/test.bn.js index 1e96362..fd19344 100644 --- a/test/test.bn.js +++ b/test/test.bn.js @@ -3,7 +3,7 @@ var should = chai.should(); var assert = chai.assert; var BN = require('../lib/bn'); -describe('BN', function() { +describe('bn', function() { it('should create a bn', function() { var bn = new BN(50); should.exist(bn); diff --git a/test/test.privkey.js b/test/test.privkey.js index db271f9..8ee56d3 100644 --- a/test/test.privkey.js +++ b/test/test.privkey.js @@ -3,7 +3,7 @@ var base58check = require('../lib/base58check'); var bn = require('../lib/bn'); var should = require('chai').should(); -describe('#privkey', function() { +describe('privkey', function() { var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'; var buf = new Buffer(hex, 'hex'); var enctestnet = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; diff --git a/test/test.random.js b/test/test.random.js index 5c8c3ec..f359c2b 100644 --- a/test/test.random.js +++ b/test/test.random.js @@ -1,7 +1,7 @@ var should = require('chai').should(); var Random = require('../lib/random'); -describe('Random', function() { +describe('random', function() { describe('getRandomBuffer', function() { From 1e4f751633ab8ced21f6f7b063455900ccabd921 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 7 Aug 2014 20:10:56 -0700 Subject: [PATCH 011/280] use hash marks in function names in tests --- test/test.random.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test.random.js b/test/test.random.js index f359c2b..a97708f 100644 --- a/test/test.random.js +++ b/test/test.random.js @@ -3,7 +3,7 @@ var Random = require('../lib/random'); describe('random', function() { - describe('getRandomBuffer', function() { + describe('#getRandomBuffer', function() { it('should return a buffer', function() { var bytes = Random.getRandomBuffer(8); @@ -28,7 +28,7 @@ describe('random', function() { }); - describe('getPseudoRandomBuffer', function() { + describe('#getPseudoRandomBuffer', function() { it('should generate 7 random bytes', function() { var buf = Random.getPseudoRandomBuffer(7); From 383fb03422db99c697eed79119e41dc2e4812394 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 7 Aug 2014 20:32:53 -0700 Subject: [PATCH 012/280] buffer is a dependency --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 0b0eef9..64ce4c1 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ var privsec = module.exports; privsec.deps = {}; privsec.deps.bnjs = require('bn.js'); privsec.deps.bs58 = require('bs58'); +privsec.deps.buffer = Buffer; privsec.deps.elliptic = require('elliptic'); privsec.deps.hashjs = require('hash.js'); privsec.deps.sha512 = require('sha512'); @@ -11,7 +12,6 @@ privsec.address = require('./lib/address'); privsec.base58 = require('./lib/base58'); privsec.base58check = require('./lib/base58check'); privsec.bn = require('./lib/bn'); -privsec.buffer = Buffer; privsec.constants = require('./lib/constants'); privsec.hash = require('./lib/hash'); privsec.point = require('./lib/point'); From 5a79879e63aecaac25030e42ac414ac5a0faee65 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 7 Aug 2014 20:39:20 -0700 Subject: [PATCH 013/280] use process.browser only, which is sufficient --- lib/random.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/random.js b/lib/random.js index eb34636..4947e7c 100644 --- a/lib/random.js +++ b/lib/random.js @@ -3,7 +3,7 @@ function Random() { /* secure random bytes that sometimes throws an error due to lack of entropy */ Random.getRandomBuffer = function(size) { - if (typeof process === 'undefined' || process.browser) + if (process.browser) return Random.getRandomBufferBrowser(size); else return Random.getRandomBufferNode(size); From 333c075ae76c775b613a1237e89df4e4b662db7b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 7 Aug 2014 21:31:36 -0700 Subject: [PATCH 014/280] key --- index.js | 2 +- lib/key.js | 46 ++++++++++++++++++++++ lib/privkey.js | 19 +++++---- test/test.key.js | 100 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 lib/key.js create mode 100644 test/test.key.js diff --git a/index.js b/index.js index 64ce4c1..c245ad6 100644 --- a/index.js +++ b/index.js @@ -14,12 +14,12 @@ privsec.base58check = require('./lib/base58check'); privsec.bn = require('./lib/bn'); privsec.constants = require('./lib/constants'); privsec.hash = require('./lib/hash'); +privsec.key = require('./lib/key'); privsec.point = require('./lib/point'); privsec.privkey = require('./lib/privkey'); privsec.pubkey = require('./lib/pubkey'); privsec.random = require('./lib/random'); -//privsec.key = require('lib/key'); //privsec.script = require('lib/script'); //privsec.scriptexec = require('lib/scriptexec'); //privsec.tx = require('lib/tx'); diff --git a/lib/key.js b/lib/key.js new file mode 100644 index 0000000..8bae38e --- /dev/null +++ b/lib/key.js @@ -0,0 +1,46 @@ +var Privkey = require('./privkey'); +var Pubkey = require('./pubkey'); +var Random = require('./random'); +var bn = require('./bn'); +var point = require('./point'); + +function Key(priv, pub) { + this.priv = priv; + this.pub = pub; +}; + +Key.prototype.fromRandom = function() { + do { + var privbuf = Random.getRandomBuffer(32); + this.priv = new Privkey(bn(privbuf)); + var condition = this.priv.n.lt(point.getN()); + } while (!condition); + this.priv2pub(); +}; + +Key.prototype.fromString = function(str) { + var obj = JSON.parse(str); + if (obj.priv) { + this.priv = new Privkey(); + this.priv.fromString(obj.priv); + } + if (obj.pub) { + this.pub = new Pubkey(); + this.pub.fromString(obj.pub); + } +}; + +Key.prototype.priv2pub = function() { + this.pub = new Pubkey(point.getG().mul(this.priv.n)); +}; + +Key.prototype.toString = function() { + var obj = {}; + if (this.priv) + obj.priv = this.priv.toString(); + if (this.pub) + obj.pub = this.pub.toString(); + return JSON.stringify(obj); +}; + +module.exports = Key; diff --git a/lib/privkey.js b/lib/privkey.js index ecb7493..ed580bf 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -4,11 +4,12 @@ var constants = require('./constants'); var base58check = require('./base58check'); var Privkey = function(n, network, compressed) { - if (typeof n === 'undefined') - return; - this.setNumber(n); - this.setNetwork(network); - this.setCompressed(compressed); + if (typeof n !== 'undefined') + this.setNumber(n); + if (typeof network !== 'undefined') + this.setNetwork(network); + if (typeof compressed !== 'undefined') + this.setCompressed(compressed); }; Privkey.prototype.setNumber = function(n) { @@ -30,12 +31,14 @@ Privkey.prototype.setCompressed = function(compressed) { }; Privkey.prototype.toWIF = function() { - this.setNetwork(this.network); - this.setCompressed(this.compressed); - var network = this.network; var compressed = this.compressed; + if (typeof this.network === 'undefined') + network = 'mainnet'; + if (typeof this.compressed === 'undefined') + compressed = true; + var privbuf = this.n.toBuffer({size: 32}); var buf; if (compressed) diff --git a/test/test.key.js b/test/test.key.js new file mode 100644 index 0000000..a3caf0a --- /dev/null +++ b/test/test.key.js @@ -0,0 +1,100 @@ +var should = require('chai').should(); +var bn = require('../lib/bn'); +var point = require('../lib/point'); +var privkey = require('../lib/privkey'); +var pubkey = require('../lib/pubkey'); +var Key = require('../lib/key'); + +describe('key', function() { + + it('should make a blank key', function() { + var key = new Key(); + should.exist(key); + }); + + it('should make a key with a priv and pub', function() { + var priv = new privkey(); + var pub = new pubkey(); + var key = new Key(priv, pub); + should.exist(key); + should.exist(key.priv); + should.exist(key.pub); + }); + + describe("#fromRandom", function() { + + it('should make a new priv and pub', function() { + var key = new Key(); + key.fromRandom(); + should.exist(key.priv); + should.exist(key.pub); + key.priv.n.gt(bn(0)).should.equal(true); + key.pub.p.getX().gt(bn(0)).should.equal(true); + key.pub.p.getY().gt(bn(0)).should.equal(true); + }); + + }); + + describe("#fromString()", function() { + + it('should recover a key creating with toString', function() { + var key = new Key(); + key.fromRandom(); + var priv = key.priv; + var pub = key.pub; + var str = key.toString(); + key.fromString(str); + should.exist(key.priv); + should.exist(key.pub); + key.priv.toString().should.equal(priv.toString()); + key.pub.toString().should.equal(pub.toString()); + }); + + it('should work with only privkey set', function() { + var key = new Key(); + key.fromRandom(); + key.pub = undefined; + var priv = key.priv; + var str = key.toString(); + key.fromString(str); + should.exist(key.priv); + key.priv.toString().should.equal(priv.toString()); + }); + + it('should work with only pubkey set', function() { + var key = new Key(); + key.fromRandom(); + key.priv = undefined; + var pub = key.pub; + var str = key.toString(); + key.fromString(str); + should.exist(key.pub); + key.pub.toString().should.equal(pub.toString()); + }); + + }); + + describe("#priv2pub", function() { + + it('should convert this known privkey to known pubkey', function() { + var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; + var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; + var key = new Key(); + key.priv = new privkey(bn(new Buffer(privhex, 'hex'))); + key.priv2pub(); + key.pub.toString().should.equal(pubhex); + }); + + }); + + describe("#toString()", function() { + + it('should exist', function() { + var key = new Key(); + key.fromRandom(); + should.exist(key.toString()); + }); + + }); + +}); From 1a0167453260a6bc79c8416deefaa6c917078576 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 14:42:23 -0700 Subject: [PATCH 015/280] signature --- index.js | 1 + lib/signature.js | 127 +++++++++++++++++++++++++++++++++++++++ test/test.signature.js | 132 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 lib/signature.js create mode 100644 test/test.signature.js diff --git a/index.js b/index.js index c245ad6..fb3ab50 100644 --- a/index.js +++ b/index.js @@ -19,6 +19,7 @@ privsec.point = require('./lib/point'); privsec.privkey = require('./lib/privkey'); privsec.pubkey = require('./lib/pubkey'); privsec.random = require('./lib/random'); +privsec.signature = require('./lib/signature'); //privsec.script = require('lib/script'); //privsec.scriptexec = require('lib/scriptexec'); diff --git a/lib/signature.js b/lib/signature.js new file mode 100644 index 0000000..451358e --- /dev/null +++ b/lib/signature.js @@ -0,0 +1,127 @@ +var bn = require('./bn'); + +var Signature = function(r, s) { + this.r = r; + this.s = s; +}; + +Signature.prototype.fromCompressed = function(buf) { + var b1 = buf.slice(0, 1)[0]; + var b2 = buf.slice(1, 33); + var b3 = buf.slice(33, 65); + + if (!(b1 === 0 || b1 === 1 || b1 === 2 || b1 === 3)) + throw new Error('signature: i must be 0, 1, 2, or 3'); + if (b2.length !== 32) + throw new Error('signature: r must be 32 bytes'); + if (b3.length !== 32) + throw new Error('signature: s must be 32 bytes'); + + this.r = bn.fromBuffer(b2); + this.s = bn.fromBuffer(b3); +}; + +Signature.prototype.fromDER = function(buf) { + var obj = Signature.parseDER(buf); + this.r = obj.r; + this.s = obj.s; +}; + +Signature.prototype.fromString = function(str) { + var buf = new Buffer(str, 'hex'); + this.fromDER(buf); +}; + +Signature.parseDER = function(buf) { + if (!Buffer.isBuffer(buf)) + throw new Error('signature: DER formatted signature should be a buffer'); + + var header = buf[0]; + + if (header !== 0x30) + throw new Error('signature: Header byte should be 0x30'); + + var length = buf[1]; + if (length !== buf.slice(2).length) + throw new Error('signature: Length byte should length of what follows'); + + var rheader = buf[2 + 0]; + if (rheader !== 0x02) + throw new Error('signature: Integer byte for r should be 0x02'); + + var rlength = buf[2 + 1]; + var rbuf = buf.slice(2 + 2, 2 + 2 + rlength); + var r = bn.fromBuffer(rbuf); + var rneg = buf[2 + 1 + 1] === 0x00 ? true : false; + if (rlength !== rbuf.length) + throw new Error('signature: Length of r incorrect'); + + var sheader = buf[2 + 2 + rlength + 0]; + if (sheader !== 0x02) + throw new Error('signature: Integer byte for s should be 0x02'); + + var slength = buf[2 + 2 + rlength + 1]; + var sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength); + var s = bn.fromBuffer(sbuf); + var sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00 ? true : false; + if (slength !== sbuf.length) + throw new Error('signature: Length of s incorrect'); + + var sumlength = 2 + 2 + rlength + 2 + slength; + if (length !== sumlength - 2) + throw new Error('signature: Length of signature incorrect'); + + var obj = { + header: header, + length: length, + rheader: rheader, + rlength: rlength, + rneg: rneg, + rbuf: rbuf, + r: r, + sheader: sheader, + slength: slength, + sneg: sneg, + sbuf: sbuf, + s: s + }; + + return obj; +}; + +Signature.prototype.toCompressed = function(i) { + if (!(i === 0 || i === 1 || i ===2 || i ===3)) + throw new Error('signature: i must be equal to 0, 1, 2, or 3'); + + var b1 = new Buffer([i]); + var b2 = this.r.toBuffer({size: 32}); + var b3 = this.s.toBuffer({size: 32}); + return Buffer.concat([b1, b2, b3]); +}; + +Signature.prototype.toDER = function() { + var rnbuf = this.r.toBuffer(); + var snbuf = this.s.toBuffer(); + + var rneg = rnbuf[0] & 0x80 ? true : false; + var sneg = snbuf[0] & 0x80 ? true : false; + + var rbuf = rneg ? Buffer.concat([new Buffer([0x00]), rnbuf]) : rnbuf; + var sbuf = sneg ? Buffer.concat([new Buffer([0x00]), snbuf]) : snbuf; + + var length = 2 + rbuf.length + 2 + sbuf.length; + var rlength = rbuf.length; + var slength = sbuf.length; + var rheader = 0x02; + var sheader = 0x02; + var header = 0x30; + + var der = Buffer.concat([new Buffer([header, length, rheader, rlength]), rbuf, new Buffer([sheader, slength]), sbuf]); + return der; +}; + +Signature.prototype.toString = function() { + var buf = this.toDER(); + return buf.toString('hex'); +}; +module.exports = Signature; diff --git a/test/test.signature.js b/test/test.signature.js new file mode 100644 index 0000000..870dd09 --- /dev/null +++ b/test/test.signature.js @@ -0,0 +1,132 @@ +var bn = require('../lib/bn'); +var should = require('chai').should(); +var Signature = require('../lib/signature'); + +describe('Signature', function() { + + it('should make a blank signature', function() { + var sig = new Signature(); + should.exist(sig); + }); + + describe('#fromCompressed', function() { + + it('should create a signature from a compressed signature', function() { + var blank = new Buffer(32); + blank.fill(0); + var compressed = Buffer.concat([ + new Buffer([0]), + blank, + blank + ]); + var sig = new Signature(); + sig.fromCompressed(compressed); + sig.r.cmp(0).should.equal(0); + sig.s.cmp(0).should.equal(0); + }); + + }); + + describe('#fromDER', function() { + + var buf = new Buffer('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex'); + + it('should parse this DER format signature', function() { + var sig = new Signature(); + sig.fromDER(buf); + sig.r.toBuffer({size: 32}).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277'); + sig.s.toBuffer({size: 32}).toString('hex').should.equal('729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2'); + }); + + }); + + describe('#fromString', function() { + + var buf = new Buffer('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex'); + + it('should parse this DER format signature in hex', function() { + var sig = new Signature(); + sig.fromString(buf.toString('hex')); + sig.r.toBuffer({size: 32}).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277'); + sig.s.toBuffer({size: 32}).toString('hex').should.equal('729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2'); + }); + + }); + + describe('#parseDER', function() { + + it('should parse this signature generated in node', function() { + var sighex = '30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'; + var sig = new Buffer(sighex, 'hex'); + var parsed = Signature.parseDER(sig); + parsed.header.should.equal(0x30) + parsed.length.should.equal(69) + parsed.rlength.should.equal(33); + parsed.rneg.should.equal(true); + parsed.rbuf.toString('hex').should.equal('008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa'); + parsed.r.toString().should.equal('63173831029936981022572627018246571655303050627048489594159321588908385378810'); + parsed.slength.should.equal(32); + parsed.sneg.should.equal(false); + parsed.sbuf.toString('hex').should.equal('0993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); + parsed.s.toString().should.equal('4331694221846364448463828256391194279133231453999942381442030409253074198130'); + }); + + it('should parse this 69 byte signature', function() { + var sighex = '3043021f59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b50302202f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa'; + var sig = new Buffer(sighex, 'hex'); + var parsed = Signature.parseDER(sig); + parsed.header.should.equal(0x30) + parsed.length.should.equal(67) + parsed.rlength.should.equal(31); + parsed.rneg.should.equal(false); + parsed.rbuf.toString('hex').should.equal('59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b503'); + parsed.r.toString().should.equal('158826015856106182499128681792325160381907915189052224498209222621383996675'); + parsed.slength.should.equal(32); + parsed.sneg.should.equal(false); + parsed.sbuf.toString('hex').should.equal('2f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa'); + parsed.s.toString().should.equal('21463938592353267769710297084836796652964571266930856168996063301532842380538'); + }); + + it('should parse this 68 byte signature', function() { + var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51'; + var sig = new Buffer(sighex, 'hex'); + var parsed = Signature.parseDER(sig); + parsed.header.should.equal(0x30) + parsed.length.should.equal(66) + parsed.rlength.should.equal(30); + parsed.rneg.should.equal(false); + parsed.rbuf.toString('hex').should.equal('17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632'); + parsed.r.toString().should.equal('164345250294671732127776123343329699648286106708464198588053542748255794'); + parsed.slength.should.equal(32); + parsed.sneg.should.equal(false); + parsed.sbuf.toString('hex').should.equal('61bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51'); + parsed.s.toString().should.equal('44212963026209759051804639008236126356702363229859210154760104982946304432721'); + }); + + }); + + describe('#toDER', function() { + + it('should convert these known r and s values into a known signature', function() { + var r = bn('63173831029936981022572627018246571655303050627048489594159321588908385378810'); + var s = bn('4331694221846364448463828256391194279133231453999942381442030409253074198130'); + var sig = new Signature(r, s); + var der = sig.toDER(r, s); + der.toString('hex').should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); + }); + + }); + + describe('#toString', function() { + + it('should convert this signature in to hex DER', function() { + var r = bn('63173831029936981022572627018246571655303050627048489594159321588908385378810'); + var s = bn('4331694221846364448463828256391194279133231453999942381442030409253074198130'); + var sig = new Signature(r, s); + var hex = sig.toString(); + hex.should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); + }); + + }); + +}); From c4064cc6e14fdb51935b5f50edc11927f247697d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 17:43:24 -0700 Subject: [PATCH 016/280] ecdsa --- index.js | 1 + lib/ecdsa.js | 92 +++++++++++++++++++++++++++++++++++++++++++++ lib/key.js | 32 ++++++++-------- lib/point.js | 13 +++++++ lib/pubkey.js | 12 ++++++ test/test.ecdsa.js | 41 ++++++++++++++++++++ test/test.key.js | 64 +++++++++++++++---------------- test/test.point.js | 20 ++++++++++ test/test.pubkey.js | 28 ++++++++++++++ 9 files changed, 255 insertions(+), 48 deletions(-) create mode 100644 lib/ecdsa.js create mode 100644 test/test.ecdsa.js diff --git a/index.js b/index.js index fb3ab50..150f5ef 100644 --- a/index.js +++ b/index.js @@ -13,6 +13,7 @@ privsec.base58 = require('./lib/base58'); privsec.base58check = require('./lib/base58check'); privsec.bn = require('./lib/bn'); privsec.constants = require('./lib/constants'); +privsec.ecdsa = require('./lib/ecdsa'); privsec.hash = require('./lib/hash'); privsec.key = require('./lib/key'); privsec.point = require('./lib/point'); diff --git a/lib/ecdsa.js b/lib/ecdsa.js new file mode 100644 index 0000000..4892a22 --- /dev/null +++ b/lib/ecdsa.js @@ -0,0 +1,92 @@ +var bn = require('./bn'); +var point = require('./point'); +var Signature = require('./signature'); +var Privkey = require('./privkey'); +var Pubkey = require('./pubkey'); +var Random = require('./random'); + +var ECDSA = function(hash, key, sig, k) { + this.hash = hash; + this.key = key; + this.sig = sig; + this.k = k; +}; + +ECDSA.prototype.sigError = function() { + if (!Buffer.isBuffer(this.hash) || this.hash.length !== 32) + return 'Invalid hash'; + + try { + this.key.pubkey.validate(); + } catch (e) { + return 'Invalid pubkey: ' + e; + }; + + var r = this.sig.r; + var s = this.sig.s; + if (!(r.gt(0) && r.lt(point.getN())) + || !(s.gt(0) && s.lt(point.getN()))) + return 'r and s not in range'; + + var e = bn.fromBuffer(this.hash); + var n = point.getN(); + var sinv = s.invm(n); + var u1 = sinv.mul(e).mod(n); + var u2 = sinv.mul(r).mod(n); + + var p = point.getG().mulAdd(u1, this.key.pubkey.p, u2); + if (p.isInfinity()) + return 'p is infinity'; + + if (!(p.getX().mod(n).cmp(r) === 0)) + return 'Invalid signature'; + else + return false; +}; + +ECDSA.prototype.randomK = function() { + var N = point.getN(); + var k; + do { + k = bn.fromBuffer(Random.getRandomBuffer(32)); + } while (!(k.lt(N) && k.gt(0))); + this.k = k; + return this; +}; + +ECDSA.prototype.sign = function() { + var hash = this.hash; + var privkey = this.key.privkey; + var k = this.k; + var d = privkey.n; + + if (!hash || !privkey || !k || !d) + throw new Error('ecdsa: invalid parameters'); + + var N = point.getN(); + var G = point.getG(); + var e = bn(hash); + + do { + var Q = G.mul(k); + var r = Q.x.mod(N); + var s = k.invm(N).mul(e.add(d.mul(r))).mod(N); + } while (r.cmp(0) <= 0 || s.cmp(0) <= 0); + + this.sig = new Signature(r, s); + return this.sig; +}; + +ECDSA.prototype.signRandomK = function() { + var k = this.randomK(); + return this.sign(); +}; + +ECDSA.prototype.verify = function() { + if (!this.sigError()) + return true; + else + return false; +}; + +module.exports = ECDSA; diff --git a/lib/key.js b/lib/key.js index 8bae38e..68cad5b 100644 --- a/lib/key.js +++ b/lib/key.js @@ -4,42 +4,42 @@ var Random = require('./random'); var bn = require('./bn'); var point = require('./point'); -function Key(priv, pub) { - this.priv = priv; - this.pub = pub; +function Key(privkey, pubkey) { + this.privkey = privkey; + this.pubkey = pubkey; }; Key.prototype.fromRandom = function() { do { var privbuf = Random.getRandomBuffer(32); - this.priv = new Privkey(bn(privbuf)); - var condition = this.priv.n.lt(point.getN()); + this.privkey = new Privkey(bn(privbuf)); + var condition = this.privkey.n.lt(point.getN()); } while (!condition); - this.priv2pub(); + this.privkey2pubkey(); }; Key.prototype.fromString = function(str) { var obj = JSON.parse(str); if (obj.priv) { - this.priv = new Privkey(); - this.priv.fromString(obj.priv); + this.privkey = new Privkey(); + this.privkey.fromString(obj.priv); } if (obj.pub) { - this.pub = new Pubkey(); - this.pub.fromString(obj.pub); + this.pubkey = new Pubkey(); + this.pubkey.fromString(obj.pub); } }; -Key.prototype.priv2pub = function() { - this.pub = new Pubkey(point.getG().mul(this.priv.n)); +Key.prototype.privkey2pubkey = function() { + this.pubkey = new Pubkey(point.getG().mul(this.privkey.n)); }; Key.prototype.toString = function() { var obj = {}; - if (this.priv) - obj.priv = this.priv.toString(); - if (this.pub) - obj.pub = this.pub.toString(); + if (this.privkey) + obj.priv = this.privkey.toString(); + if (this.pubkey) + obj.pub = this.pubkey.toString(); return JSON.stringify(obj); }; diff --git a/lib/point.js b/lib/point.js index 32e41e6..6a301c5 100644 --- a/lib/point.js +++ b/lib/point.js @@ -29,4 +29,17 @@ Point.prototype.getY = function() { return bn(this._getY().toArray()); }; +//https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf +Point.prototype.validate = function() { + var p2 = Point.fromX(this.getY().isOdd(), this.getX()); + if (!(p2.y.cmp(this.y) === 0)) + throw new Error('point: Invalid y value of public key'); + if (!(this.getX().gt(-1) && this.getX().lt(Point.getN())) + ||!(this.getY().gt(-1) && this.getY().lt(Point.getN()))) + throw new Error('point: Point does not lie on the curve'); + if (!(this.mul(Point.getN()).isInfinity())) + throw new Error('point: Point times N must be infinity'); + return this; +}; + module.exports = Point; diff --git a/lib/pubkey.js b/lib/pubkey.js index 8da4b91..20b4978 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -2,6 +2,8 @@ var point = require('./point'); var bn = require('./bn'); var Pubkey = function(p) { + if (p && !p.getX() && !p.getY()) + throw new Error('pubkey: Invalid point'); this.p = p; }; @@ -64,4 +66,14 @@ Pubkey.prototype.toString = function() { return this.toDER(true).toString('hex'); }; +//https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf +Pubkey.prototype.validate = function() { + if (this.p.isInfinity()) + throw new Error('point: Point cannot be equal to Infinity'); + if (this.p.eq(point(bn(0), bn(0)))) + throw new Error('point: Point cannot be equal to 0, 0'); + this.p.validate(); + return this; +}; + module.exports = Pubkey; diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js new file mode 100644 index 0000000..674b744 --- /dev/null +++ b/test/test.ecdsa.js @@ -0,0 +1,41 @@ +var ECDSA = require('../lib/ecdsa'); +var Hash = require('../lib/hash'); +var Key = require('../lib/key'); +var Privkey = require('../lib/privkey'); +var Pubkey = require('../lib/pubkey'); +var bn = require('../lib/bn'); +var point = require('../lib/point'); +var should = require('chai').should(); + +describe("ecdsa", function() { + + it('should create a blank ecdsa', function() { + var ecdsa = new ECDSA(); + }); + + var ecdsa = new ECDSA(); + ecdsa.hash = Hash.sha256(new Buffer('test data')); + ecdsa.key = new Key(); + ecdsa.key.privkey = new Privkey(bn.fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))); + ecdsa.key.pubkey = new Pubkey(point(bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), + bn.fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))); + + describe('#signRandomK', function() { + + it('should produce a signature', function() { + ecdsa.signRandomK(); + should.exist(ecdsa.sig); + }); + + }); + + describe('#verify', function() { + + it('should verify a signature that was just signed', function() { + ecdsa.signRandomK(); + ecdsa.verify().should.equal(true); + }); + + }); + +}); diff --git a/test/test.key.js b/test/test.key.js index a3caf0a..2ba0382 100644 --- a/test/test.key.js +++ b/test/test.key.js @@ -1,8 +1,8 @@ var should = require('chai').should(); var bn = require('../lib/bn'); var point = require('../lib/point'); -var privkey = require('../lib/privkey'); -var pubkey = require('../lib/pubkey'); +var Privkey = require('../lib/privkey'); +var Pubkey = require('../lib/pubkey'); var Key = require('../lib/key'); describe('key', function() { @@ -13,12 +13,12 @@ describe('key', function() { }); it('should make a key with a priv and pub', function() { - var priv = new privkey(); - var pub = new pubkey(); + var priv = new Privkey(); + var pub = new Pubkey(); var key = new Key(priv, pub); should.exist(key); - should.exist(key.priv); - should.exist(key.pub); + should.exist(key.privkey); + should.exist(key.pubkey); }); describe("#fromRandom", function() { @@ -26,11 +26,11 @@ describe('key', function() { it('should make a new priv and pub', function() { var key = new Key(); key.fromRandom(); - should.exist(key.priv); - should.exist(key.pub); - key.priv.n.gt(bn(0)).should.equal(true); - key.pub.p.getX().gt(bn(0)).should.equal(true); - key.pub.p.getY().gt(bn(0)).should.equal(true); + should.exist(key.privkey); + should.exist(key.pubkey); + key.privkey.n.gt(bn(0)).should.equal(true); + key.pubkey.p.getX().gt(bn(0)).should.equal(true); + key.pubkey.p.getY().gt(bn(0)).should.equal(true); }); }); @@ -40,49 +40,49 @@ describe('key', function() { it('should recover a key creating with toString', function() { var key = new Key(); key.fromRandom(); - var priv = key.priv; - var pub = key.pub; + var priv = key.privkey; + var pub = key.pubkey; var str = key.toString(); key.fromString(str); - should.exist(key.priv); - should.exist(key.pub); - key.priv.toString().should.equal(priv.toString()); - key.pub.toString().should.equal(pub.toString()); + should.exist(key.privkey); + should.exist(key.pubkey); + key.privkey.toString().should.equal(priv.toString()); + key.pubkey.toString().should.equal(pub.toString()); }); - it('should work with only privkey set', function() { + it('should work with only Privkey set', function() { var key = new Key(); key.fromRandom(); - key.pub = undefined; - var priv = key.priv; + key.pubkey = undefined; + var priv = key.privkey; var str = key.toString(); key.fromString(str); - should.exist(key.priv); - key.priv.toString().should.equal(priv.toString()); + should.exist(key.privkey); + key.privkey.toString().should.equal(priv.toString()); }); - it('should work with only pubkey set', function() { + it('should work with only Pubkey set', function() { var key = new Key(); key.fromRandom(); - key.priv = undefined; - var pub = key.pub; + key.privkey = undefined; + var pub = key.pubkey; var str = key.toString(); key.fromString(str); - should.exist(key.pub); - key.pub.toString().should.equal(pub.toString()); + should.exist(key.pubkey); + key.pubkey.toString().should.equal(pub.toString()); }); }); - describe("#priv2pub", function() { + describe("#privkey2pubkey", function() { - it('should convert this known privkey to known pubkey', function() { + it('should convert this known Privkey to known Pubkey', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; var key = new Key(); - key.priv = new privkey(bn(new Buffer(privhex, 'hex'))); - key.priv2pub(); - key.pub.toString().should.equal(pubhex); + key.privkey = new Privkey(bn(new Buffer(privhex, 'hex'))); + key.privkey2pubkey(); + key.pubkey.toString().should.equal(pubhex); }); }); diff --git a/test/test.point.js b/test/test.point.js index 01fbc31..272b399 100644 --- a/test/test.point.js +++ b/test/test.point.js @@ -89,4 +89,24 @@ describe('point', function() { }); + describe('#validate', function() { + + it('should validate this valid point', function() { + var x = bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')); + var y = bn.fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')); + var p = point(x, y); + should.exist(p.validate()); + }); + + it('should invalidate this invalid point', function() { + var x = bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')); + var y = bn.fromBuffer(new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')); + var p = point(x, y); + (function() { + p.validate(); + }).should.throw('point: Invalid y value of public key'); + }); + + }); + }); diff --git a/test/test.pubkey.js b/test/test.pubkey.js index e362a27..8caeead 100644 --- a/test/test.pubkey.js +++ b/test/test.pubkey.js @@ -93,4 +93,32 @@ describe('pubkey', function() { }); + describe('#validate', function() { + + it('should not throw an error if pubkey is valid', function() { + var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; + var pk = new pubkey(); + pk.fromString(hex); + should.exist(pk.validate()); + }); + + it('should not throw an error if pubkey is invalid', function() { + var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a0000000000000000000000000000000000000000000000000000000000000000'; + var pk = new pubkey(); + pk.fromString(hex); + (function() { + pk.validate(); + }).should.throw('point: Invalid y value of public key'); + }); + + it('should not throw an error if pubkey is infinity', function() { + var pk = new pubkey(); + pk.p = point.getG().mul(point.getN()); + (function() { + pk.validate(); + }).should.throw('point: Point cannot be equal to Infinity'); + }); + + }); + }); From 2ed5290a4e34ea6dd1eed4e6efa12e6bb6940fb2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 19:03:59 -0700 Subject: [PATCH 017/280] make "new Point()" work --- lib/point.js | 7 ++++++- test/test.point.js | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/point.js b/lib/point.js index 6a301c5..27b4b1f 100644 --- a/lib/point.js +++ b/lib/point.js @@ -2,9 +2,14 @@ var bn = require('./bn'); var elliptic = require('elliptic'); var ec = elliptic.curves.secp256k1; -var Point = ec.curve.point.bind(ec.curve) +var ecpoint = ec.curve.point.bind(ec.curve) var p = ec.curve.point(); var Curve = Object.getPrototypeOf(ec.curve); + +var Point = function Point(x, y, isRed) { + return ecpoint(x, y, isRed); +}; + Point.prototype = Object.getPrototypeOf(p); Point.fromX = ec.curve.pointFromX.bind(ec.curve); diff --git a/test/test.point.js b/test/test.point.js index 272b399..bf5e078 100644 --- a/test/test.point.js +++ b/test/test.point.js @@ -8,6 +8,11 @@ describe('point', function() { var p = point(); should.exist(p); }); + + it('should create a point when called with "new"', function() { + var p = new point(); + should.exist(p); + }); describe('#getX', function() { From 304210c1321aee4a14989466b3a8752691819008 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 19:42:25 -0700 Subject: [PATCH 018/280] add tests for all ecdsa functions --- lib/ecdsa.js | 47 ++++++++++++++++++------ test/test.ecdsa.js | 90 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 10 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 4892a22..1130f99 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -1,6 +1,7 @@ var bn = require('./bn'); var point = require('./point'); var Signature = require('./signature'); +var Key = require('./key'); var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var Random = require('./random'); @@ -12,6 +13,29 @@ var ECDSA = function(hash, key, sig, k) { this.k = k; }; +ECDSA.prototype.fromString = function(str) { + var obj = JSON.parse(str); + if (obj.hash) + this.hash = new Buffer(obj.hash, 'hex'); + if (obj.key) + this.key = (new Key()).fromString(obj.key); + if (obj.sig) + this.sig = (new Signature()).fromString(obj.sig); + if (obj.k) + this.k = bn(obj.k, 10); + return this; +}; + +ECDSA.prototype.randomK = function() { + var N = point.getN(); + var k; + do { + k = bn.fromBuffer(Random.getRandomBuffer(32)); + } while (!(k.lt(N) && k.gt(0))); + this.k = k; + return this; +}; + ECDSA.prototype.sigError = function() { if (!Buffer.isBuffer(this.hash) || this.hash.length !== 32) return 'Invalid hash'; @@ -44,16 +68,6 @@ ECDSA.prototype.sigError = function() { return false; }; -ECDSA.prototype.randomK = function() { - var N = point.getN(); - var k; - do { - k = bn.fromBuffer(Random.getRandomBuffer(32)); - } while (!(k.lt(N) && k.gt(0))); - this.k = k; - return this; -}; - ECDSA.prototype.sign = function() { var hash = this.hash; var privkey = this.key.privkey; @@ -82,6 +96,19 @@ ECDSA.prototype.signRandomK = function() { return this.sign(); }; +ECDSA.prototype.toString = function() { + var obj = {}; + if (this.hash) + obj.hash = this.hash.toString('hex'); + if (this.key) + obj.key = this.key.toString(); + if (this.sig) + obj.sig = this.sig.toString(); + if (this.k) + obj.k = this.k.toString(); + return JSON.stringify(obj); +}; + ECDSA.prototype.verify = function() { if (!this.sigError()) return true; diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index 674b744..1632f9c 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -3,6 +3,7 @@ var Hash = require('../lib/hash'); var Key = require('../lib/key'); var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); +var Signature = require('../lib/signature'); var bn = require('../lib/bn'); var point = require('../lib/point'); var should = require('chai').should(); @@ -20,6 +21,80 @@ describe("ecdsa", function() { ecdsa.key.pubkey = new Pubkey(point(bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), bn.fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))); + describe('#fromString', function() { + + it('should to a round trip with to string', function() { + var str = ecdsa.toString(); + var ecdsa2 = new ECDSA(); + ecdsa2.fromString(str); + should.exist(ecdsa.hash); + should.exist(ecdsa.key); + }); + + }); + + describe('#randomK', function() { + + it('should generate a new random k when called twice in a row', function() { + ecdsa.randomK(); + var k1 = ecdsa.k; + ecdsa.randomK(); + var k2 = ecdsa.k; + (k1.cmp(k2) === 0).should.equal(false); + }); + + it('should generate a random k that is (almost always) greater than this relatively small number', function() { + ecdsa.randomK(); + var k1 = ecdsa.k; + var k2 = bn(Math.pow(2, 32)).mul(bn(Math.pow(2, 32))).mul(bn(Math.pow(2, 32))); + k2.gt(k1).should.equal(false); + }); + + }); + + describe('#sigError', function() { + + it('should return an error if the hash is invalid', function() { + var ecdsa = new ECDSA(); + ecdsa.sigError().should.equal('Invalid hash'); + }); + + it('should return an error if the pubkey is invalid', function() { + var ecdsa = new ECDSA(); + ecdsa.hash = Hash.sha256(new Buffer('test')); + ecdsa.sigError().should.equal("Invalid pubkey: TypeError: Cannot read property 'pubkey' of undefined"); + }); + + it('should return an error if r, s are invalid', function() { + var ecdsa = new ECDSA(); + ecdsa.hash = Hash.sha256(new Buffer('test')); + ecdsa.pubkey = new Pubkey(); + ecdsa.pubkey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + ecdsa.sig = new Signature(); + ecdsa.sig.r = bn(0); + ecdsa.sig.s = bn(0); + ecdsa.sigError().should.equal("Invalid pubkey: TypeError: Cannot read property 'pubkey' of undefined"); + }); + + it('should return an error if the signature is incorrect', function() { + ecdsa.sig = new Signature(); + ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); + ecdsa.sig.r = ecdsa.sig.r.add(bn(1)); + ecdsa.sigError().should.equal("Invalid signature"); + }); + + }); + + describe('#sign', function() { + + it('should create a valid signature', function() { + ecdsa.randomK(); + ecdsa.sign(); + ecdsa.verify().should.equal(true); + }); + + }); + describe('#signRandomK', function() { it('should produce a signature', function() { @@ -29,9 +104,24 @@ describe("ecdsa", function() { }); + describe('#toString', function() { + + it('should convert this to a string', function() { + var str = ecdsa.toString(); + (typeof str === 'string').should.equal(true); + }); + + }); + describe('#verify', function() { it('should verify a signature that was just signed', function() { + ecdsa.sig = new Signature(); + ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); + ecdsa.verify().should.equal(true); + }); + + it('should verify this known good signature', function() { ecdsa.signRandomK(); ecdsa.verify().should.equal(true); }); From cd6c2b241067721b6362e1f058b6455d2bc01720 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 19:58:48 -0700 Subject: [PATCH 019/280] refactor privkey --- lib/privkey.js | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/lib/privkey.js b/lib/privkey.js index ed580bf..dac93e5 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -4,30 +4,18 @@ var constants = require('./constants'); var base58check = require('./base58check'); var Privkey = function(n, network, compressed) { - if (typeof n !== 'undefined') - this.setNumber(n); - if (typeof network !== 'undefined') - this.setNetwork(network); - if (typeof compressed !== 'undefined') - this.setCompressed(compressed); -}; - -Privkey.prototype.setNumber = function(n) { - if (!n.lt(point.getN())) - throw new Error('privkey: Number must be less than N'); this.n = n; -}; - -Privkey.prototype.setNetwork = function(network) { - if (typeof constants[network] === undefined) - throw new Error('privkey: Must specify the network ("mainnet" or "testnet")'); this.network = network; + this.compressed = compressed; }; -Privkey.prototype.setCompressed = function(compressed) { - if (typeof compressed !== 'boolean') +Privkey.prototype.validate = function() { + if (!this.n.lt(point.getN())) + throw new Error('privkey: Number must be less than N'); + if (typeof constants[this.network] === undefined) + throw new Error('privkey: Must specify the network ("mainnet" or "testnet")'); + if (typeof this.compressed !== 'boolean') throw new Error('privkey: Must specify whether the corresponding public key is compressed or not (true or false)'); - this.compressed = compressed; }; Privkey.prototype.toWIF = function() { From 3871e43dd84d5cfda5d982e7e634bbccdeca710b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 20:26:03 -0700 Subject: [PATCH 020/280] 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"'); }); }); From 5606e083e2ddc777d8421e3bf6c41b74cad64256 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 21:51:26 -0700 Subject: [PATCH 021/280] sha512hmac and sha256hmac --- lib/hash.js | 48 +++++++++++++++++++++++++++++++++++++++++++++++ test/test.hash.js | 32 +++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/lib/hash.js b/lib/hash.js index f02d570..3044074 100644 --- a/lib/hash.js +++ b/lib/hash.js @@ -10,6 +10,8 @@ Hash.sha256 = function(buf) { return new Buffer(hash); }; +Hash.sha256.blocksize = 512; + Hash.sha256sha256 = function(buf) { try { return Hash.sha256(Hash.sha256(buf)); @@ -39,3 +41,49 @@ Hash.sha512 = function(buf) { var hash = sha512(buf); return new Buffer(hash); }; + +Hash.sha512.blocksize = 1024; + +Hash.hmac = function(hashf, data, key) { + if (!Buffer.isBuffer(data) || !Buffer.isBuffer(key)) + throw new Error('hash: data and key must be buffers'); + + //http://en.wikipedia.org/wiki/Hash-based_message_authentication_code + //http://tools.ietf.org/html/rfc4868#section-2 + if (!hashf.blocksize) + throw new Error('hash: Blocksize for hash function unknown'); + + var blocksize = hashf.blocksize/8; + + if (key.length > blocksize) + key = hashf(key); + else if (key < blocksize) { + var fill = new Buffer(blocksize); + fill.fill(0); + key.copy(fill); + key = fill; + } + + var o_key = new Buffer(blocksize); + o_key.fill(0x5c); + + var i_key = new Buffer(blocksize); + i_key.fill(0x36); + + var o_key_pad = new Buffer(blocksize); + var i_key_pad = new Buffer(blocksize); + for (var i = 0; i < blocksize; i++) { + o_key_pad[i] = o_key[i] ^ key[i]; + i_key_pad[i] = i_key[i] ^ key[i]; + } + + return hashf(Buffer.concat([o_key_pad, hashf(Buffer.concat([i_key_pad, data]))])); +}; + +Hash.sha256hmac = function(data, key) { + return Hash.hmac(Hash.sha256, data, key); +}; + +Hash.sha512hmac = function(data, key) { + return Hash.hmac(Hash.sha512, data, key); +}; diff --git a/test/test.hash.js b/test/test.hash.js index eea3517..2e8d010 100644 --- a/test/test.hash.js +++ b/test/test.hash.js @@ -20,6 +20,22 @@ describe('hash', function() { }); + describe('#sha256hmac', function() { + + it('should compute this known empty test vector correctly', function() { + var key = new Buffer(''); + var data = new Buffer(''); + Hash.sha256hmac(data, key).toString('hex').should.equal('b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad'); + }); + + it('should compute this known non-empty test vector correctly', function() { + var key = new Buffer('key'); + var data = new Buffer('The quick brown fox jumps over the lazy dog'); + Hash.sha256hmac(data, key).toString('hex').should.equal('f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8'); + }); + + }); + describe('#sha256sha256', function() { it('should calculate the hash of this buffer correctly', function() { @@ -80,4 +96,20 @@ describe('hash', function() { }); + describe("#sha512hmac", function() { + + it('should calculate this known empty test vector correctly', function() { + var hex = 'b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47'; + Hash.sha512hmac(new Buffer([]), new Buffer([])).toString('hex').should.equal(hex); + }); + + it('should calculate this known non-empty test vector correctly', function() { + var hex = 'c40bd7c15aa493b309c940e08a73ffbd28b2e4cb729eb94480d727e4df577b13cc403a78e6150d83595f3b17c4cc331f12ca5952691de3735a63c1d4c69a2bac'; + var data = new Buffer("test1"); + var key = new Buffer("test2"); + Hash.sha512hmac(data, key).toString('hex').should.equal(hex); + }); + + }); + }); From 9649cc58e9d460f1b10f7b54ce0ca9a174035eee Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 22:19:28 -0700 Subject: [PATCH 022/280] bip32 code from bitcore. original implementation derived from here: https://github.com/sarchar/brainwallet.github.com/blob/bip32/js/bip32.js --- lib/bip32.js | 336 ++++++++++++++++++++++++++++++++++++++++++++ lib/key.js | 1 + lib/pubkey.js | 5 + test/test.bip32.js | 322 ++++++++++++++++++++++++++++++++++++++++++ test/test.pubkey.js | 11 ++ 5 files changed, 675 insertions(+) create mode 100644 lib/bip32.js create mode 100644 test/test.bip32.js diff --git a/lib/bip32.js b/lib/bip32.js new file mode 100644 index 0000000..7ccc560 --- /dev/null +++ b/lib/bip32.js @@ -0,0 +1,336 @@ +var base58 = require('./base58'); +var Hash = require('./hash'); +var Key = require('./key'); +var Pubkey = require('./pubkey'); +var Privkey = require('./privkey'); +var Point = require('./point'); +var Random = require('./random'); +var bn = require('./bn'); +var constants = require('./constants'); + +var BIP32 = function(str) { + if (str === 'testnet' || str === 'mainnet') { + this.version = constants[str].bip32privkey; + this.fromRandom(); + } + else if (str) + this.fromString(str); +} + +BIP32.prototype.fromRandom = function() { + this.depth = 0x00; + this.parentFingerprint = new Buffer([0, 0, 0, 0]); + this.childIndex = new Buffer([0, 0, 0, 0]); + this.chainCode = Random.getRandomBuffer(32); + this.key = (new Key()).fromRandom(); + this.hasPrivateKey = true; + this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); + this.buildExtendedPublicKey(); + this.buildExtendedPrivateKey(); +}; + +BIP32.prototype.fromString = function(str) { + var decoded = base58.decode(str); + if (decoded.length != 82) + throw new Error('Not enough data, expected 82 and received ' + decoded.length); + var checksum = decoded.slice(78, 82); + var bytes = decoded.slice(0, 78); + + var hash = Hash.sha256sha256(bytes); + + if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) + throw new Error('Invalid checksum'); + + if (bytes !== undefined && bytes !== null) + this.initFromBytes(bytes); +}; + +BIP32.prototype.fromSeed = function(bytes, network) { + if (!network) + network = 'mainnet'; + + if (!Buffer.isBuffer(bytes)) + throw new Error('bip32: bytes must be a buffer'); + if (bytes.length < 128 / 8) + throw new Error('bip32: Need more than 128 bytes of entropy'); + if (bytes.length > 512 / 8) + throw new Error('bip32: More than 512 bytes of entropy is nonstandard'); + var hash = Hash.sha512hmac(bytes, new Buffer('Bitcoin seed')); + + this.depth = 0x00; + this.parentFingerprint = new Buffer([0, 0, 0, 0]); + this.childIndex = new Buffer([0, 0, 0, 0]); + this.chainCode = hash.slice(32, 64); + this.version = constants[network].bip32privkey; + this.key = new Key(); + this.key.privkey = new Privkey(bn.fromBuffer(hash.slice(0, 32))); + this.key.privkey2pubkey(); + this.hasPrivateKey = true; + this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); + + this.buildExtendedPublicKey(); + this.buildExtendedPrivateKey(); + + return this; +}; + +BIP32.prototype.initFromBytes = function(bytes) { + // Both pub and private extended keys are 78 bytes + if (bytes.length != 78) + throw new Error('bip32: not enough data'); + + this.version = u32(bytes.slice(0, 4)); + this.depth = u8(bytes.slice(4, 5)); + this.parentFingerprint = bytes.slice(5, 9); + this.childIndex = u32(bytes.slice(9, 13)); + this.chainCode = bytes.slice(13, 45); + + var keyBytes = bytes.slice(45, 78); + + var isPrivate = + (this.version == constants.mainnet.bip32privkey || + this.version == constants.testnet.bip32privkey); + + var isPublic = + (this.version == constants.mainnet.bip32pubkey || + this.version == constants.testnet.bip32pubkey); + + if (isPrivate && keyBytes[0] == 0) { + this.key = new Key(); + this.key.privkey = new Privkey(bn.fromBuffer(keyBytes.slice(1, 33))); + this.key.privkey2pubkey(); + this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); + this.hasPrivateKey = true; + } else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) { + this.key = new Key(); + this.key.pubkey = (new Pubkey()).fromDER(keyBytes); + this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); + this.hasPrivateKey = false; + } else { + throw new Error('Invalid key'); + } + + this.buildExtendedPublicKey(); + this.buildExtendedPrivateKey(); +} + +BIP32.prototype.buildExtendedPublicKey = function() { + this.extendedPublicKey = new Buffer([]); + + var v = null; + switch (this.version) { + case constants.mainnet.bip32pubkey: + case constants.mainnet.bip32privkey: + v = constants.mainnet.bip32pubkey; + break; + case constants.testnet.bip32pubkey: + case constants.testnet.bip32privkey: + v = constants.testnet.bip32pubkey; + break; + default: + throw new Error('bip32: Unknown version'); + } + + // Version + this.extendedPublicKey = Buffer.concat([ + new Buffer([v >> 24]), + new Buffer([(v >> 16) & 0xff]), + new Buffer([(v >> 8) & 0xff]), + new Buffer([v & 0xff]), + new Buffer([this.depth]), + this.parentFingerprint, + new Buffer([this.childIndex >>> 24]), + new Buffer([(this.childIndex >>> 16) & 0xff]), + new Buffer([(this.childIndex >>> 8) & 0xff]), + new Buffer([this.childIndex & 0xff]), + this.chainCode, + this.key.pubkey.toBuffer() + ]); +} + +BIP32.prototype.extendedPublicKeyString = function(format) { + if (format === undefined || format === 'base58') { + if (!Buffer.isBuffer(this.extendedPublicKey)) + console.log('extendedPublicKey: ' + this.extendedPublicKey); + var hash = Hash.sha256sha256(this.extendedPublicKey); + var checksum = hash.slice(0, 4); + var data = Buffer.concat([this.extendedPublicKey, checksum]); + return base58.encode(data); + } else if (format === 'hex') { + return this.extendedPublicKey.toString('hex');; + } else { + throw new Error('bad format'); + } +} + +BIP32.prototype.buildExtendedPrivateKey = function() { + if (!this.hasPrivateKey) return; + this.extendedPrivateKey = new Buffer([]); + + var v = this.version; + + this.extendedPrivateKey = Buffer.concat([ + new Buffer([v >> 24]), + new Buffer([(v >> 16) & 0xff]), + new Buffer([(v >> 8) & 0xff]), + new Buffer([v & 0xff]), + new Buffer([this.depth]), + this.parentFingerprint, + new Buffer([this.childIndex >>> 24]), + new Buffer([(this.childIndex >>> 16) & 0xff]), + new Buffer([(this.childIndex >>> 8) & 0xff]), + new Buffer([this.childIndex & 0xff]), + this.chainCode, + new Buffer([0]), + this.key.privkey.n.toBuffer({size: 32}) + ]); +} + +BIP32.prototype.extendedPrivateKeyString = function(format) { + if (format === undefined || format === 'base58') { + var hash = Hash.sha256sha256(this.extendedPrivateKey); + var checksum = hash.slice(0, 4); + var data = Buffer.concat([this.extendedPrivateKey, checksum]); + return base58.encode(data); + } else if (format === 'hex') { + return this.extendedPrivateKey.toString('hex'); + } else { + throw new Error('bad format'); + } +} + + +BIP32.prototype.derive = function(path) { + var e = path.split('/'); + + // Special cases: + if (path == 'm' || path == 'M' || path == 'm\'' || path == 'M\'') + return this; + + var bip32 = this; + for (var i in e) { + var c = e[i]; + + if (i == 0) { + if (c != 'm') throw new Error('invalid path'); + continue; + } + + var usePrivate = (c.length > 1) && (c[c.length - 1] == '\''); + var childIndex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c) & 0x7fffffff; + + if (usePrivate) + childIndex += 0x80000000; + + bip32 = bip32.deriveChild(childIndex); + } + + return bip32; +} + +BIP32.prototype.deriveChild = function(i) { + var ib = []; + ib.push((i >> 24) & 0xff); + ib.push((i >> 16) & 0xff); + ib.push((i >> 8) & 0xff); + ib.push(i & 0xff); + ib = new Buffer(ib); + + var usePrivate = (i & 0x80000000) != 0; + + var isPrivate = + (this.version == constants.mainnet.bip32privkey || + this.version == constants.testnet.bip32privkey); + + if (usePrivate && (!this.hasPrivateKey || !isPrivate)) + throw new Error('Cannot do private key derivation without private key'); + + var ret = null; + if (this.hasPrivateKey) { + var data = null; + + if (usePrivate) { + data = Buffer.concat([new Buffer([0]), this.key.privkey.n.toBuffer({size: 32}), ib]); + } else { + data = Buffer.concat([this.key.pubkey.toBuffer({size: 32}), ib]); + } + + var hash = Hash.sha512hmac(data, this.chainCode); + var il = bn.fromBuffer(hash.slice(0, 32), {size: 32}); + var ir = hash.slice(32, 64); + + // ki = IL + kpar (mod n). + var k = il.add(this.key.privkey.n).mod(Point.getN()); + + ret = new BIP32(); + ret.chainCode = ir; + + ret.key = new Key(); + ret.key.privkey = new Privkey(k); + ret.key.privkey2pubkey(); + ret.hasPrivateKey = true; + + } else { + var data = Buffer.concat([this.key.pubkey.toBuffer(), ib]); + var hash = Hash.sha512hmac(data, this.chainCode); + var il = bn(hash.slice(0, 32)); + var ir = hash.slice(32, 64); + + // Ki = (IL + kpar)*G = IL*G + Kpar + var ilG = Point.getG().mul(il); + var Kpar = this.key.pubkey.p; + var Ki = ilG.add(Kpar); + var newpub = new Pubkey(); + newpub.p = Ki; + + ret = new BIP32(); + ret.chainCode = ir; + + var key = new Key(); + key.pubkey = newpub; + ret.key = key; + ret.hasPrivateKey = false; + } + + ret.childIndex = i; + ret.parentFingerprint = this.pubKeyHash.slice(0, 4); + ret.version = this.version; + ret.depth = this.depth + 1; + + ret.pubKeyHash = Hash.sha256ripemd160(ret.key.pubkey.toBuffer()); + + ret.buildExtendedPublicKey(); + ret.buildExtendedPrivateKey(); + + return ret; +} + + +function uint(f, size) { + if (f.length < size) + throw new Error('not enough data'); + var n = 0; + for (var i = 0; i < size; i++) { + n *= 256; + n += f[i]; + } + return n; +} + +function u8(f) { + return uint(f, 1); +} + +function u16(f) { + return uint(f, 2); +} + +function u32(f) { + return uint(f, 4); +} + +function u64(f) { + return uint(f, 8); +} + +module.exports = BIP32; diff --git a/lib/key.js b/lib/key.js index 68cad5b..4a897a2 100644 --- a/lib/key.js +++ b/lib/key.js @@ -16,6 +16,7 @@ Key.prototype.fromRandom = function() { var condition = this.privkey.n.lt(point.getN()); } while (!condition); this.privkey2pubkey(); + return this; }; Key.prototype.fromString = function(str) { diff --git a/lib/pubkey.js b/lib/pubkey.js index 20b4978..b00fd72 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -27,6 +27,7 @@ Pubkey.prototype.fromDER = function(buf) { } else { throw new Error('pubkey: Invalid DER format pubkey'); } + return this; }; Pubkey.prototype.fromString = function(str) { @@ -39,6 +40,10 @@ Pubkey.prototype.fromX = function(odd, x) { this.p = point.fromX(odd, x); }; +Pubkey.prototype.toBuffer = function() { + return this.toDER(true); +}; + Pubkey.prototype.toDER = function(compressed) { if (typeof compressed !== 'boolean') throw new Error('pubkey: Must specify whether the public key is compressed or not (true or false)'); diff --git a/test/test.bip32.js b/test/test.bip32.js new file mode 100644 index 0000000..e101870 --- /dev/null +++ b/test/test.bip32.js @@ -0,0 +1,322 @@ +var should = require('chai').should(); +var BIP32 = require('../lib/bip32'); + +describe('BIP32', function() { + + //test vectors: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki + var vector1_master = '000102030405060708090a0b0c0d0e0f'; + var vector1_m_public = 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8' + var vector1_m_private = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'; + var vector1_m0h_public = 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw'; + var vector1_m0h_private = 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7'; + var vector1_m0h1_public = 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ'; + var vector1_m0h1_private = 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs'; + var vector1_m0h12h_public = 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5'; + var vector1_m0h12h_private = 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM'; + var vector1_m0h12h2_public = 'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV'; + var vector1_m0h12h2_private = 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334'; + var vector1_m0h12h21000000000_public = 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy'; + var vector1_m0h12h21000000000_private = 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76'; + var vector2_master = 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542'; + var vector2_m_public = 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB'; + var vector2_m_private = 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U'; + var vector2_m0_public = 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH'; + var vector2_m0_private = 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt'; + var vector2_m02147483647h_public = 'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a'; + var vector2_m02147483647h_private = 'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9'; + var vector2_m02147483647h1_public = 'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon'; + var vector2_m02147483647h1_private = 'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef'; + var vector2_m02147483647h12147483646h_public = 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL'; + var vector2_m02147483647h12147483646h_private = 'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc'; + var vector2_m02147483647h12147483646h2_public = 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt'; + var vector2_m02147483647h12147483646h2_private = 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j'; + + + it('should initialize the class', function() { + should.exist(BIP32); + }); + + it('should create a mainnet bip32', function() { + var bip32 = new BIP32('mainnet'); + should.exist(bip32); + }); + + it('should create a testnet bip32', function() { + var bip32 = new BIP32('testnet'); + should.exist(bip32); + }); + + it('should initialize test vector 1 from the extended public key', function() { + var bip32 = new BIP32(vector1_m_public); + should.exist(bip32); + }); + + it('should initialize test vector 1 from the extended private key', function() { + var bip32 = new BIP32(vector1_m_private); + should.exist(bip32); + }); + + it('should get the extended public key from the extended private key for test vector 1', function() { + var bip32 = new BIP32(vector1_m_private); + bip32.extendedPublicKeyString().should.equal(vector1_m_public); + }); + + it("should get m/0' ext. private key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector1_m0h_private); + }); + + it("should get m/0' ext. public key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector1_m0h_public); + }); + + it("should get m/0'/1 ext. private key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector1_m0h1_private); + }); + + it("should get m/0'/1 ext. public key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector1_m0h1_public); + }); + + it("should get m/0'/1 ext. public key from m/0' public key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'"); + var child_pub = new BIP32(child.extendedPublicKeyString()); + var child2 = child_pub.derive("m/1"); + should.exist(child2); + child2.extendedPublicKeyString().should.equal(vector1_m0h1_public); + }); + + it("should get m/0'/1/2h ext. private key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1/2'"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector1_m0h12h_private); + }); + + it("should get m/0'/1/2h ext. public key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1/2'"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector1_m0h12h_public); + }); + + it("should get m/0'/1/2h/2 ext. private key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1/2'/2"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector1_m0h12h2_private); + }); + + it("should get m/0'/1/2'/2 ext. public key from m/0'/1/2' public key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1/2'"); + var child_pub = new BIP32(child.extendedPublicKeyString()); + var child2 = child_pub.derive("m/2"); + should.exist(child2); + child2.extendedPublicKeyString().should.equal(vector1_m0h12h2_public); + }); + + it("should get m/0'/1/2h/2 ext. public key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1/2'/2"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector1_m0h12h2_public); + }); + + it("should get m/0'/1/2h/2/1000000000 ext. private key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1/2'/2/1000000000"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector1_m0h12h21000000000_private); + }); + + it("should get m/0'/1/2h/2/1000000000 ext. public key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1/2'/2/1000000000"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector1_m0h12h21000000000_public); + }); + + it("should get m/0'/1/2'/2/1000000000 ext. public key from m/0'/1/2'/2 public key from test vector 1", function() { + var bip32 = new BIP32(vector1_m_private); + var child = bip32.derive("m/0'/1/2'/2"); + var child_pub = new BIP32(child.extendedPublicKeyString()); + var child2 = child_pub.derive("m/1000000000"); + should.exist(child2); + child2.extendedPublicKeyString().should.equal(vector1_m0h12h21000000000_public); + }); + + it('should initialize test vector 2 from the extended public key', function() { + var bip32 = new BIP32(vector2_m_public); + should.exist(bip32); + }); + + it('should initialize test vector 2 from the extended private key', function() { + var bip32 = new BIP32(vector2_m_private); + should.exist(bip32); + }); + + it('should get the extended public key from the extended private key for test vector 2', function() { + var bip32 = new BIP32(vector2_m_private); + bip32.extendedPublicKeyString().should.equal(vector2_m_public); + }); + + it("should get m/0 ext. private key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector2_m0_private); + }); + + it("should get m/0 ext. public key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector2_m0_public); + }); + + it("should get m/0 ext. public key from m public key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m"); + var child_pub = new BIP32(child.extendedPublicKeyString()); + var child2 = child_pub.derive("m/0"); + should.exist(child2); + child2.extendedPublicKeyString().should.equal(vector2_m0_public); + }); + + it("should get m/0/2147483647h ext. private key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector2_m02147483647h_private); + }); + + it("should get m/0/2147483647h ext. public key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector2_m02147483647h_public); + }); + + it("should get m/0/2147483647h/1 ext. private key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'/1"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector2_m02147483647h1_private); + }); + + it("should get m/0/2147483647h/1 ext. public key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'/1"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector2_m02147483647h1_public); + }); + + it("should get m/0/2147483647h/1 ext. public key from m/0/2147483647h public key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'"); + var child_pub = new BIP32(child.extendedPublicKeyString()); + var child2 = child_pub.derive("m/1"); + should.exist(child2); + child2.extendedPublicKeyString().should.equal(vector2_m02147483647h1_public); + }); + + it("should get m/0/2147483647h/1/2147483646h ext. private key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'/1/2147483646'"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector2_m02147483647h12147483646h_private); + }); + + it("should get m/0/2147483647h/1/2147483646h ext. public key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'/1/2147483646'"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h_public); + }); + + it("should get m/0/2147483647h/1/2147483646h/2 ext. private key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'/1/2147483646'/2"); + should.exist(child); + child.extendedPrivateKeyString().should.equal(vector2_m02147483647h12147483646h2_private); + }); + + it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'/1/2147483646'/2"); + should.exist(child); + child.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h2_public); + }); + + it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from m/0/2147483647h/2147483646h public key from test vector 2", function() { + var bip32 = new BIP32(vector2_m_private); + var child = bip32.derive("m/0/2147483647'/1/2147483646'"); + var child_pub = new BIP32(child.extendedPublicKeyString()); + var child2 = child_pub.derive("m/2"); + should.exist(child2); + child2.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h2_public); + }); + + describe('#seed', function() { + + it('should initialize a new BIP32 correctly from test vector 1 seed', function() { + var hex = vector1_master; + var bip32 = (new BIP32()).fromSeed(new Buffer(hex, 'hex'), 'mainnet'); + should.exist(bip32); + bip32.extendedPrivateKeyString().should.equal(vector1_m_private); + bip32.extendedPublicKeyString().should.equal(vector1_m_public); + }); + + it('should initialize a new BIP32 correctly from test vector 2 seed', function() { + var hex = vector2_master; + var bip32 = (new BIP32()).fromSeed(new Buffer(hex, 'hex'), 'mainnet'); + should.exist(bip32); + bip32.extendedPrivateKeyString().should.equal(vector2_m_private); + bip32.extendedPublicKeyString().should.equal(vector2_m_public); + }); + }); + + describe('testnet', function() { + it('should initialize a new BIP32 correctly from a random BIP32', function() { + var b1 = new BIP32('testnet'); + var b2 = new BIP32(b1.extendedPublicKeyString()); + b2.extendedPublicKeyString().should.equal(b1.extendedPublicKeyString()); + }); + + it('should generate valid ext pub key for testnet', function() { + var b = new BIP32('testnet'); + b.extendedPublicKeyString().substring(0,4).should.equal('tpub'); + }); + }); + + describe('derivation in linux', function() { + it('should not be non-deterministic', function(){ + var hp = 'm/45\''; + var sp = 'm/45'; + + var hk = new BIP32('tprv8ZgxMBicQKsPdSF1avR6mXyDj5Uv1XY2UyUHSDpAXQ5TvPN7prGeDppjy4562rBB9gMMAhRfFdJrNDpQ4t69kkqHNEEen3PX1zBJqSehJDH'); + //hk.derive(sp).extendedPrivateKeyString().should.equal( + // 'tprv8cSDV3fVD6wqGoLKykTPhRwWLiwD6WBHvYHYkFvp8PJvApm7HCfY9HH9P6Q6iPaCGNsU3LEqh7iJMN7478TqjkLFnf71f9zBXXd7XoiL7dw'); + //hk.derive(sp).extendedPrivateKeyString().should.equal(hk.derive(sp).extendedPrivateKeyString()); + var epk1 = hk.derive(hp).extendedPrivateKeyString(); + var epk2 = hk.derive(hp).extendedPrivateKeyString(); + epk1.should.equal(epk2); + //hk.derive(hp).extendedPrivateKeyString().should.equal( + // 'tprv8cSDV3fdYmUoTNGu4xRTm6qh3DPrNxPZzukM5FPdWoa9m22ALFJVGbjnU7J4TC5t3MJp293GtZWssAPuV1PNWGjXavQTnXy9xW6Lee2X6rd'); + }); + }); + +}); diff --git a/test/test.pubkey.js b/test/test.pubkey.js index 8caeead..0921130 100644 --- a/test/test.pubkey.js +++ b/test/test.pubkey.js @@ -64,6 +64,17 @@ describe('pubkey', function() { }); + describe('#toBuffer', function() { + + it('should return this compressed DER format', function() { + var x = bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = new pubkey(); + pk.fromX(true, x); + pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + }); + + }); + describe('#toDER', function() { it('should return this compressed DER format', function() { From bd3a2c42ec3820b573abb0937d6623910dce7b1f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 22:47:32 -0700 Subject: [PATCH 023/280] works in browser compiled with ./browser/build and then open test/index.html --- .gitignore | 2 ++ browser/build | 4 ++++ package.json | 3 ++- test/index.html | 18 ++++++++++++++++++ test/test.ecdsa.js | 10 ++++++---- 5 files changed, 32 insertions(+), 5 deletions(-) create mode 100755 browser/build create mode 100644 test/index.html diff --git a/.gitignore b/.gitignore index 2716af2..d3c99a0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.swp coverage node_modules +browser/privsec.js +browser/tests.js diff --git a/browser/build b/browser/build new file mode 100755 index 0000000..136320a --- /dev/null +++ b/browser/build @@ -0,0 +1,4 @@ +#!/bin/bash + +browserify index.js -o browser/privsec.js +ls test/test.* | xargs browserify -o browser/tests.js diff --git a/package.json b/package.json index 7d56d18..3399445 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ }, "devDependencies": { "chai": "~1.9.1", - "mocha": "~1.21.0" + "mocha": "~1.21.0", + "browserify": "~5.9.1" }, "author": "Ryan X. Charles ", "license": "MIT" diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..54742fd --- /dev/null +++ b/test/index.html @@ -0,0 +1,18 @@ + + + + Mocha + + + + + +
+ + + + + + diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index 1632f9c..915acf7 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -62,18 +62,20 @@ describe("ecdsa", function() { it('should return an error if the pubkey is invalid', function() { var ecdsa = new ECDSA(); ecdsa.hash = Hash.sha256(new Buffer('test')); - ecdsa.sigError().should.equal("Invalid pubkey: TypeError: Cannot read property 'pubkey' of undefined"); + ecdsa.sigError().indexOf("Invalid pubkey").should.equal(0); }); it('should return an error if r, s are invalid', function() { var ecdsa = new ECDSA(); ecdsa.hash = Hash.sha256(new Buffer('test')); - ecdsa.pubkey = new Pubkey(); - ecdsa.pubkey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + var pk = new Pubkey(); + pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + ecdsa.key = new Key(); + ecdsa.key.pubkey = pk; ecdsa.sig = new Signature(); ecdsa.sig.r = bn(0); ecdsa.sig.s = bn(0); - ecdsa.sigError().should.equal("Invalid pubkey: TypeError: Cannot read property 'pubkey' of undefined"); + ecdsa.sigError().should.equal("r and s not in range"); }); it('should return an error if the signature is incorrect', function() { From e743e265df9c61cc3892698546ae9c40d71ba2b3 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 23:01:59 -0700 Subject: [PATCH 024/280] add README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..f7f95a4 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +privsec +======= + +Bitcoin privacy and security features implemented in pure javascript. From 8228d8204fda970ffda1ae5110aae7dbb920a5a1 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 23:02:50 -0700 Subject: [PATCH 025/280] add LICENSE --- LICENSE.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3e2c98c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +This software is licensed under the MIT License. + +Copyright Ryan X. Charles, 2014. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From be37f5e37f789ea0807db4b4e539d765c89c2c4d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 23:16:15 -0700 Subject: [PATCH 026/280] improve bip32 interface slightly --- lib/bip32.js | 5 ++++- test/test.bip32.js | 15 ++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 7ccc560..4891417 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -17,7 +17,10 @@ var BIP32 = function(str) { this.fromString(str); } -BIP32.prototype.fromRandom = function() { +BIP32.prototype.fromRandom = function(network) { + if (!network) + network = 'mainnet'; + this.version = constants[network].bip32privkey; this.depth = 0x00; this.parentFingerprint = new Buffer([0, 0, 0, 0]); this.childIndex = new Buffer([0, 0, 0, 0]); diff --git a/test/test.bip32.js b/test/test.bip32.js index e101870..a0d32fb 100644 --- a/test/test.bip32.js +++ b/test/test.bip32.js @@ -36,13 +36,8 @@ describe('BIP32', function() { should.exist(BIP32); }); - it('should create a mainnet bip32', function() { - var bip32 = new BIP32('mainnet'); - should.exist(bip32); - }); - - it('should create a testnet bip32', function() { - var bip32 = new BIP32('testnet'); + it('should create a bip32', function() { + var bip32 = new BIP32(); should.exist(bip32); }); @@ -291,13 +286,15 @@ describe('BIP32', function() { describe('testnet', function() { it('should initialize a new BIP32 correctly from a random BIP32', function() { - var b1 = new BIP32('testnet'); + var b1 = new BIP32(); + b1.fromRandom('testnet'); var b2 = new BIP32(b1.extendedPublicKeyString()); b2.extendedPublicKeyString().should.equal(b1.extendedPublicKeyString()); }); it('should generate valid ext pub key for testnet', function() { - var b = new BIP32('testnet'); + var b = new BIP32(); + b.fromRandom('testnet'); b.extendedPublicKeyString().substring(0,4).should.equal('tpub'); }); }); From 43f288d205e1811d351e064fa37b7d9a0b7a91b5 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 23:19:24 -0700 Subject: [PATCH 027/280] remove obsolete test this test was relevant for a bug in the C++ code of bitcore, eckey.cc, which no longer exists in bitcore, much less privsec. --- test/test.bip32.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/test/test.bip32.js b/test/test.bip32.js index a0d32fb..6f08d8e 100644 --- a/test/test.bip32.js +++ b/test/test.bip32.js @@ -299,21 +299,4 @@ describe('BIP32', function() { }); }); - describe('derivation in linux', function() { - it('should not be non-deterministic', function(){ - var hp = 'm/45\''; - var sp = 'm/45'; - - var hk = new BIP32('tprv8ZgxMBicQKsPdSF1avR6mXyDj5Uv1XY2UyUHSDpAXQ5TvPN7prGeDppjy4562rBB9gMMAhRfFdJrNDpQ4t69kkqHNEEen3PX1zBJqSehJDH'); - //hk.derive(sp).extendedPrivateKeyString().should.equal( - // 'tprv8cSDV3fVD6wqGoLKykTPhRwWLiwD6WBHvYHYkFvp8PJvApm7HCfY9HH9P6Q6iPaCGNsU3LEqh7iJMN7478TqjkLFnf71f9zBXXd7XoiL7dw'); - //hk.derive(sp).extendedPrivateKeyString().should.equal(hk.derive(sp).extendedPrivateKeyString()); - var epk1 = hk.derive(hp).extendedPrivateKeyString(); - var epk2 = hk.derive(hp).extendedPrivateKeyString(); - epk1.should.equal(epk2); - //hk.derive(hp).extendedPrivateKeyString().should.equal( - // 'tprv8cSDV3fdYmUoTNGu4xRTm6qh3DPrNxPZzukM5FPdWoa9m22ALFJVGbjnU7J4TC5t3MJp293GtZWssAPuV1PNWGjXavQTnXy9xW6Lee2X6rd'); - }); - }); - }); From 7def2609ec697b69cb8b0bad6e064e749fae630c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 23:29:05 -0700 Subject: [PATCH 028/280] add toString method for bip32 ...so that it is consistent with the rest of the library --- lib/bip32.js | 10 ++++++++++ test/test.bip32.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lib/bip32.js b/lib/bip32.js index 4891417..e552269 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -308,6 +308,16 @@ BIP32.prototype.deriveChild = function(i) { return ret; } +BIP32.prototype.toString = function() { + var isPrivate = + (this.version == constants.mainnet.bip32privkey || + this.version == constants.testnet.bip32privkey); + + if (isPrivate) + return this.extendedPrivateKeyString(); + else + return this.extendedPublicKeyString(); +}; function uint(f, size) { if (f.length < size) diff --git a/test/test.bip32.js b/test/test.bip32.js index 6f08d8e..971318f 100644 --- a/test/test.bip32.js +++ b/test/test.bip32.js @@ -1,4 +1,5 @@ var should = require('chai').should(); +var constants = require('../lib/constants'); var BIP32 = require('../lib/bip32'); describe('BIP32', function() { @@ -299,4 +300,31 @@ describe('BIP32', function() { }); }); + describe('#toString', function() { + var bip32 = new BIP32(); + bip32.fromRandom('mainnet'); + var tip32 = new BIP32(); + tip32.fromRandom('testnet'); + + it('should return an xprv string', function() { + bip32.toString().slice(0, 4).should.equal('xprv'); + }); + + it('should return an xpub string', function() { + var bip32b = new BIP32(bip32.extendedPublicKeyString()); + bip32b.toString().slice(0, 4).should.equal('xpub'); + }); + + it('should return a tprv string', function() { + tip32.toString().slice(0, 4).should.equal('tprv'); + }); + + it('should return a tpub string', function() { + var tip32b = new BIP32(tip32.extendedPublicKeyString()); + tip32b.toString().slice(0, 4).should.equal('tpub'); + }); + + + }); + }); From 134952d1f8033969a5dc431cc5bc03102b04e772 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 23:43:51 -0700 Subject: [PATCH 029/280] add bip32 to main privsec interface --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 150f5ef..d0cc2f5 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,7 @@ privsec.deps.sha512 = require('sha512'); privsec.address = require('./lib/address'); privsec.base58 = require('./lib/base58'); privsec.base58check = require('./lib/base58check'); +privsec.bip32 = require('./lib/bip32'); privsec.bn = require('./lib/bn'); privsec.constants = require('./lib/constants'); privsec.ecdsa = require('./lib/ecdsa'); @@ -27,5 +28,4 @@ privsec.signature = require('./lib/signature'); //privsec.tx = require('lib/tx'); //privsec.txpartial = require('lib/txpartial'); -//privsec.bip32 = require('lib/bip32'); //privsec.bip70 = require('lib/bip70'); From efbebb3528b4362ad38261991bd57f9ac403b9a6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 23:52:19 -0700 Subject: [PATCH 030/280] throw error if deriving an invalid path string ...the path consists "m", numbers, /, and ' characters --- lib/bip32.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/bip32.js b/lib/bip32.js index e552269..eeb7a15 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -215,10 +215,13 @@ BIP32.prototype.derive = function(path) { var c = e[i]; if (i == 0) { - if (c != 'm') throw new Error('invalid path'); + if (c != 'm') throw new Error('bip32: invalid path'); continue; } + if (parseInt(c.replace("'", "")).toString() !== c.replace("'", "")) + throw new Error('bip32: invalid path'); + var usePrivate = (c.length > 1) && (c[c.length - 1] == '\''); var childIndex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c) & 0x7fffffff; From cc310fbc2356590d367a308bf73e78ee3cef184d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 9 Aug 2014 23:54:08 -0700 Subject: [PATCH 031/280] add bip32: to error messages ...to be consistent with the rest of the library --- lib/bip32.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index eeb7a15..2ad3af9 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -35,14 +35,14 @@ BIP32.prototype.fromRandom = function(network) { BIP32.prototype.fromString = function(str) { var decoded = base58.decode(str); if (decoded.length != 82) - throw new Error('Not enough data, expected 82 and received ' + decoded.length); + throw new Error('bip32: Not enough data, expected 82 and received ' + decoded.length); var checksum = decoded.slice(78, 82); var bytes = decoded.slice(0, 78); var hash = Hash.sha256sha256(bytes); if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) - throw new Error('Invalid checksum'); + throw new Error('bip32: Invalid checksum'); if (bytes !== undefined && bytes !== null) this.initFromBytes(bytes); @@ -110,7 +110,7 @@ BIP32.prototype.initFromBytes = function(bytes) { this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); this.hasPrivateKey = false; } else { - throw new Error('Invalid key'); + throw new Error('bip32: Invalid key'); } this.buildExtendedPublicKey(); @@ -162,7 +162,7 @@ BIP32.prototype.extendedPublicKeyString = function(format) { } else if (format === 'hex') { return this.extendedPublicKey.toString('hex');; } else { - throw new Error('bad format'); + throw new Error('bip32: bad format'); } } @@ -198,7 +198,7 @@ BIP32.prototype.extendedPrivateKeyString = function(format) { } else if (format === 'hex') { return this.extendedPrivateKey.toString('hex'); } else { - throw new Error('bad format'); + throw new Error('bip32: bad format'); } } @@ -235,6 +235,9 @@ BIP32.prototype.derive = function(path) { } BIP32.prototype.deriveChild = function(i) { + if (typeof i !== 'number') + throw new Error('bip32: i must be a number'); + var ib = []; ib.push((i >> 24) & 0xff); ib.push((i >> 16) & 0xff); @@ -249,7 +252,7 @@ BIP32.prototype.deriveChild = function(i) { this.version == constants.testnet.bip32privkey); if (usePrivate && (!this.hasPrivateKey || !isPrivate)) - throw new Error('Cannot do private key derivation without private key'); + throw new Error('bip32: Cannot do private key derivation without private key'); var ret = null; if (this.hasPrivateKey) { @@ -324,7 +327,7 @@ BIP32.prototype.toString = function() { function uint(f, size) { if (f.length < size) - throw new Error('not enough data'); + throw new Error('bip32: not enough data'); var n = 0; for (var i = 0; i < size; i++) { n *= 256; From 1fa7fb527ed99666ca0a1c7321e07f9043e47d4e Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 11 Aug 2014 15:44:19 -0400 Subject: [PATCH 032/280] move core code to top --- index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index d0cc2f5..a8f495e 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,5 @@ var privsec = module.exports; -privsec.deps = {}; -privsec.deps.bnjs = require('bn.js'); -privsec.deps.bs58 = require('bs58'); -privsec.deps.buffer = Buffer; -privsec.deps.elliptic = require('elliptic'); -privsec.deps.hashjs = require('hash.js'); -privsec.deps.sha512 = require('sha512'); - privsec.address = require('./lib/address'); privsec.base58 = require('./lib/base58'); privsec.base58check = require('./lib/base58check'); @@ -23,6 +15,14 @@ privsec.pubkey = require('./lib/pubkey'); privsec.random = require('./lib/random'); privsec.signature = require('./lib/signature'); +privsec.deps = {}; +privsec.deps.bnjs = require('bn.js'); +privsec.deps.bs58 = require('bs58'); +privsec.deps.buffer = Buffer; +privsec.deps.elliptic = require('elliptic'); +privsec.deps.hashjs = require('hash.js'); +privsec.deps.sha512 = require('sha512'); + //privsec.script = require('lib/script'); //privsec.scriptexec = require('lib/scriptexec'); //privsec.tx = require('lib/tx'); From a2e471ae9e0b2d3721a7e1778dc6bfcabed2ba88 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 13 Aug 2014 15:23:06 -0400 Subject: [PATCH 033/280] more consistency: n -> bn, p -> point --- lib/bip32.js | 10 +++---- lib/ecdsa.js | 4 +-- lib/key.js | 20 +++++++------- lib/privkey.js | 16 +++++------ lib/pubkey.js | 22 +++++++-------- test/test.key.js | 6 ++--- test/test.privkey.js | 8 +++--- test/test.pubkey.js | 64 ++++++++++++++++++++++---------------------- 8 files changed, 75 insertions(+), 75 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 2ad3af9..0df8129 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -185,7 +185,7 @@ BIP32.prototype.buildExtendedPrivateKey = function() { new Buffer([this.childIndex & 0xff]), this.chainCode, new Buffer([0]), - this.key.privkey.n.toBuffer({size: 32}) + this.key.privkey.bn.toBuffer({size: 32}) ]); } @@ -259,7 +259,7 @@ BIP32.prototype.deriveChild = function(i) { var data = null; if (usePrivate) { - data = Buffer.concat([new Buffer([0]), this.key.privkey.n.toBuffer({size: 32}), ib]); + data = Buffer.concat([new Buffer([0]), this.key.privkey.bn.toBuffer({size: 32}), ib]); } else { data = Buffer.concat([this.key.pubkey.toBuffer({size: 32}), ib]); } @@ -269,7 +269,7 @@ BIP32.prototype.deriveChild = function(i) { var ir = hash.slice(32, 64); // ki = IL + kpar (mod n). - var k = il.add(this.key.privkey.n).mod(Point.getN()); + var k = il.add(this.key.privkey.bn).mod(Point.getN()); ret = new BIP32(); ret.chainCode = ir; @@ -287,10 +287,10 @@ BIP32.prototype.deriveChild = function(i) { // Ki = (IL + kpar)*G = IL*G + Kpar var ilG = Point.getG().mul(il); - var Kpar = this.key.pubkey.p; + var Kpar = this.key.pubkey.point; var Ki = ilG.add(Kpar); var newpub = new Pubkey(); - newpub.p = Ki; + newpub.point = Ki; ret = new BIP32(); ret.chainCode = ir; diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 1130f99..76cd580 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -58,7 +58,7 @@ ECDSA.prototype.sigError = function() { var u1 = sinv.mul(e).mod(n); var u2 = sinv.mul(r).mod(n); - var p = point.getG().mulAdd(u1, this.key.pubkey.p, u2); + var p = point.getG().mulAdd(u1, this.key.pubkey.point, u2); if (p.isInfinity()) return 'p is infinity'; @@ -72,7 +72,7 @@ ECDSA.prototype.sign = function() { var hash = this.hash; var privkey = this.key.privkey; var k = this.k; - var d = privkey.n; + var d = privkey.bn; if (!hash || !privkey || !k || !d) throw new Error('ecdsa: invalid parameters'); diff --git a/lib/key.js b/lib/key.js index 4a897a2..11e11b2 100644 --- a/lib/key.js +++ b/lib/key.js @@ -1,7 +1,7 @@ var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var Random = require('./random'); -var bn = require('./bn'); +var Bn = require('./bn'); var point = require('./point'); function Key(privkey, pubkey) { @@ -12,8 +12,8 @@ function Key(privkey, pubkey) { Key.prototype.fromRandom = function() { do { var privbuf = Random.getRandomBuffer(32); - this.privkey = new Privkey(bn(privbuf)); - var condition = this.privkey.n.lt(point.getN()); + this.privkey = new Privkey(Bn(privbuf)); + var condition = this.privkey.bn.lt(point.getN()); } while (!condition); this.privkey2pubkey(); return this; @@ -21,26 +21,26 @@ Key.prototype.fromRandom = function() { Key.prototype.fromString = function(str) { var obj = JSON.parse(str); - if (obj.priv) { + if (obj.privkey) { this.privkey = new Privkey(); - this.privkey.fromString(obj.priv); + this.privkey.fromString(obj.privkey); } - if (obj.pub) { + if (obj.pubkey) { this.pubkey = new Pubkey(); - this.pubkey.fromString(obj.pub); + this.pubkey.fromString(obj.pubkey); } }; Key.prototype.privkey2pubkey = function() { - this.pubkey = new Pubkey(point.getG().mul(this.privkey.n)); + this.pubkey = new Pubkey(point.getG().mul(this.privkey.bn)); }; Key.prototype.toString = function() { var obj = {}; if (this.privkey) - obj.priv = this.privkey.toString(); + obj.privkey = this.privkey.toString(); if (this.pubkey) - obj.pub = this.pubkey.toString(); + obj.pubkey = this.pubkey.toString(); return JSON.stringify(obj); }; diff --git a/lib/privkey.js b/lib/privkey.js index dac93e5..7aa09cd 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -1,16 +1,16 @@ -var bn = require('./bn'); +var Bn = require('./bn'); var point = require('./point'); var constants = require('./constants'); var base58check = require('./base58check'); -var Privkey = function(n, network, compressed) { - this.n = n; +var Privkey = function(bn, network, compressed) { + this.bn = bn; this.network = network; this.compressed = compressed; }; Privkey.prototype.validate = function() { - if (!this.n.lt(point.getN())) + if (!this.bn.lt(point.getN())) throw new Error('privkey: Number must be less than N'); if (typeof constants[this.network] === undefined) throw new Error('privkey: Must specify the network ("mainnet" or "testnet")'); @@ -27,12 +27,12 @@ Privkey.prototype.toWIF = function() { if (typeof this.compressed === 'undefined') compressed = true; - var privbuf = this.n.toBuffer({size: 32}); + var privbuf = this.bn.toBuffer({size: 32}); var buf; if (compressed) - buf = Buffer.concat([new Buffer([constants[network].privkey]), this.n.toBuffer({size: 32}), new Buffer([0x01])]); + buf = Buffer.concat([new Buffer([constants[network].privkey]), this.bn.toBuffer({size: 32}), new Buffer([0x01])]); else - buf = Buffer.concat([new Buffer([constants[network].privkey]), this.n.toBuffer({size: 32})]); + buf = Buffer.concat([new Buffer([constants[network].privkey]), this.bn.toBuffer({size: 32})]); return base58check.encode(buf); }; @@ -54,7 +54,7 @@ Privkey.prototype.fromWIF = function(str) { else throw new Error('privkey: Invalid network'); - this.n = bn.fromBuffer(buf.slice(1, 32 + 1)); + this.bn = Bn.fromBuffer(buf.slice(1, 32 + 1)); }; Privkey.prototype.toString = function() { diff --git a/lib/pubkey.js b/lib/pubkey.js index b00fd72..ded1060 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -1,10 +1,10 @@ -var point = require('./point'); +var Point = require('./point'); var bn = require('./bn'); -var Pubkey = function(p) { - if (p && !p.getX() && !p.getY()) +var Pubkey = function(point) { + if (point && !point.getX() && !point.getY()) throw new Error('pubkey: Invalid point'); - this.p = p; + this.point = point; }; Pubkey.prototype.fromDER = function(buf) { @@ -15,7 +15,7 @@ Pubkey.prototype.fromDER = function(buf) { throw new Error('pubkey: Length of x and y must be 32 bytes'); var x = bn(xbuf); var y = bn(ybuf); - this.p = point(x, y); + this.point = Point(x, y); } else if (buf[0] == 0x03) { var xbuf = buf.slice(1); var x = bn(xbuf); @@ -37,7 +37,7 @@ Pubkey.prototype.fromString = function(str) { Pubkey.prototype.fromX = function(odd, x) { if (typeof odd !== 'boolean') throw new Error('pubkey: Must specify whether y is odd or not (true or false)'); - this.p = point.fromX(odd, x); + this.point = Point.fromX(odd, x); }; Pubkey.prototype.toBuffer = function() { @@ -48,8 +48,8 @@ Pubkey.prototype.toDER = function(compressed) { if (typeof compressed !== 'boolean') throw new Error('pubkey: Must specify whether the public key is compressed or not (true or false)'); - var x = this.p.getX(); - var y = this.p.getY(); + var x = this.point.getX(); + var y = this.point.getY(); var xbuf = x.toBuffer({size: 32}); var ybuf = y.toBuffer({size: 32}); @@ -73,11 +73,11 @@ Pubkey.prototype.toString = function() { //https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf Pubkey.prototype.validate = function() { - if (this.p.isInfinity()) + if (this.point.isInfinity()) throw new Error('point: Point cannot be equal to Infinity'); - if (this.p.eq(point(bn(0), bn(0)))) + if (this.point.eq(Point(bn(0), bn(0)))) throw new Error('point: Point cannot be equal to 0, 0'); - this.p.validate(); + this.point.validate(); return this; }; diff --git a/test/test.key.js b/test/test.key.js index 2ba0382..37a4559 100644 --- a/test/test.key.js +++ b/test/test.key.js @@ -28,9 +28,9 @@ describe('key', function() { key.fromRandom(); should.exist(key.privkey); should.exist(key.pubkey); - key.privkey.n.gt(bn(0)).should.equal(true); - key.pubkey.p.getX().gt(bn(0)).should.equal(true); - key.pubkey.p.getY().gt(bn(0)).should.equal(true); + key.privkey.bn.gt(bn(0)).should.equal(true); + key.pubkey.point.getX().gt(bn(0)).should.equal(true); + key.pubkey.point.getY().gt(bn(0)).should.equal(true); }); }); diff --git a/test/test.privkey.js b/test/test.privkey.js index 8ee56d3..2ab2e86 100644 --- a/test/test.privkey.js +++ b/test/test.privkey.js @@ -1,6 +1,6 @@ var Privkey = require('../lib/privkey'); var base58check = require('../lib/base58check'); -var bn = require('../lib/bn'); +var Bn = require('../lib/bn'); var should = require('chai').should(); describe('privkey', function() { @@ -17,17 +17,17 @@ describe('privkey', function() { }); it('should create a mainnet private key', function() { - var privkey = new Privkey(bn.fromBuffer(buf), 'mainnet', true); + var privkey = new Privkey(Bn.fromBuffer(buf), 'mainnet', true); privkey.toString().should.equal(encmainnet); }); it('should create an uncompressed testnet private key', function() { - var privkey = new Privkey(bn.fromBuffer(buf), 'testnet', false); + var privkey = new Privkey(Bn.fromBuffer(buf), 'testnet', false); privkey.toString().should.equal(enctu); }); it('should create an uncompressed mainnet private key', function() { - var privkey = new Privkey(bn.fromBuffer(buf), 'mainnet', false); + var privkey = new Privkey(Bn.fromBuffer(buf), 'mainnet', false); privkey.toString().should.equal(encmu); }); diff --git a/test/test.pubkey.js b/test/test.pubkey.js index 0921130..9eeb7df 100644 --- a/test/test.pubkey.js +++ b/test/test.pubkey.js @@ -1,39 +1,39 @@ var should = require('chai').should(); -var pubkey = require('../lib/pubkey'); -var point = require('../lib/point'); -var bn = require('../lib/bn'); +var Pubkey = require('../lib/pubkey'); +var Point = require('../lib/point'); +var Bn = require('../lib/bn'); describe('pubkey', function() { it('should create a blank public key', function() { - var pk = new pubkey(); + var pk = new Pubkey(); should.exist(pk); }); it('should create a public key with a point', function() { - var p = point(); - var pk = new pubkey(p); - should.exist(pk.p); + var p = Point(); + var pk = new Pubkey(p); + should.exist(pk.point); }); describe('#fromDER', function() { it('should parse this uncompressed public key', function() { - var pk = new pubkey(); + var pk = new Pubkey(); pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); - pk.p.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.p.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); it('should parse this compressed public key', function() { - var pk = new pubkey(); + var pk = new Pubkey(); pk.fromDER(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - pk.p.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.p.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); it('should throw an error on this invalid public key', function() { - var pk = new pubkey(); + var pk = new Pubkey(); (function() { pk.fromDER(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); }).should.throw(); @@ -44,10 +44,10 @@ describe('pubkey', function() { describe('#fromString', function() { it('should parse this known valid public key', function() { - pk = new pubkey(); + pk = new Pubkey(); pk.fromString('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); - pk.p.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.p.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); }); @@ -55,11 +55,11 @@ describe('pubkey', function() { describe('#fromX', function() { it('should create this known public key', function() { - var x = bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new pubkey(); + var x = Bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = new Pubkey(); pk.fromX(true, x); - pk.p.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); - pk.p.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); }); @@ -67,8 +67,8 @@ describe('pubkey', function() { describe('#toBuffer', function() { it('should return this compressed DER format', function() { - var x = bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new pubkey(); + var x = Bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = new Pubkey(); pk.fromX(true, x); pk.toBuffer().toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); }); @@ -78,15 +78,15 @@ describe('pubkey', function() { describe('#toDER', function() { it('should return this compressed DER format', function() { - var x = bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new pubkey(); + var x = Bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = new Pubkey(); pk.fromX(true, x); pk.toDER(true).toString('hex').should.equal('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); }); it('should return this uncompressed DER format', function() { - var x = bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); - var pk = new pubkey(); + var x = Bn.fromBuffer(new Buffer('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + var pk = new Pubkey(); pk.fromX(true, x); pk.toDER(false).toString('hex').should.equal('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); }); @@ -97,7 +97,7 @@ describe('pubkey', function() { it('should print this known public key', function() { var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; - var pk = new pubkey(); + var pk = new Pubkey(); pk.fromString(hex); pk.toString().should.equal(hex); }); @@ -108,14 +108,14 @@ describe('pubkey', function() { it('should not throw an error if pubkey is valid', function() { var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'; - var pk = new pubkey(); + var pk = new Pubkey(); pk.fromString(hex); should.exist(pk.validate()); }); it('should not throw an error if pubkey is invalid', function() { var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a0000000000000000000000000000000000000000000000000000000000000000'; - var pk = new pubkey(); + var pk = new Pubkey(); pk.fromString(hex); (function() { pk.validate(); @@ -123,8 +123,8 @@ describe('pubkey', function() { }); it('should not throw an error if pubkey is infinity', function() { - var pk = new pubkey(); - pk.p = point.getG().mul(point.getN()); + var pk = new Pubkey(); + pk.point = Point.getG().mul(Point.getN()); (function() { pk.validate(); }).should.throw('point: Point cannot be equal to Infinity'); From 8743c68ce67a9e5f21e0f3f6ed2e023c697d633d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 13 Aug 2014 17:31:28 -0400 Subject: [PATCH 034/280] kdf --- index.js | 35 +++++++++++++++++++++-------------- lib/kdf.js | 33 +++++++++++++++++++++++++++++++++ test/test.kdf.js | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 lib/kdf.js create mode 100644 test/test.kdf.js diff --git a/index.js b/index.js index a8f495e..e7bf374 100644 --- a/index.js +++ b/index.js @@ -1,20 +1,27 @@ var privsec = module.exports; -privsec.address = require('./lib/address'); -privsec.base58 = require('./lib/base58'); -privsec.base58check = require('./lib/base58check'); -privsec.bip32 = require('./lib/bip32'); -privsec.bn = require('./lib/bn'); -privsec.constants = require('./lib/constants'); -privsec.ecdsa = require('./lib/ecdsa'); -privsec.hash = require('./lib/hash'); -privsec.key = require('./lib/key'); -privsec.point = require('./lib/point'); -privsec.privkey = require('./lib/privkey'); -privsec.pubkey = require('./lib/pubkey'); -privsec.random = require('./lib/random'); -privsec.signature = require('./lib/signature'); +//main bitcoin library +privsec.Address = require('./lib/address'); +privsec.Base58 = require('./lib/base58'); +privsec.Base58Check = require('./lib/base58check'); +privsec.BIP32 = require('./lib/bip32'); +privsec.BN = require('./lib/bn'); +privsec.Constants = require('./lib/constants'); +privsec.ECDSA = require('./lib/ecdsa'); +privsec.Hash = require('./lib/hash'); +privsec.KDF = require('./lib/kdf'); +privsec.Key = require('./lib/key'); +privsec.Point = require('./lib/point'); +privsec.Privkey = require('./lib/privkey'); +privsec.Pubkey = require('./lib/pubkey'); +privsec.Random = require('./lib/random'); +privsec.Signature = require('./lib/signature'); +//experimental +//privsec.expmt = {}; +//privsec.expmt.Stealth = require('./lib/expmt/stealth'); + +//dependencies privsec.deps = {}; privsec.deps.bnjs = require('bn.js'); privsec.deps.bs58 = require('bs58'); diff --git a/lib/kdf.js b/lib/kdf.js new file mode 100644 index 0000000..91d0752 --- /dev/null +++ b/lib/kdf.js @@ -0,0 +1,33 @@ +var Bn = require('./bn'); +var Privkey = require('./privkey'); +var Point = require('./point'); +var Pubkey = require('./pubkey'); +var Key = require('./key'); +var Hash = require('./hash'); + +function KDF() { +}; + +KDF.buf2key = function(buf) { + return KDF.sha256hmac2key(buf); +}; + +KDF.sha256hmac2key = function(buf) { + var privkey = KDF.sha256hmac2privkey(buf); + var key = new Key(privkey); + key.privkey2pubkey(); + return key; +}; + +KDF.sha256hmac2privkey = function(buf) { + var bn; + var concat = new Buffer([]); + do { + var hash = Hash.sha256hmac(buf, concat); + var bn = Bn.fromBuffer(hash); + concat = Buffer.concat([concat, new Buffer(0)]); + } while(!bn.lt(Point.getN())); + return new Privkey(bn); +}; + +module.exports = KDF; diff --git a/test/test.kdf.js b/test/test.kdf.js new file mode 100644 index 0000000..4571b4c --- /dev/null +++ b/test/test.kdf.js @@ -0,0 +1,39 @@ +var should = require('chai').should(); +var KDF = require('../lib/kdf'); +var Hash = require('../lib/hash'); + +describe('kdf', function() { + + describe('#buf2key', function() { + + it('should compute these known values', function() { + var buf = Hash.sha256(new Buffer('test')); + var key = KDF.buf2key(buf); + key.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); + key.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1'); + }); + + }); + + describe('#sha256hmac2key', function() { + + it('should compute these known values', function() { + var buf = Hash.sha256(new Buffer('test')); + var key = KDF.sha256hmac2key(buf); + key.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); + key.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1'); + }); + + }); + + describe('#sha256hmac2privkey', function() { + + it('should compute this known privkey', function() { + var buf = Hash.sha256(new Buffer('test')); + var privkey = KDF.sha256hmac2privkey(buf); + privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); + }); + + }); + +}); From a2512226f89cc49cf2684342650c1fcce68a41c5 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 13 Aug 2014 18:00:41 -0400 Subject: [PATCH 035/280] address convenience functions --- lib/address.js | 11 +++++++++++ lib/key.js | 5 +++++ test/test.address.js | 21 +++++++++++++++++++++ test/test.key.js | 14 ++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/lib/address.js b/lib/address.js index 798d993..35488bd 100644 --- a/lib/address.js +++ b/lib/address.js @@ -1,5 +1,7 @@ var base58check = require('./base58check'); var constants = require('./constants'); +var Hash = require('./hash'); +var Pubkey = require('./pubkey'); function Address(hash, network, type) { this.hash = hash; @@ -7,6 +9,15 @@ function Address(hash, network, type) { this.type = type; }; +Address.prototype.fromPubkey = function(pubkey, network, compressed) { + if (typeof compressed === 'undefined') + compressed = true; + this.hash = Hash.sha256ripemd160(pubkey.toDER(compressed)); + this.network = network || 'mainnet'; + this.type = 'pubkeyhash'; + return this; +}; + Address.prototype.fromString = function(str) { var buf = base58check.decode(str); if (buf.length !== 1 + 20) diff --git a/lib/key.js b/lib/key.js index 11e11b2..cefc341 100644 --- a/lib/key.js +++ b/lib/key.js @@ -1,3 +1,4 @@ +var Address = require('../lib/address'); var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var Random = require('./random'); @@ -31,6 +32,10 @@ Key.prototype.fromString = function(str) { } }; +Key.prototype.getAddress = function(network, compressed) { + return (new Address()).fromPubkey(this.pubkey, network, compressed); +}; + Key.prototype.privkey2pubkey = function() { this.pubkey = new Pubkey(point.getG().mul(this.privkey.bn)); }; diff --git a/test/test.address.js b/test/test.address.js index 2bd7002..498d110 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -1,5 +1,6 @@ var should = require('chai').should(); var constants = require('../lib/constants'); +var Pubkey = require('../lib/pubkey'); var Address = require('../lib/address'); describe('address', function() { @@ -11,6 +12,26 @@ describe('address', function() { should.exist(address); }); + describe('#fromPubkey', function() { + + it('should make this address from a compressed pubkey', function() { + var pubkey = new Pubkey(); + pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex')); + var address = new Address(); + address.fromPubkey(pubkey); + address.toString().should.equal('19gH5uhqY6DKrtkU66PsZPUZdzTd11Y7ke'); + }); + + it('should make this address from an uncompressed pubkey', function() { + var pubkey = new Pubkey(); + pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex')); + var address = new Address(); + address.fromPubkey(pubkey, 'mainnet', false); + address.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); + }); + + }); + describe('#fromString', function() { it('should derive from this known address string mainnet', function() { diff --git a/test/test.key.js b/test/test.key.js index 37a4559..7e21fac 100644 --- a/test/test.key.js +++ b/test/test.key.js @@ -1,6 +1,7 @@ var should = require('chai').should(); var bn = require('../lib/bn'); var point = require('../lib/point'); +var Address = require('../lib/address'); var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); var Key = require('../lib/key'); @@ -74,6 +75,19 @@ describe('key', function() { }); + describe('#getAddress', function() { + + it('should return an address', function() { + var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; + var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; + var key = new Key(); + key.privkey = new Privkey(bn(new Buffer(privhex, 'hex'))); + key.privkey2pubkey(); + key.getAddress().toString().should.equal((new Address()).fromPubkey(key.pubkey).toString()); + }); + + }); + describe("#privkey2pubkey", function() { it('should convert this known Privkey to known Pubkey', function() { From f6f7a870fb1594a96fd9a4dd6031359fa2b35c11 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 13 Aug 2014 18:55:33 -0400 Subject: [PATCH 036/280] allow creating objects without using "new" --- lib/bip32.js | 4 +++- lib/ecdsa.js | 4 +++- lib/key.js | 4 +++- lib/privkey.js | 4 +++- lib/pubkey.js | 4 +++- lib/signature.js | 4 +++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 0df8129..9e20626 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -8,7 +8,9 @@ var Random = require('./random'); var bn = require('./bn'); var constants = require('./constants'); -var BIP32 = function(str) { +var BIP32 = function BIP32(str) { + if (!(this instanceof BIP32)) + return new BIP32(str); if (str === 'testnet' || str === 'mainnet') { this.version = constants[str].bip32privkey; this.fromRandom(); diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 76cd580..38b0ce9 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -6,7 +6,9 @@ var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var Random = require('./random'); -var ECDSA = function(hash, key, sig, k) { +var ECDSA = function ECDSA(hash, key, sig, k) { + if (!(this instanceof ECDSA)) + return new ECDSA(hash, key, sig, k); this.hash = hash; this.key = key; this.sig = sig; diff --git a/lib/key.js b/lib/key.js index cefc341..c48d3f6 100644 --- a/lib/key.js +++ b/lib/key.js @@ -5,7 +5,9 @@ var Random = require('./random'); var Bn = require('./bn'); var point = require('./point'); -function Key(privkey, pubkey) { +var Key = function Key(privkey, pubkey) { + if (!(this instanceof Key)) + return new Key(privkey, pubkey); this.privkey = privkey; this.pubkey = pubkey; }; diff --git a/lib/privkey.js b/lib/privkey.js index 7aa09cd..bed0b23 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -3,7 +3,9 @@ var point = require('./point'); var constants = require('./constants'); var base58check = require('./base58check'); -var Privkey = function(bn, network, compressed) { +var Privkey = function Privkey(bn, network, compressed) { + if (!(this instanceof Privkey)) + return new Privkey(bn, network, compressed); this.bn = bn; this.network = network; this.compressed = compressed; diff --git a/lib/pubkey.js b/lib/pubkey.js index ded1060..1199034 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -1,7 +1,9 @@ var Point = require('./point'); var bn = require('./bn'); -var Pubkey = function(point) { +var Pubkey = function Pubkey(point) { + if (!(this instanceof Pubkey)) + return new Pubkey(point); if (point && !point.getX() && !point.getY()) throw new Error('pubkey: Invalid point'); this.point = point; diff --git a/lib/signature.js b/lib/signature.js index 451358e..8d911a1 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -1,6 +1,8 @@ var bn = require('./bn'); -var Signature = function(r, s) { +var Signature = function Signature(r, s) { + if (!(this instanceof Signature)) + return new Signature(r, s); this.r = r; this.s = s; }; From f8fc3812f078ff732168867ecd9cd9092fd6acbd Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 13 Aug 2014 19:23:45 -0400 Subject: [PATCH 037/280] add BN.prototype.fromBuffer --- lib/bn.js | 7 +++++++ test/test.bn.js | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/lib/bn.js b/lib/bn.js index ad1ba05..a4eeea0 100644 --- a/lib/bn.js +++ b/lib/bn.js @@ -31,6 +31,13 @@ bnjs.fromBuffer = function(buf, opts) { return bn; }; +bnjs.prototype.fromBuffer = function(buf, opts) { + var bn = bnjs.fromBuffer(buf, opts); + bn.copy(this); + + return this; +}; + bnjs.prototype.toBuffer = function(opts) { var buf; if (opts && opts.size) { diff --git a/test/test.bn.js b/test/test.bn.js index fd19344..da7226a 100644 --- a/test/test.bn.js +++ b/test/test.bn.js @@ -83,6 +83,11 @@ describe('bn', function() { bn.toString().should.equal('1'); }); + it('should work as a prototype method', function() { + var bn = BN().fromBuffer(new Buffer('0100', 'hex'), {size: 2, endian: 'little'}); + bn.toString().should.equal('1'); + }); + }); describe('#toBuffer', function() { From bc1c4235f250ea468c1da64dd11b99b68e42e8d1 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 13 Aug 2014 20:54:05 -0400 Subject: [PATCH 038/280] basic stealth address support Math only. Does not yet support transactions. Not yet compatible with Dark Wallet. --- index.js | 4 +- lib/expmt/stealth.js | 111 +++++++++++++++++++++++++++ test/test.stealth.js | 177 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 lib/expmt/stealth.js create mode 100644 test/test.stealth.js diff --git a/index.js b/index.js index e7bf374..8fa81aa 100644 --- a/index.js +++ b/index.js @@ -18,8 +18,8 @@ privsec.Random = require('./lib/random'); privsec.Signature = require('./lib/signature'); //experimental -//privsec.expmt = {}; -//privsec.expmt.Stealth = require('./lib/expmt/stealth'); +privsec.expmt = {}; +privsec.expmt.Stealth = require('./lib/expmt/stealth'); //dependencies privsec.deps = {}; diff --git a/lib/expmt/stealth.js b/lib/expmt/stealth.js new file mode 100644 index 0000000..87d0431 --- /dev/null +++ b/lib/expmt/stealth.js @@ -0,0 +1,111 @@ +var Key = require('../key'); +var Privkey = require('../privkey'); +var Pubkey = require('../pubkey'); +var Hash = require('../hash'); +var KDF = require('../kdf'); +var base58check = require('../base58check'); + +var Stealth = function Stealth(payloadKey, scanKey) { + if (!(this instanceof Stealth)) + return new Stealth(payloadKey, scanKey); + + this.payloadKey = payloadKey; + this.scanKey = scanKey; +}; + +Stealth.prototype.fromAddressBuffer = function(buf) { + if (!Buffer.isBuffer(buf) || buf.length !== 66) + throw new Error('stealth: A stealth address must have length 66'); + + var pPubBuf = buf.slice(0, 33); + var sPubBuf = buf.slice(33, 66); + + var payloadPubkey = Pubkey().fromDER(pPubBuf); + this.payloadKey = Key(undefined, payloadPubkey); + var scanPubkey = Pubkey().fromDER(sPubBuf); + this.scanKey = Key(undefined, scanPubkey); + + return this; +}; + +Stealth.prototype.fromAddressString = function(str) { + var buf = base58check.decode(str); + this.fromAddressBuffer(buf); + + return this; +}; + +Stealth.prototype.fromRandom = function() { + this.payloadKey = Key().fromRandom(); + this.scanKey = Key().fromRandom(); + + return this; +}; + +Stealth.prototype.getSharedKeyAsReceiver = function(senderPubkey) { + var sharedSecretPoint = senderPubkey.point.mul(this.scanKey.privkey.bn); + var sharedSecretPubkey = Pubkey(sharedSecretPoint); + var buf = sharedSecretPubkey.toDER(true); + var sharedKey = KDF.sha256hmac2key(buf); + + return sharedKey; +}; + +Stealth.prototype.getSharedKeyAsSender = function(senderKey) { + var sharedSecretPoint = this.scanKey.pubkey.point.mul(senderKey.privkey.bn); + var sharedSecretPubkey = Pubkey(sharedSecretPoint); + var buf = sharedSecretPubkey.toDER(true); + var sharedKey = KDF.sha256hmac2key(buf); + + return sharedKey; +}; + +Stealth.prototype.getReceivePubkeyAsReceiver = function(senderPubkey) { + var sharedKey = this.getSharedKeyAsReceiver(senderPubkey); + var pubkey = Pubkey(this.payloadKey.pubkey.point.add(sharedKey.pubkey.point)); + + return pubkey; +}; + +Stealth.prototype.getReceivePubkeyAsSender = function(senderKey) { + var sharedKey = this.getSharedKeyAsSender(senderKey); + var pubkey = Pubkey(this.payloadKey.pubkey.point.add(sharedKey.pubkey.point)); + + return pubkey; +}; + +Stealth.prototype.getReceiveKey = function(senderPubkey) { + var sharedKey = this.getSharedKeyAsReceiver(senderPubkey); + var privkey = Privkey(this.payloadKey.privkey.bn.add(sharedKey.privkey.bn)); + var key = Key(privkey); + key.privkey2pubkey(); + + return key; +}; + +Stealth.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) { + var pubkey = this.getReceivePubkeyAsReceiver(senderPubkey); + var pubkeybuf = pubkey.toDER(true); + var pubkeyhash = Hash.sha256ripemd160(pubkeybuf); + + if (pubkeyhash.toString('hex') === myPossiblePubkeyhash.toString('hex')) + return true; + else + return false; +}; + +Stealth.prototype.toAddressBuffer = function() { + var pBuf = this.payloadKey.pubkey.toDER(true); + var sBuf = this.scanKey.pubkey.toDER(true); + + return Buffer.concat([pBuf, sBuf]); +}; + +Stealth.prototype.toAddressString = function() { + var buf = this.toAddressBuffer(); + var b58 = base58check.encode(buf); + + return b58; +}; + +module.exports = Stealth; diff --git a/test/test.stealth.js b/test/test.stealth.js new file mode 100644 index 0000000..40af9b3 --- /dev/null +++ b/test/test.stealth.js @@ -0,0 +1,177 @@ +var should = require('chai').should(); +var Stealth = require('../lib/expmt/stealth'); +var Key = require('../lib/key'); +var Privkey = require('../lib/privkey'); +var Pubkey = require('../lib/pubkey'); +var BN = require('../lib/bn'); +var Hash = require('../lib/hash'); +var base58check = require('../lib/base58check'); + +describe('stealth', function() { + + var stealth = Stealth(); + stealth.payloadKey = Key(); + stealth.payloadKey.privkey = Privkey(); + stealth.payloadKey.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1'))); + stealth.payloadKey.privkey2pubkey(); + stealth.scanKey = Key(); + stealth.scanKey.privkey = Privkey(); + stealth.scanKey.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2'))); + stealth.scanKey.privkey2pubkey(); + + var senderKey = Key(); + senderKey.privkey = Privkey(); + senderKey.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); + senderKey.privkey2pubkey(); + + var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW'; + + it('should create a new stealth', function() { + var stealth = new Stealth(); + should.exist(stealth); + }); + + it('should create a new stealth without using "new"', function() { + var stealth = Stealth(); + should.exist(stealth); + }); + + describe('#fromAddressBuffer', function() { + + it('should give a stealth address with the right pubkeys', function() { + var stealth2 = new Stealth(); + var buf = base58check.decode(addressString); + stealth2.fromAddressBuffer(buf); + stealth2.payloadKey.pubkey.toString().should.equal(stealth.payloadKey.pubkey.toString()); + stealth2.scanKey.pubkey.toString().should.equal(stealth.scanKey.pubkey.toString()); + }); + + }); + + describe('#fromAddressString', function() { + + it('should give a stealth address with the right pubkeys', function() { + var stealth2 = new Stealth(); + stealth2.fromAddressString(addressString); + stealth2.payloadKey.pubkey.toString().should.equal(stealth.payloadKey.pubkey.toString()); + stealth2.scanKey.pubkey.toString().should.equal(stealth.scanKey.pubkey.toString()); + }); + + }); + + describe('#fromRandom', function() { + + it('should create a new stealth from random', function() { + var stealth = Stealth().fromRandom(); + should.exist(stealth.payloadKey.privkey.bn.gt(0)); + should.exist(stealth.scanKey.privkey.bn.gt(0)); + }); + + }); + + describe('#getSharedKeyAsReceiver', function() { + + it('should return a key', function() { + var key = stealth.getSharedKeyAsReceiver(senderKey.pubkey); + (key instanceof Key).should.equal(true); + }); + + }); + + describe('#getSharedKeyAsSender', function() { + + it('should return a key', function() { + var stealth2 = new Stealth(); + stealth2.payloadKey = new Key(); + stealth2.payloadKey.pubkey = stealth.payloadKey.pubkey; + stealth2.scanKey = new Key(); + stealth2.scanKey.pubkey = stealth.scanKey.pubkey; + var key = stealth2.getSharedKeyAsSender(senderKey); + (key instanceof Key).should.equal(true); + }); + + it('should return the same key as getSharedKeyAsReceiver', function() { + var stealth2 = new Stealth(); + stealth2.payloadKey = new Key(); + stealth2.payloadKey.pubkey = stealth.payloadKey.pubkey; + stealth2.scanKey = new Key(); + stealth2.scanKey.pubkey = stealth.scanKey.pubkey; + var key = stealth2.getSharedKeyAsSender(senderKey); + + var key2 = stealth.getSharedKeyAsReceiver(senderKey.pubkey); + key.toString().should.equal(key2.toString()); + }); + + }); + + describe('#getReceivePubkeyAsReceiver', function() { + + it('should return a pubkey', function() { + var pubkey = stealth.getReceivePubkeyAsReceiver(senderKey.pubkey); + (pubkey instanceof Pubkey).should.equal(true); + }); + + }); + + describe('#getReceivePubkeyAsSender', function() { + + it('should return a pubkey', function() { + var pubkey = stealth.getReceivePubkeyAsSender(senderKey); + (pubkey instanceof Pubkey).should.equal(true); + }); + + it('should return the same pubkey as getReceivePubkeyAsReceiver', function() { + var pubkey = stealth.getReceivePubkeyAsSender(senderKey); + var pubkey2 = stealth.getReceivePubkeyAsReceiver(senderKey.pubkey); + pubkey2.toString().should.equal(pubkey.toString()); + }); + + }); + + describe('#getReceiveKey', function() { + + it('should return a key', function() { + var key = stealth.getReceiveKey(senderKey.pubkey); + (key instanceof Key).should.equal(true); + }); + + it('should return a key with the same pubkey as getReceivePubkeyAsReceiver', function() { + var key = stealth.getReceiveKey(senderKey.pubkey); + var pubkey = stealth.getReceivePubkeyAsReceiver(senderKey.pubkey); + key.pubkey.toString().should.equal(pubkey.toString()); + }); + + }); + + describe('#isForMe', function() { + + it('should return true if it (the transaction or message) is for me', function() { + var pubkeyhash = new Buffer('3cb64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); + stealth.isForMe(senderKey.pubkey, pubkeyhash).should.equal(true); + }); + + it('should return false if it (the transaction or message) is not for me', function() { + var pubkeyhash = new Buffer('00b64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); + stealth.isForMe(senderKey.pubkey, pubkeyhash).should.equal(false); + }); + + }); + + describe('#toAddressBuffer', function() { + + it('should return this known address buffer', function() { + var buf = stealth.toAddressBuffer(); + base58check.encode(buf).should.equal(addressString); + }); + + }); + + describe('#toAddressString', function() { + + it('should return this known address string', function() { + stealth.toAddressString().should.equal(addressString); + }); + + }); + +}); From b783b58762af75d52b1202aa8e097dae72c53465 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 14 Aug 2014 11:02:28 -0400 Subject: [PATCH 039/280] rename privsec -> bitcore --- LICENSE.md | 4 +++- README.md | 4 ++-- index.js | 64 ++++++++++++++++++++++++++-------------------------- package.json | 4 ++-- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 3e2c98c..eb49cdc 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,8 @@ This software is licensed under the MIT License. -Copyright Ryan X. Charles, 2014. +Copyright BitPay, 2014. + +Portions of this software are Copyright Stefan Thomas, 2011. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index f7f95a4..af85596 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -privsec +bitcore ======= -Bitcoin privacy and security features implemented in pure javascript. +A bitcoin library. diff --git a/index.js b/index.js index 8fa81aa..a8b9b4f 100644 --- a/index.js +++ b/index.js @@ -1,38 +1,38 @@ -var privsec = module.exports; +var bitcore = module.exports; //main bitcoin library -privsec.Address = require('./lib/address'); -privsec.Base58 = require('./lib/base58'); -privsec.Base58Check = require('./lib/base58check'); -privsec.BIP32 = require('./lib/bip32'); -privsec.BN = require('./lib/bn'); -privsec.Constants = require('./lib/constants'); -privsec.ECDSA = require('./lib/ecdsa'); -privsec.Hash = require('./lib/hash'); -privsec.KDF = require('./lib/kdf'); -privsec.Key = require('./lib/key'); -privsec.Point = require('./lib/point'); -privsec.Privkey = require('./lib/privkey'); -privsec.Pubkey = require('./lib/pubkey'); -privsec.Random = require('./lib/random'); -privsec.Signature = require('./lib/signature'); +bitcore.Address = require('./lib/address'); +bitcore.Base58 = require('./lib/base58'); +bitcore.Base58Check = require('./lib/base58check'); +bitcore.BIP32 = require('./lib/bip32'); +bitcore.BN = require('./lib/bn'); +bitcore.Constants = require('./lib/constants'); +bitcore.ECDSA = require('./lib/ecdsa'); +bitcore.Hash = require('./lib/hash'); +bitcore.KDF = require('./lib/kdf'); +bitcore.Key = require('./lib/key'); +bitcore.Point = require('./lib/point'); +bitcore.Privkey = require('./lib/privkey'); +bitcore.Pubkey = require('./lib/pubkey'); +bitcore.Random = require('./lib/random'); +bitcore.Signature = require('./lib/signature'); -//experimental -privsec.expmt = {}; -privsec.expmt.Stealth = require('./lib/expmt/stealth'); +//experimental, nonstandard, or unstable features +bitcore.expmt = {}; +bitcore.expmt.Stealth = require('./lib/expmt/stealth'); -//dependencies -privsec.deps = {}; -privsec.deps.bnjs = require('bn.js'); -privsec.deps.bs58 = require('bs58'); -privsec.deps.buffer = Buffer; -privsec.deps.elliptic = require('elliptic'); -privsec.deps.hashjs = require('hash.js'); -privsec.deps.sha512 = require('sha512'); +//dependencies, subject to change +bitcore.deps = {}; +bitcore.deps.bnjs = require('bn.js'); +bitcore.deps.bs58 = require('bs58'); +bitcore.deps.buffer = Buffer; +bitcore.deps.elliptic = require('elliptic'); +bitcore.deps.hashjs = require('hash.js'); +bitcore.deps.sha512 = require('sha512'); -//privsec.script = require('lib/script'); -//privsec.scriptexec = require('lib/scriptexec'); -//privsec.tx = require('lib/tx'); -//privsec.txpartial = require('lib/txpartial'); +//bitcore.script = require('lib/script'); +//bitcore.scriptexec = require('lib/scriptexec'); +//bitcore.tx = require('lib/tx'); +//bitcore.txpartial = require('lib/txpartial'); -//privsec.bip70 = require('lib/bip70'); +//bitcore.bip70 = require('lib/bip70'); diff --git a/package.json b/package.json index 3399445..fd7de48 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "privsec", + "name": "bitcore", "version": "0.0.0", - "description": "A bitcoin wallet prioritizing privacy and security.", + "description": "A bitcoin library.", "main": "index.js", "scripts": { "test": "mocha" From 9cc214cf064e5f58d417ff1442f4c45a52623515 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 14 Aug 2014 11:32:47 -0400 Subject: [PATCH 040/280] capitalize classes in tests --- test/test.address.js | 2 +- test/test.base58check.js | 2 +- test/test.bn.js | 2 +- test/test.ecdsa.js | 2 +- test/test.hash.js | 2 +- test/test.kdf.js | 2 +- test/test.key.js | 2 +- test/test.point.js | 2 +- test/test.privkey.js | 2 +- test/test.pubkey.js | 2 +- test/test.random.js | 2 +- test/test.stealth.js | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/test.address.js b/test/test.address.js index 498d110..68881cf 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -3,7 +3,7 @@ var constants = require('../lib/constants'); var Pubkey = require('../lib/pubkey'); var Address = require('../lib/address'); -describe('address', function() { +describe('Address', function() { var pubkeyhash = new Buffer('3c3fa3d4adcaf8f52d5b1843975e122548269937', 'hex'); var str = '16VZnHwRhwrExfeHFHGjwrgEMq8VcYPs9r'; diff --git a/test/test.base58check.js b/test/test.base58check.js index 8b50813..ac6da3c 100644 --- a/test/test.base58check.js +++ b/test/test.base58check.js @@ -2,7 +2,7 @@ var should = require('chai').should(); var base58check = require('../lib/base58check'); var base58 = require('../lib/base58'); -describe('base58check', function() { +describe('Base58check', function() { var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); var enc = "14HV44ipwoaqfg"; diff --git a/test/test.bn.js b/test/test.bn.js index da7226a..8a7d458 100644 --- a/test/test.bn.js +++ b/test/test.bn.js @@ -3,7 +3,7 @@ var should = chai.should(); var assert = chai.assert; var BN = require('../lib/bn'); -describe('bn', function() { +describe('BN', function() { it('should create a bn', function() { var bn = new BN(50); should.exist(bn); diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index 915acf7..f7db604 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -8,7 +8,7 @@ var bn = require('../lib/bn'); var point = require('../lib/point'); var should = require('chai').should(); -describe("ecdsa", function() { +describe("ECDSA", function() { it('should create a blank ecdsa', function() { var ecdsa = new ECDSA(); diff --git a/test/test.hash.js b/test/test.hash.js index 2e8d010..f146a62 100644 --- a/test/test.hash.js +++ b/test/test.hash.js @@ -1,7 +1,7 @@ var should = require('chai').should(); var Hash = require('../lib/hash'); -describe('hash', function() { +describe('Hash', function() { var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); var str = "test string"; diff --git a/test/test.kdf.js b/test/test.kdf.js index 4571b4c..faeeda2 100644 --- a/test/test.kdf.js +++ b/test/test.kdf.js @@ -2,7 +2,7 @@ var should = require('chai').should(); var KDF = require('../lib/kdf'); var Hash = require('../lib/hash'); -describe('kdf', function() { +describe('KDF', function() { describe('#buf2key', function() { diff --git a/test/test.key.js b/test/test.key.js index 7e21fac..f2c098f 100644 --- a/test/test.key.js +++ b/test/test.key.js @@ -6,7 +6,7 @@ var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); var Key = require('../lib/key'); -describe('key', function() { +describe('Key', function() { it('should make a blank key', function() { var key = new Key(); diff --git a/test/test.point.js b/test/test.point.js index bf5e078..583d709 100644 --- a/test/test.point.js +++ b/test/test.point.js @@ -2,7 +2,7 @@ var should = require('chai').should(); var point = require('../lib/point'); var bn = require('../lib/bn'); -describe('point', function() { +describe('Point', function() { it('should create a point', function() { var p = point(); diff --git a/test/test.privkey.js b/test/test.privkey.js index 2ab2e86..8bb0a85 100644 --- a/test/test.privkey.js +++ b/test/test.privkey.js @@ -3,7 +3,7 @@ var base58check = require('../lib/base58check'); var Bn = require('../lib/bn'); var should = require('chai').should(); -describe('privkey', function() { +describe('Privkey', function() { var hex = '96c132224121b509b7d0a16245e957d9192609c5637c6228311287b1be21627a'; var buf = new Buffer(hex, 'hex'); var enctestnet = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; diff --git a/test/test.pubkey.js b/test/test.pubkey.js index 9eeb7df..a9bf39e 100644 --- a/test/test.pubkey.js +++ b/test/test.pubkey.js @@ -3,7 +3,7 @@ var Pubkey = require('../lib/pubkey'); var Point = require('../lib/point'); var Bn = require('../lib/bn'); -describe('pubkey', function() { +describe('Pubkey', function() { it('should create a blank public key', function() { var pk = new Pubkey(); diff --git a/test/test.random.js b/test/test.random.js index a97708f..242f978 100644 --- a/test/test.random.js +++ b/test/test.random.js @@ -1,7 +1,7 @@ var should = require('chai').should(); var Random = require('../lib/random'); -describe('random', function() { +describe('Random', function() { describe('#getRandomBuffer', function() { diff --git a/test/test.stealth.js b/test/test.stealth.js index 40af9b3..7ebbf7d 100644 --- a/test/test.stealth.js +++ b/test/test.stealth.js @@ -7,7 +7,7 @@ var BN = require('../lib/bn'); var Hash = require('../lib/hash'); var base58check = require('../lib/base58check'); -describe('stealth', function() { +describe('Stealth', function() { var stealth = Stealth(); stealth.payloadKey = Key(); From de08f78d742001bfa114ff414f0f781885e7b94e Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 14 Aug 2014 11:56:17 -0400 Subject: [PATCH 041/280] give Base58Check the same fromString toString ...interface like the rest of the classes --- index.js | 2 +- lib/base58check.js | 53 ++++++++++++++++++++++-------- test/test.base58check.js | 71 +++++++++++++++++++++++++++++++++------- 3 files changed, 100 insertions(+), 26 deletions(-) diff --git a/index.js b/index.js index a8b9b4f..2dbcbd6 100644 --- a/index.js +++ b/index.js @@ -25,7 +25,7 @@ bitcore.expmt.Stealth = require('./lib/expmt/stealth'); bitcore.deps = {}; bitcore.deps.bnjs = require('bn.js'); bitcore.deps.bs58 = require('bs58'); -bitcore.deps.buffer = Buffer; +bitcore.deps.Buffer = Buffer; bitcore.deps.elliptic = require('elliptic'); bitcore.deps.hashjs = require('hash.js'); bitcore.deps.sha512 = require('sha512'); diff --git a/lib/base58check.js b/lib/base58check.js index 15313d8..273cdc3 100644 --- a/lib/base58check.js +++ b/lib/base58check.js @@ -1,26 +1,20 @@ 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); +var Base58Check = function Base58Check(buf) { + if (!(this instanceof Base58Check)) + return new Base58Check(buf); + this.buf = buf; }; -base58check.decode = function(s) { +Base58Check.decode = function(s) { if (typeof s !== 'string') - throw new Error('base58check: Input must be a 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"); + throw new Error("Base58Check: Input string too short"); var data = buf.slice(0, -4); var csum = buf.slice(-4); @@ -29,7 +23,38 @@ base58check.decode = function(s) { var hash4 = hash.slice(0, 4); if (csum.toString('hex') !== hash4.toString('hex')) - throw new Error("base58check: Checksum mismatch"); + throw new Error("Base58Check: Checksum mismatch"); return data; }; + +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.prototype.fromBuffer = function(buf) { + this.buf = buf; + return this; +}; + +Base58Check.prototype.fromString = function(str) { + var buf = Base58Check.decode(str); + this.buf = buf; + return this; +}; + +Base58Check.prototype.toBuffer = function() { + return this.buf; +}; + +Base58Check.prototype.toString = function() { + return Base58Check.encode(this.buf); +}; + +module.exports = Base58Check; diff --git a/test/test.base58check.js b/test/test.base58check.js index ac6da3c..c834020 100644 --- a/test/test.base58check.js +++ b/test/test.base58check.js @@ -1,21 +1,31 @@ var should = require('chai').should(); -var base58check = require('../lib/base58check'); +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"; + it('should make an instance with "new"', function() { + var b58 = new Base58Check(); + should.exist(b58); + }); + + it('should make an instance without "new"', function() { + var b58 = Base58Check(); + should.exist(b58); + }); + describe('#encode', function() { it('should encode the buffer accurately', function() { - base58check.encode(buf).should.equal(enc); + 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'); + Base58Check.encode("string") + }).should.throw('Base58Check: Input must be a buffer'); }); }); @@ -23,19 +33,19 @@ describe('Base58check', function() { describe('#decode', function() { it('should decode this encoded value correctly', function() { - base58check.decode(enc).toString('hex').should.equal(buf.toString('hex')); + 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'); + 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'); + 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() { @@ -43,8 +53,47 @@ describe('Base58check', function() { buf2[0] = buf2[0] + 1; var enc2 = base58.encode(buf2); (function() { - base58check.decode(enc2); - }).should.throw('base58check: Checksum mismatch'); + Base58Check.decode(enc2); + }).should.throw('Base58Check: Checksum mismatch'); + }); + + }); + + describe('#fromBuffer', function() { + + it('should not fail', function() { + should.exist(Base58Check().fromBuffer(buf)); + }); + + it('should set buffer', function() { + var b58 = Base58Check().fromBuffer(buf); + b58.buf.toString('hex').should.equal(buf.toString('hex')); + }); + + }); + + describe('#fromString', function() { + + it('should convert this known string to a buffer', function() { + Base58Check().fromString(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + }); + + describe('#toBuffer', function() { + + it('should return the buffer', function() { + var b58 = Base58Check(buf); + b58.buf.toString('hex').should.equal(buf.toString('hex')); + }); + + }); + + describe('#toString', function() { + + it('should return the buffer', function() { + var b58 = Base58Check(buf); + b58.toString().should.equal(enc); }); }); From d73ff6fa6ac55730ed89590276b310f958c77baa Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 14 Aug 2014 12:13:09 -0400 Subject: [PATCH 042/280] give Base58 the normal bitcore2 interface --- lib/base58.js | 40 ++++++++++++++++++++- test/test.base58.js | 85 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 test/test.base58.js diff --git a/lib/base58.js b/lib/base58.js index e7516a7..1bb24f8 100644 --- a/lib/base58.js +++ b/lib/base58.js @@ -1,2 +1,40 @@ var bs58 = require('bs58'); -module.exports = bs58; + +var Base58 = function Base58(buf) { + if (!(this instanceof Base58)) + return new Base58(buf); + this.buf = buf; +}; + +Base58.encode = function(buf) { + if (!Buffer.isBuffer(buf)) + throw new Error('Input should be a buffer'); + return bs58.encode(buf); +}; + +Base58.decode = function(str) { + if (typeof str !== 'string') + throw new Error('Input should be a string'); + return bs58.decode(str); +}; + +Base58.prototype.fromBuffer = function(buf) { + this.buf = buf; + return this; +}; + +Base58.prototype.fromString = function(str) { + var buf = Base58.decode(str); + this.buf = buf; + return this; +}; + +Base58.prototype.toBuffer = function() { + return this.buf; +}; + +Base58.prototype.toString = function() { + return Base58.encode(this.buf); +}; + +module.exports = Base58; diff --git a/test/test.base58.js b/test/test.base58.js new file mode 100644 index 0000000..d05b944 --- /dev/null +++ b/test/test.base58.js @@ -0,0 +1,85 @@ +var Base58 = require('../lib/base58'); +var should = require('chai').should(); + +describe('Base58', function() { + var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); + var enc = "1W7N4RuG"; + + it('should make an instance with "new"', function() { + var b58 = new Base58(); + should.exist(b58); + }); + + it('should make an instance without "new"', function() { + var b58 = Base58(); + should.exist(b58); + }); + + describe('#encode', function() { + + it('should encode the buffer accurately', function() { + Base58.encode(buf).should.equal(enc); + }); + + it('should throw an error when the Input is not a buffer', function() { + (function() { + Base58.encode("string") + }).should.throw('Input should be a buffer'); + }); + + }); + + describe('#decode', function() { + + it('should decode this encoded value correctly', function() { + Base58.decode(enc).toString('hex').should.equal(buf.toString('hex')); + }); + + it('should throw an error when Input is not a string', function() { + (function() { + Base58.decode(5); + }).should.throw('Input should be a string'); + }); + + }); + + describe('#fromBuffer', function() { + + it('should not fail', function() { + should.exist(Base58().fromBuffer(buf)); + }); + + it('should set buffer', function() { + var b58 = Base58().fromBuffer(buf); + b58.buf.toString('hex').should.equal(buf.toString('hex')); + }); + + }); + + describe('#fromString', function() { + + it('should convert this known string to a buffer', function() { + Base58().fromString(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + }); + + describe('#toBuffer', function() { + + it('should return the buffer', function() { + var b58 = Base58(buf); + b58.buf.toString('hex').should.equal(buf.toString('hex')); + }); + + }); + + describe('#toString', function() { + + it('should return the buffer', function() { + var b58 = Base58(buf); + b58.toString().should.equal(enc); + }); + + }); + +}); From ab2a4a4d463f87896766287978effacffa9d07cc Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 14 Aug 2014 12:36:35 -0400 Subject: [PATCH 043/280] capitalize 'c' --- test/test.base58check.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.base58check.js b/test/test.base58check.js index c834020..5051ae1 100644 --- a/test/test.base58check.js +++ b/test/test.base58check.js @@ -2,7 +2,7 @@ var should = require('chai').should(); var Base58Check = require('../lib/Base58Check'); var base58 = require('../lib/base58'); -describe('Base58check', function() { +describe('Base58Check', function() { var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]); var enc = "14HV44ipwoaqfg"; From 2dba978ae0dbadbabd7ea8d1f7e5c7e4bcb51d58 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 14 Aug 2014 12:48:41 -0400 Subject: [PATCH 044/280] correct file capitalization --- test/test.base58check.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.base58check.js b/test/test.base58check.js index 5051ae1..c1ef051 100644 --- a/test/test.base58check.js +++ b/test/test.base58check.js @@ -1,5 +1,5 @@ var should = require('chai').should(); -var Base58Check = require('../lib/Base58Check'); +var Base58Check = require('../lib/base58check'); var base58 = require('../lib/base58'); describe('Base58Check', function() { From 381481fb7ce05bf7335bd6b1dd64b6b19ae6ad42 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 15 Aug 2014 12:57:31 -0400 Subject: [PATCH 045/280] rename back to privsec ...if this package ever actually turns into bitcore2, then we will rename it again. --- LICENSE.md | 4 +++- README.md | 4 ++-- index.js | 60 ++++++++++++++++++++++++++-------------------------- package.json | 4 ++-- 4 files changed, 37 insertions(+), 35 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index eb49cdc..c0e6e35 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,8 @@ This software is licensed under the MIT License. -Copyright BitPay, 2014. +Copyright Ryan X. Charles, 2014. + +Portions of this software are Copyright BitPay, 2014. Portions of this software are Copyright Stefan Thomas, 2011. diff --git a/README.md b/README.md index af85596..e5bdb7e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -bitcore +privsec ======= -A bitcoin library. +Bitcoin privacy and security. diff --git a/index.js b/index.js index 2dbcbd6..dbca854 100644 --- a/index.js +++ b/index.js @@ -1,38 +1,38 @@ -var bitcore = module.exports; +var privsec = module.exports; //main bitcoin library -bitcore.Address = require('./lib/address'); -bitcore.Base58 = require('./lib/base58'); -bitcore.Base58Check = require('./lib/base58check'); -bitcore.BIP32 = require('./lib/bip32'); -bitcore.BN = require('./lib/bn'); -bitcore.Constants = require('./lib/constants'); -bitcore.ECDSA = require('./lib/ecdsa'); -bitcore.Hash = require('./lib/hash'); -bitcore.KDF = require('./lib/kdf'); -bitcore.Key = require('./lib/key'); -bitcore.Point = require('./lib/point'); -bitcore.Privkey = require('./lib/privkey'); -bitcore.Pubkey = require('./lib/pubkey'); -bitcore.Random = require('./lib/random'); -bitcore.Signature = require('./lib/signature'); +privsec.Address = require('./lib/address'); +privsec.Base58 = require('./lib/base58'); +privsec.Base58Check = require('./lib/base58check'); +privsec.BIP32 = require('./lib/bip32'); +privsec.BN = require('./lib/bn'); +privsec.Constants = require('./lib/constants'); +privsec.ECDSA = require('./lib/ecdsa'); +privsec.Hash = require('./lib/hash'); +privsec.KDF = require('./lib/kdf'); +privsec.Key = require('./lib/key'); +privsec.Point = require('./lib/point'); +privsec.Privkey = require('./lib/privkey'); +privsec.Pubkey = require('./lib/pubkey'); +privsec.Random = require('./lib/random'); +privsec.Signature = require('./lib/signature'); //experimental, nonstandard, or unstable features -bitcore.expmt = {}; -bitcore.expmt.Stealth = require('./lib/expmt/stealth'); +privsec.expmt = {}; +privsec.expmt.Stealth = require('./lib/expmt/stealth'); //dependencies, subject to change -bitcore.deps = {}; -bitcore.deps.bnjs = require('bn.js'); -bitcore.deps.bs58 = require('bs58'); -bitcore.deps.Buffer = Buffer; -bitcore.deps.elliptic = require('elliptic'); -bitcore.deps.hashjs = require('hash.js'); -bitcore.deps.sha512 = require('sha512'); +privsec.deps = {}; +privsec.deps.bnjs = require('bn.js'); +privsec.deps.bs58 = require('bs58'); +privsec.deps.Buffer = Buffer; +privsec.deps.elliptic = require('elliptic'); +privsec.deps.hashjs = require('hash.js'); +privsec.deps.sha512 = require('sha512'); -//bitcore.script = require('lib/script'); -//bitcore.scriptexec = require('lib/scriptexec'); -//bitcore.tx = require('lib/tx'); -//bitcore.txpartial = require('lib/txpartial'); +//privsec.script = require('lib/script'); +//privsec.scriptexec = require('lib/scriptexec'); +//privsec.tx = require('lib/tx'); +//privsec.txpartial = require('lib/txpartial'); -//bitcore.bip70 = require('lib/bip70'); +//privsec.bip70 = require('lib/bip70'); diff --git a/package.json b/package.json index fd7de48..f856dc0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "bitcore", + "name": "privsec", "version": "0.0.0", - "description": "A bitcoin library.", + "description": "Bitcoin privacy and security.", "main": "index.js", "scripts": { "test": "mocha" From f11ed4d20b3f18dd21f15d8454051bd7c88a918d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 15 Aug 2014 15:09:28 -0400 Subject: [PATCH 046/280] bug: should mod bn addition when adding two private keys to get a new private key, you should mod the result with N so that it is always less than N. --- lib/expmt/stealth.js | 3 ++- test/test.stealth.js | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/expmt/stealth.js b/lib/expmt/stealth.js index 87d0431..20fe407 100644 --- a/lib/expmt/stealth.js +++ b/lib/expmt/stealth.js @@ -1,6 +1,7 @@ var Key = require('../key'); var Privkey = require('../privkey'); var Pubkey = require('../pubkey'); +var Point = require('../point'); var Hash = require('../hash'); var KDF = require('../kdf'); var base58check = require('../base58check'); @@ -76,7 +77,7 @@ Stealth.prototype.getReceivePubkeyAsSender = function(senderKey) { Stealth.prototype.getReceiveKey = function(senderPubkey) { var sharedKey = this.getSharedKeyAsReceiver(senderPubkey); - var privkey = Privkey(this.payloadKey.privkey.bn.add(sharedKey.privkey.bn)); + var privkey = Privkey(this.payloadKey.privkey.bn.add(sharedKey.privkey.bn).mod(Point.getN())); var key = Key(privkey); key.privkey2pubkey(); diff --git a/test/test.stealth.js b/test/test.stealth.js index 7ebbf7d..6bacacd 100644 --- a/test/test.stealth.js +++ b/test/test.stealth.js @@ -141,6 +141,11 @@ describe('Stealth', function() { key.pubkey.toString().should.equal(pubkey.toString()); }); + it('should return private key with length 32 or less', function() { + var key = stealth.getReceiveKey(senderKey.pubkey); + key.privkey.bn.toBuffer().length.should.be.below(33); + }); + }); describe('#isForMe', function() { From 5e3ad3d09d6c0cef60520adc17af2a1c5fac8cb8 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 18 Aug 2014 18:04:47 -0700 Subject: [PATCH 047/280] BufferReader --- lib/bufferreader.js | 90 +++++++++++++++++++ test/test.bufferreader.js | 177 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 lib/bufferreader.js create mode 100644 test/test.bufferreader.js diff --git a/lib/bufferreader.js b/lib/bufferreader.js new file mode 100644 index 0000000..fc06b17 --- /dev/null +++ b/lib/bufferreader.js @@ -0,0 +1,90 @@ +var BufferReader = function BufferReader(buf, pos) { + if (!(this instanceof BufferReader)) + return new BufferReader(buf); + this.buf = buf; + this.pos = pos || 0; +}; + +BufferReader.prototype.eof = function eof() { + return this.pos >= this.buf.length; +}; + +BufferReader.prototype.read = function() { + var buf = this.buf.slice(this.pos); + this.pos = this.buf.length; + return buf; +}; + +BufferReader.prototype.readUInt8 = function() { + var val = this.buf.readUInt8(this.pos); + this.pos = this.pos + 1; + return val; +}; + +BufferReader.prototype.readUInt16BE = function() { + var val = this.buf.readUInt16BE(this.pos); + this.pos = this.pos + 2; + return val; +}; + +BufferReader.prototype.readUInt16LE = function() { + var val = this.buf.readUInt16LE(this.pos); + this.pos = this.pos + 2; + return val; +}; + +BufferReader.prototype.readUInt32BE = function() { + var val = this.buf.readUInt32BE(this.pos); + this.pos = this.pos + 4; + return val; +}; + +BufferReader.prototype.readUInt32LE = function() { + var val = this.buf.readUInt32LE(this.pos); + this.pos = this.pos + 4; + return val; +}; + +//TODO: What if n is so large that it loses precision? +BufferReader.prototype.readUInt64BE = function() { + var val = 0; + for (var i = 0; i < 8; i++) { + val += Math.pow(256, i) * this.buf[this.pos + 8 - 1 - i]; + } + this.pos = this.pos + 8; + return val; +}; + +//TODO: What if n is so large that it loses precision? +BufferReader.prototype.readUInt64LE = function() { + var val = 0; + for (var i = 0; i < 8; i++) { + val += Math.pow(256, i) * this.buf[this.pos + i]; + } + this.pos = this.pos + 8; + return val; +}; + +BufferReader.prototype.readVarInt = function() { + var first = this.readUInt8(); + switch (first) { + case 0xFD: + return this.readUInt16LE(); + case 0xFE: + return this.readUInt32LE(); + case 0xFF: + return this.readUInt64LE(); + default: + return first; + } +}; + +BufferReader.prototype.reverse = function() { + var buf = new Buffer(this.buf.length); + for (var i = 0; i < buf.length; i++) + buf[i] = this.buf[this.buf.length - 1 - i] + this.buf = buf; + return this; +}; + +module.exports = BufferReader; diff --git a/test/test.bufferreader.js b/test/test.bufferreader.js new file mode 100644 index 0000000..bca7b63 --- /dev/null +++ b/test/test.bufferreader.js @@ -0,0 +1,177 @@ +var BufferReader = require('../lib/bufferreader'); +var should = require('chai').should(); + +describe('BufferReader', function() { + + it('should make a new BufferReader', function() { + var br = new BufferReader(); + should.exist(br); + }); + + describe('#eof', function() { + + it('should return true for a blank br', function() { + var br = new BufferReader(new Buffer([])); + br.eof().should.equal(true); + }); + + }); + + describe('read', function() { + + it('should return the same buffer', function() { + var buf = new Buffer([0]); + var br = new BufferReader(buf); + br.read().toString('hex').should.equal(buf.toString('hex')); + }); + + }); + + describe('#readUInt8', function() { + + it('should return 1', function() { + var buf = new Buffer(1); + buf.writeUInt8(1, 0); + var br = new BufferReader(buf); + br.readUInt8().should.equal(1); + }); + + }); + + describe('#readUInt16BE', function() { + + it('should return 1', function() { + var buf = new Buffer(2); + buf.writeUInt16BE(1, 0); + var br = new BufferReader(buf); + br.readUInt16BE().should.equal(1); + }); + + }); + + describe('#readUInt16LE', function() { + + it('should return 1', function() { + var buf = new Buffer(2); + buf.writeUInt16LE(1, 0); + var br = new BufferReader(buf); + br.readUInt16LE().should.equal(1); + }); + + }); + + describe('#readUInt32BE', function() { + + it('should return 1', function() { + var buf = new Buffer(4); + buf.writeUInt32BE(1, 0); + var br = new BufferReader(buf); + br.readUInt32BE().should.equal(1); + }); + + }); + + describe('#readUInt32LE', function() { + + it('should return 1', function() { + var buf = new Buffer(4); + buf.writeUInt32LE(1, 0); + var br = new BufferReader(buf); + br.readUInt32LE().should.equal(1); + }); + + }); + + describe('#readUInt64BE', function() { + + it('should return 1', function() { + var buf = new Buffer(8); + buf.fill(0); + buf.writeUInt32BE(1, 4); + var br = new BufferReader(buf); + br.readUInt64BE().should.equal(1); + }); + + it('should return 2^64', function() { + var buf = new Buffer(8); + buf.fill(0xff); + var br = new BufferReader(buf); + br.readUInt64BE().should.equal(Math.pow(2, 64)); + }); + + }); + + describe('#readUInt64LE', function() { + + it('should return 1', function() { + var buf = new Buffer(8); + buf.fill(0); + buf.writeUInt32LE(1, 0); + var br = new BufferReader(buf); + br.readUInt64LE().should.equal(1); + }); + + it('should return 2^30', function() { + var buf = new Buffer(8); + buf.fill(0); + buf.writeUInt32LE(Math.pow(2, 30), 0); + var br = new BufferReader(buf); + br.readUInt64LE().should.equal(Math.pow(2, 30)); + }); + + it('should return 0', function() { + var buf = new Buffer(8); + buf.fill(0); + var br = new BufferReader(buf); + br.readUInt64LE().should.equal(0); + }); + + it('should return 2^64', function() { + var buf = new Buffer(8); + buf.fill(0xff); + var br = new BufferReader(buf); + br.readUInt64LE().should.equal(Math.pow(2, 64)); + }); + + }); + + describe('#readVarInt', function() { + + it('should read a 1 byte varint', function() { + var buf = new Buffer([50]); + var br = new BufferReader(buf); + br.readVarInt().should.equal(50); + }); + + it('should read a 3 byte varint', function() { + var buf = new Buffer([253, 253, 0]); + var br = new BufferReader(buf); + br.readVarInt().should.equal(253); + }); + + it('should read a 5 byte varint', function() { + var buf = new Buffer([254, 0, 0, 0, 0]); + buf.writeUInt32LE(50000, 1); + var br = new BufferReader(buf); + br.readVarInt().should.equal(50000); + }); + + it('should read a 9 byte varint', function() { + var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]); + var br = new BufferReader(buf); + br.readVarInt().should.equal(Math.pow(2, 64)); + }); + + }); + + describe('#reverse', function() { + + it('should reverse this [0, 1]', function() { + var buf = new Buffer([0, 1]); + var br = new BufferReader(buf); + br.reverse().read().toString('hex').should.equal('0100'); + }); + + }); + +}); From 9d600f47843e602e6d30a6130c0d42bf46f5bdb4 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 18 Aug 2014 18:20:54 -0700 Subject: [PATCH 048/280] BufferWriter --- index.js | 2 + lib/bufferwriter.js | 98 ++++++++++++++++++++++++++++++ test/test.bufferwriter.js | 124 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 lib/bufferwriter.js create mode 100644 test/test.bufferwriter.js diff --git a/index.js b/index.js index dbca854..6a28bce 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,8 @@ privsec.Base58 = require('./lib/base58'); privsec.Base58Check = require('./lib/base58check'); privsec.BIP32 = require('./lib/bip32'); privsec.BN = require('./lib/bn'); +privsec.BufferReader = require('./lib/bufferreader'); +privsec.BufferWriter = require('./lib/bufferwriter'); privsec.Constants = require('./lib/constants'); privsec.ECDSA = require('./lib/ecdsa'); privsec.Hash = require('./lib/hash'); diff --git a/lib/bufferwriter.js b/lib/bufferwriter.js new file mode 100644 index 0000000..2082aed --- /dev/null +++ b/lib/bufferwriter.js @@ -0,0 +1,98 @@ +var BufferWriter = function BufferWriter(bufs) { + if (!(this instanceof BufferWriter)) + return new BufferReader(buf); + this.bufs = bufs || []; +}; + +BufferWriter.prototype.concat = function() { + return Buffer.concat(this.bufs); +}; + +BufferWriter.prototype.write = function(buf) { + this.bufs.push(buf); + return this; +}; + +BufferWriter.prototype.writeUInt8 = function(n) { + var buf = new Buffer(1); + buf.writeUInt8(n, 0); + this.write(buf); + return this; +}; + +BufferWriter.prototype.writeUInt16BE = function(n) { + var buf = new Buffer(2); + buf.writeUInt16BE(n, 0); + this.write(buf); + return this; +}; + +BufferWriter.prototype.writeUInt16LE = function(n) { + var buf = new Buffer(2); + buf.writeUInt16LE(n, 0); + this.write(buf); + return this; +}; + +BufferWriter.prototype.writeUInt32BE = function(n) { + var buf = new Buffer(4); + buf.writeUInt32BE(n, 0); + this.write(buf); + return this; +}; + +BufferWriter.prototype.writeUInt32LE = function(n) { + var buf = new Buffer(4); + buf.writeUInt32LE(n, 0); + this.write(buf); + return this; +}; + +//TODO: What if n is so large that it loses precision? +BufferWriter.prototype.writeUInt64BE = function(n) { + var buf = new Buffer(8); + buf.writeInt32BE(n & -1, 4); + buf.writeUInt32BE(Math.floor(n / 0x100000000), 0); + this.write(buf); + return this; +}; + +//TODO: What if n is so large that it loses precision? +BufferWriter.prototype.writeUInt64LE = function(n) { + var buf = new Buffer(8); + buf.writeInt32LE(n & -1, 0); + buf.writeUInt32LE(Math.floor(n / 0x100000000), 4); + this.write(buf); + return this; +}; + +BufferWriter.prototype.writeVarInt = function(n) { + var buf = BufferWriter.varIntBuf(n); + this.write(buf); + return this; +}; + +//TODO: What if n is so large that it loses precision? +BufferWriter.varIntBuf = function(n) { + var buf = undefined; + if (n < 253) { + buf = new Buffer(1); + buf.writeUInt8(n, 0); + } else if (n < 0x10000) { + buf = new Buffer(1 + 2); + buf.writeUInt8(253, 0); + buf.writeUInt16LE(n, 1); + } else if (n < 0x100000000) { + buf = new Buffer(1 + 4); + buf.writeUInt8(254, 0); + buf.writeUInt32LE(n, 1); + } else { + buf = new Buffer(1 + 8); + buf.writeUInt8(255, 0); + buf.writeInt32LE(n & -1, 1); + buf.writeUInt32LE(Math.floor(n / 0x100000000), 5); + } + return buf; +}; + +module.exports = BufferWriter; diff --git a/test/test.bufferwriter.js b/test/test.bufferwriter.js new file mode 100644 index 0000000..05be1f5 --- /dev/null +++ b/test/test.bufferwriter.js @@ -0,0 +1,124 @@ +var BufferWriter = require('../lib/bufferwriter'); +var should = require('chai').should(); + +describe('BufferWriter', function() { + + it('should create a new buffer writer', function() { + var bw = new BufferWriter(); + should.exist(bw); + }); + + describe('#concat', function() { + + it('should concat these two bufs', function() { + var buf1 = new Buffer([0]); + var buf2 = new Buffer([1]); + var bw = new BufferWriter([buf1, buf2]); + bw.concat().toString('hex').should.equal('0001'); + }); + + }); + + describe('#write', function() { + + it('should write a buffer', function() { + var buf = new Buffer([0]); + var bw = new BufferWriter(); + bw.write(buf); + bw.concat().toString('hex').should.equal('00'); + }); + + }); + + describe('#writeUInt8', function() { + + it('should write 1', function() { + var bw = new BufferWriter(); + bw.writeUInt8(1).concat().toString('hex').should.equal('01'); + }); + + }); + + describe('#writeUInt16BE', function() { + + it('should write 1', function() { + var bw = new BufferWriter(); + bw.writeUInt16BE(1).concat().toString('hex').should.equal('0001'); + }); + + }); + + describe('#writeUInt16LE', function() { + + it('should write 1', function() { + var bw = new BufferWriter(); + bw.writeUInt16LE(1).concat().toString('hex').should.equal('0100'); + }); + + }); + + describe('#writeUInt32BE', function() { + + it('should write 1', function() { + var bw = new BufferWriter(); + bw.writeUInt32BE(1).concat().toString('hex').should.equal('00000001'); + }); + + }); + + describe('#writeUInt32LE', function() { + + it('should write 1', function() { + var bw = new BufferWriter(); + bw.writeUInt32LE(1).concat().toString('hex').should.equal('01000000'); + }); + + }); + + describe('#writeUInt64BE', function() { + + it('should write 1', function() { + var bw = new BufferWriter(); + bw.writeUInt64BE(1).concat().toString('hex').should.equal('0000000000000001'); + }); + + }); + + describe('#writeUInt64LE', function() { + + it('should write 1', function() { + var bw = new BufferWriter(); + bw.writeUInt64LE(1).concat().toString('hex').should.equal('0100000000000000'); + }); + + }); + + describe('#writeVarInt', function() { + + it('should write a 1 byte varInt', function() { + var bw = new BufferWriter(); + bw.writeVarInt(1); + bw.concat().length.should.equal(1); + }); + + it('should write a 3 byte varInt', function() { + var bw = new BufferWriter(); + bw.writeVarInt(1000); + bw.concat().length.should.equal(3); + }); + + it('should write a 5 byte varInt', function() { + var bw = new BufferWriter(); + bw.writeVarInt(Math.pow(2, 16 + 1)); + bw.concat().length.should.equal(5); + }); + + it('should write a 9 byte varInt', function() { + var bw = new BufferWriter(); + bw.writeVarInt(Math.pow(2, 32 + 1)); + bw.concat().length.should.equal(9); + }); + + }); + +}); From 337b19849c7db5f0c85d6b7bc26a1bb221a9be99 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 19 Aug 2014 09:41:46 -0700 Subject: [PATCH 049/280] fork bitcore from privsec --- LICENSE.md | 4 ++-- README.md | 4 ++-- index.js | 64 ++++++++++++++++++++++++++-------------------------- package.json | 4 ++-- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index c0e6e35..bff264c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,8 +1,8 @@ This software is licensed under the MIT License. -Copyright Ryan X. Charles, 2014. +Copyright BitPay, 2014. -Portions of this software are Copyright BitPay, 2014. +Portions of this software are Copyright Ryan X. Charles, 2014. Portions of this software are Copyright Stefan Thomas, 2011. diff --git a/README.md b/README.md index e5bdb7e..a7662e0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -privsec +bitcore ======= -Bitcoin privacy and security. +Bitcoin library. diff --git a/index.js b/index.js index 6a28bce..867c47f 100644 --- a/index.js +++ b/index.js @@ -1,40 +1,40 @@ -var privsec = module.exports; +var bitcore = module.exports; //main bitcoin library -privsec.Address = require('./lib/address'); -privsec.Base58 = require('./lib/base58'); -privsec.Base58Check = require('./lib/base58check'); -privsec.BIP32 = require('./lib/bip32'); -privsec.BN = require('./lib/bn'); -privsec.BufferReader = require('./lib/bufferreader'); -privsec.BufferWriter = require('./lib/bufferwriter'); -privsec.Constants = require('./lib/constants'); -privsec.ECDSA = require('./lib/ecdsa'); -privsec.Hash = require('./lib/hash'); -privsec.KDF = require('./lib/kdf'); -privsec.Key = require('./lib/key'); -privsec.Point = require('./lib/point'); -privsec.Privkey = require('./lib/privkey'); -privsec.Pubkey = require('./lib/pubkey'); -privsec.Random = require('./lib/random'); -privsec.Signature = require('./lib/signature'); +bitcore.Address = require('./lib/address'); +bitcore.Base58 = require('./lib/base58'); +bitcore.Base58Check = require('./lib/base58check'); +bitcore.BIP32 = require('./lib/bip32'); +bitcore.BN = require('./lib/bn'); +bitcore.BufferReader = require('./lib/bufferreader'); +bitcore.BufferWriter = require('./lib/bufferwriter'); +bitcore.Constants = require('./lib/constants'); +bitcore.ECDSA = require('./lib/ecdsa'); +bitcore.Hash = require('./lib/hash'); +bitcore.KDF = require('./lib/kdf'); +bitcore.Key = require('./lib/key'); +bitcore.Point = require('./lib/point'); +bitcore.Privkey = require('./lib/privkey'); +bitcore.Pubkey = require('./lib/pubkey'); +bitcore.Random = require('./lib/random'); +bitcore.Signature = require('./lib/signature'); //experimental, nonstandard, or unstable features -privsec.expmt = {}; -privsec.expmt.Stealth = require('./lib/expmt/stealth'); +bitcore.expmt = {}; +bitcore.expmt.Stealth = require('./lib/expmt/stealth'); //dependencies, subject to change -privsec.deps = {}; -privsec.deps.bnjs = require('bn.js'); -privsec.deps.bs58 = require('bs58'); -privsec.deps.Buffer = Buffer; -privsec.deps.elliptic = require('elliptic'); -privsec.deps.hashjs = require('hash.js'); -privsec.deps.sha512 = require('sha512'); +bitcore.deps = {}; +bitcore.deps.bnjs = require('bn.js'); +bitcore.deps.bs58 = require('bs58'); +bitcore.deps.Buffer = Buffer; +bitcore.deps.elliptic = require('elliptic'); +bitcore.deps.hashjs = require('hash.js'); +bitcore.deps.sha512 = require('sha512'); -//privsec.script = require('lib/script'); -//privsec.scriptexec = require('lib/scriptexec'); -//privsec.tx = require('lib/tx'); -//privsec.txpartial = require('lib/txpartial'); +//bitcore.script = require('lib/script'); +//bitcore.scriptexec = require('lib/scriptexec'); +//bitcore.tx = require('lib/tx'); +//bitcore.txpartial = require('lib/txpartial'); -//privsec.bip70 = require('lib/bip70'); +//bitcore.bip70 = require('lib/bip70'); diff --git a/package.json b/package.json index f856dc0..2f62f64 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "privsec", + "name": "bitcore", "version": "0.0.0", - "description": "Bitcoin privacy and security.", + "description": "Bitcoin library.", "main": "index.js", "scripts": { "test": "mocha" From da296abb900ef7dffecea057b19b6921a891611a Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 19 Aug 2014 10:18:38 -0700 Subject: [PATCH 050/280] make LICENSE compatible with bitcore1 --- LICENSE.md | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index bff264c..d6d6d13 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,13 +1,30 @@ This software is licensed under the MIT License. -Copyright BitPay, 2014. +Copyright (c) BitPay, 2014. -Portions of this software are Copyright Ryan X. Charles, 2014. +Portions of this software are based on privsec +Copyright (c) Ryan X. Charles, 2014. -Portions of this software are Copyright Stefan Thomas, 2011. +Parts of this software are based on BitcoinJS +Copyright (c) 2011 Stefan Thomas -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Parts of this software are based on BitcoinJ +Copyright (c) 2011 Google Inc. -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. From 1550d5ec83e7651f609254eb5316667d09485512 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 19 Aug 2014 10:27:32 -0700 Subject: [PATCH 051/280] add basic info about what this library is to README --- README.md | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a7662e0..b557c8b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,31 @@ -bitcore -======= +bitcore2 +======== -Bitcoin library. +Bitcore 2 is a rewrite of bitcore to satisfy several goals: + +1) Support ease-of-use by being internally consistent. It should not be +necessary to read the source code of a class or function to know how to use it. +I.e., where in old bitcore a "privkey" might be anything from a buffer to a hex +string to a key to a private key object, in bitcore 2 "privkey" is the same +type of object always and everywhere. + +2) Have 100% test coverage so that the library is known to be reliable. + +3) Library objects have an interface suitable for use with a command-line +interface and API, in particular having toString, fromString, toObject, +fromObject methods. + +4) All standard features of the bitcoin protocol are implemented and saved in +lib/. All BIPs are correctly implemented and saved as BIPxx.js in lib/ (since +that is their standard name). Any non-standard features (such as SINs and +stealtha addresses) are placed in the lib/expmt/ folder and are accessible at +bitcore.expmt. Once they are standardized and given a BIP, they are renamed and +placed in lib/. + +5) It is always possible to create a new object without using "new". + +6) Compatible with browserify (i.e., using require('bitcore/lib/message') +should work both in node, and be automatically work in the browser with used in +conjunction with browserify). + +7) Minimize the use of dependencies so that all code can be easily audited. From 7fe333d367f2e220f2b33db3429e016caa42794d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 19 Aug 2014 10:27:47 -0700 Subject: [PATCH 052/280] temporarily rename to bitcore2 ...to distinguish from old bitcore. Will rename once we merge it into the main branch. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2f62f64..71685fc 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "bitcore", + "name": "bitcore2", "version": "0.0.0", "description": "Bitcoin library.", "main": "index.js", From 8fad4087f8fe1410144ec1a9368acc4d2486ec5f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 19 Aug 2014 11:57:15 -0700 Subject: [PATCH 053/280] refine copyright --- LICENSE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index d6d6d13..19004df 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,9 +1,9 @@ This software is licensed under the MIT License. -Copyright (c) BitPay, 2014. +Copyright (c) 2014 BitPay Inc. -Portions of this software are based on privsec -Copyright (c) Ryan X. Charles, 2014. +Parts of this software are based on privsec +Copyright (c) 2014 Ryan X. Charles Parts of this software are based on BitcoinJS Copyright (c) 2011 Stefan Thomas From 39236fab141b5e529200268b9da2811c206daf75 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 19 Aug 2014 12:02:34 -0700 Subject: [PATCH 054/280] formatting --- lib/signature.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/signature.js b/lib/signature.js index 8d911a1..5dcf3b4 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -126,4 +126,5 @@ Signature.prototype.toString = function() { var buf = this.toDER(); return buf.toString('hex'); }; + module.exports = Signature; From 65c3545cb64de6654124dfc9d759de14b2873158 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 19 Aug 2014 16:27:28 -0700 Subject: [PATCH 055/280] include i in sig obj + cosmetic improvements --- lib/signature.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/signature.js b/lib/signature.js index 5dcf3b4..f87215e 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -1,26 +1,31 @@ -var bn = require('./bn'); +var BN = require('./bn'); +var Point = require('./point'); +var Pubkey = require('./pubkey'); -var Signature = function Signature(r, s) { +var Signature = function Signature(r, s, i) { if (!(this instanceof Signature)) - return new Signature(r, s); + return new Signature(r, s, i); + this.r = r; this.s = s; + this.i = i; //public key recovery parameter in range [0, 3] }; Signature.prototype.fromCompressed = function(buf) { - var b1 = buf.slice(0, 1)[0]; + var i = buf.slice(0, 1)[0]; var b2 = buf.slice(1, 33); var b3 = buf.slice(33, 65); - if (!(b1 === 0 || b1 === 1 || b1 === 2 || b1 === 3)) + if (!(i === 0 || i === 1 || i === 2 || i === 3)) throw new Error('signature: i must be 0, 1, 2, or 3'); if (b2.length !== 32) throw new Error('signature: r must be 32 bytes'); if (b3.length !== 32) throw new Error('signature: s must be 32 bytes'); - this.r = bn.fromBuffer(b2); - this.s = bn.fromBuffer(b3); + this.i = i; + this.r = BN().fromBuffer(b2); + this.s = BN().fromBuffer(b3); }; Signature.prototype.fromDER = function(buf) { @@ -53,7 +58,7 @@ Signature.parseDER = function(buf) { var rlength = buf[2 + 1]; var rbuf = buf.slice(2 + 2, 2 + 2 + rlength); - var r = bn.fromBuffer(rbuf); + var r = BN().fromBuffer(rbuf); var rneg = buf[2 + 1 + 1] === 0x00 ? true : false; if (rlength !== rbuf.length) throw new Error('signature: Length of r incorrect'); @@ -64,7 +69,7 @@ Signature.parseDER = function(buf) { var slength = buf[2 + 2 + rlength + 1]; var sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength); - var s = bn.fromBuffer(sbuf); + var s = BN().fromBuffer(sbuf); var sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00 ? true : false; if (slength !== sbuf.length) throw new Error('signature: Length of s incorrect'); @@ -92,7 +97,8 @@ Signature.parseDER = function(buf) { }; Signature.prototype.toCompressed = function(i) { - if (!(i === 0 || i === 1 || i ===2 || i ===3)) + i = typeof i === 'number' ? i : this.i; + if (!(i === 0 || i === 1 || i === 2 || i === 3)) throw new Error('signature: i must be equal to 0, 1, 2, or 3'); var b1 = new Buffer([i]); From ca7fdd77c13edca700ab51c8c926f10da73490ca Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 19 Aug 2014 17:15:54 -0700 Subject: [PATCH 056/280] recover public key from signature --- lib/ecdsa.js | 96 ++++++++++++++++++++++++++++++++++++++-------- test/test.ecdsa.js | 55 ++++++++++++++++++++++---- 2 files changed, 127 insertions(+), 24 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 38b0ce9..552cd7f 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -1,5 +1,5 @@ -var bn = require('./bn'); -var point = require('./point'); +var BN = require('./bn'); +var Point = require('./point'); var Signature = require('./signature'); var Key = require('./key'); var Privkey = require('./privkey'); @@ -15,29 +15,93 @@ var ECDSA = function ECDSA(hash, key, sig, k) { this.k = k; }; +ECDSA.prototype.calci = function() { + for (var i = 0; i < 4; i++) { + this.sig.i = i; + try { + var Qprime = this.sig2pubkey(); + } catch (e) { + console.log(e); + continue; + } + + if (Qprime.point.eq(this.key.pubkey.point)) { + return this; + } + } + + this.sig.i = undefined; + throw new Error('Unable to find valid recovery factor'); +}; + ECDSA.prototype.fromString = function(str) { var obj = JSON.parse(str); if (obj.hash) this.hash = new Buffer(obj.hash, 'hex'); if (obj.key) - this.key = (new Key()).fromString(obj.key); + this.key = Key().fromString(obj.key); if (obj.sig) - this.sig = (new Signature()).fromString(obj.sig); + this.sig = Signature().fromString(obj.sig); if (obj.k) - this.k = bn(obj.k, 10); + this.k = BN(obj.k, 10); return this; }; ECDSA.prototype.randomK = function() { - var N = point.getN(); + var N = Point.getN(); var k; do { - k = bn.fromBuffer(Random.getRandomBuffer(32)); + k = BN().fromBuffer(Random.getRandomBuffer(32)); } while (!(k.lt(N) && k.gt(0))); this.k = k; return this; }; +ECDSA.prototype.sig2pubkey = function() { + var i = this.sig.i; + if (!(i === 0 || i === 1 || i === 2 || i === 3)) + throw new Error('signature: i must be equal to 0, 1, 2, or 3'); + + var e = BN().fromBuffer(this.hash); + var r = this.sig.r; + var s = this.sig.s; + + // A set LSB signifies that the y-coordinate is odd + var isYOdd = i & 1; + + // The more significant bit specifies whether we should use the + // first or second candidate key. + var isSecondKey = i >> 1; + + var n = Point.getN(); + var G = Point.getG(); + + // 1.1 Let x = r + jn + var x = isSecondKey ? r.add(n) : r; + var R = Point.fromX(isYOdd, x); + + // 1.4 Check that nR is at infinity + var nR = R.mul(n); + + if (!nR.isInfinity()) + throw new Error('nR is not a valid curve point'); + + // Compute -e from e + var eNeg = e.neg().mod(n); + + // 1.6.1 Compute Q = r^-1 (sR - eG) + // Q = r^-1 (sR + -eG) + var rInv = r.invm(n); + + //var Q = R.multiplyTwo(s, G, eNeg).mul(rInv); + var Q = R.mul(s).add(G.mul(eNeg)).mul(rInv); + + var pubkey = new Pubkey(Q); + pubkey.validate(); + + return pubkey; +}; + ECDSA.prototype.sigError = function() { if (!Buffer.isBuffer(this.hash) || this.hash.length !== 32) return 'Invalid hash'; @@ -46,21 +110,21 @@ ECDSA.prototype.sigError = function() { this.key.pubkey.validate(); } catch (e) { return 'Invalid pubkey: ' + e; - }; + } var r = this.sig.r; var s = this.sig.s; - if (!(r.gt(0) && r.lt(point.getN())) - || !(s.gt(0) && s.lt(point.getN()))) + if (!(r.gt(0) && r.lt(Point.getN())) + || !(s.gt(0) && s.lt(Point.getN()))) return 'r and s not in range'; - var e = bn.fromBuffer(this.hash); - var n = point.getN(); + var e = BN().fromBuffer(this.hash); + var n = Point.getN(); var sinv = s.invm(n); var u1 = sinv.mul(e).mod(n); var u2 = sinv.mul(r).mod(n); - var p = point.getG().mulAdd(u1, this.key.pubkey.point, u2); + var p = Point.getG().mulAdd(u1, this.key.pubkey.point, u2); if (p.isInfinity()) return 'p is infinity'; @@ -79,9 +143,9 @@ ECDSA.prototype.sign = function() { if (!hash || !privkey || !k || !d) throw new Error('ecdsa: invalid parameters'); - var N = point.getN(); - var G = point.getG(); - var e = bn(hash); + var N = Point.getN(); + var G = Point.getG(); + var e = BN().fromBuffer(hash); do { var Q = G.mul(k); diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index f7db604..0a623a6 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -4,7 +4,7 @@ var Key = require('../lib/key'); var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); var Signature = require('../lib/signature'); -var bn = require('../lib/bn'); +var BN = require('../lib/bn'); var point = require('../lib/point'); var should = require('chai').should(); @@ -17,9 +17,36 @@ describe("ECDSA", function() { var ecdsa = new ECDSA(); ecdsa.hash = Hash.sha256(new Buffer('test data')); ecdsa.key = new Key(); - ecdsa.key.privkey = new Privkey(bn.fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))); - ecdsa.key.pubkey = new Pubkey(point(bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), - bn.fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))); + ecdsa.key.privkey = new Privkey(BN.fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))); + ecdsa.key.pubkey = new Pubkey(point(BN.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), + BN.fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))); + + describe('#calci', function() { + + it('should calculate i', function() { + ecdsa.randomK(); + ecdsa.sign(); + ecdsa.calci(); + should.exist(ecdsa.sig.i); + }); + + it('should calulate this known i', function() { + var hash = Hash.sha256(new Buffer('some data')); + var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10); + var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10); + var ecdsa = new ECDSA(); + ecdsa.key = new Key(); + ecdsa.key.privkey = Privkey(); + ecdsa.key.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test'))); + ecdsa.key.privkey2pubkey(); + ecdsa.hash = hash; + ecdsa.sig = new Signature(r, s); + + ecdsa.calci(); + ecdsa.sig.i.should.equal(1); + }); + + }); describe('#fromString', function() { @@ -46,12 +73,24 @@ describe("ECDSA", function() { it('should generate a random k that is (almost always) greater than this relatively small number', function() { ecdsa.randomK(); var k1 = ecdsa.k; - var k2 = bn(Math.pow(2, 32)).mul(bn(Math.pow(2, 32))).mul(bn(Math.pow(2, 32))); + var k2 = BN(Math.pow(2, 32)).mul(BN(Math.pow(2, 32))).mul(BN(Math.pow(2, 32))); k2.gt(k1).should.equal(false); }); }); + describe('#sig2pubkey', function() { + + it('should calculate the correct public key', function() { + ecdsa.k = BN('114860389168127852803919605627759231199925249596762615988727970217268189974335', 10); + ecdsa.sign(); + ecdsa.sig.i = 1; + var pubkey = ecdsa.sig2pubkey(); + pubkey.point.eq(ecdsa.key.pubkey.point).should.equal(true); + }); + + }); + describe('#sigError', function() { it('should return an error if the hash is invalid', function() { @@ -73,15 +112,15 @@ describe("ECDSA", function() { ecdsa.key = new Key(); ecdsa.key.pubkey = pk; ecdsa.sig = new Signature(); - ecdsa.sig.r = bn(0); - ecdsa.sig.s = bn(0); + ecdsa.sig.r = BN(0); + ecdsa.sig.s = BN(0); ecdsa.sigError().should.equal("r and s not in range"); }); it('should return an error if the signature is incorrect', function() { ecdsa.sig = new Signature(); ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); - ecdsa.sig.r = ecdsa.sig.r.add(bn(1)); + ecdsa.sig.r = ecdsa.sig.r.add(BN(1)); ecdsa.sigError().should.equal("Invalid signature"); }); From 3e82c57e1975c79b15ec6393724f362f0accae14 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 20 Aug 2014 10:46:01 -0700 Subject: [PATCH 057/280] "hashbuf" indicates type is a buffer --- lib/ecdsa.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 552cd7f..8debeb8 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -6,10 +6,10 @@ var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var Random = require('./random'); -var ECDSA = function ECDSA(hash, key, sig, k) { +var ECDSA = function ECDSA(hashbuf, key, sig, k) { if (!(this instanceof ECDSA)) - return new ECDSA(hash, key, sig, k); - this.hash = hash; + return new ECDSA(hashbuf, key, sig, k); + this.hashbuf = hashbuf; this.key = key; this.sig = sig; this.k = k; @@ -36,8 +36,8 @@ ECDSA.prototype.calci = function() { ECDSA.prototype.fromString = function(str) { var obj = JSON.parse(str); - if (obj.hash) - this.hash = new Buffer(obj.hash, 'hex'); + if (obj.hashbuf) + this.hashbuf = new Buffer(obj.hashbuf, 'hex'); if (obj.key) this.key = Key().fromString(obj.key); if (obj.sig) @@ -62,7 +62,7 @@ ECDSA.prototype.sig2pubkey = function() { if (!(i === 0 || i === 1 || i === 2 || i === 3)) throw new Error('signature: i must be equal to 0, 1, 2, or 3'); - var e = BN().fromBuffer(this.hash); + var e = BN().fromBuffer(this.hashbuf); var r = this.sig.r; var s = this.sig.s; @@ -103,7 +103,7 @@ ECDSA.prototype.sig2pubkey = function() { }; ECDSA.prototype.sigError = function() { - if (!Buffer.isBuffer(this.hash) || this.hash.length !== 32) + if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 32) return 'Invalid hash'; try { @@ -118,7 +118,7 @@ ECDSA.prototype.sigError = function() { || !(s.gt(0) && s.lt(Point.getN()))) return 'r and s not in range'; - var e = BN().fromBuffer(this.hash); + var e = BN().fromBuffer(this.hashbuf); var n = Point.getN(); var sinv = s.invm(n); var u1 = sinv.mul(e).mod(n); @@ -135,17 +135,17 @@ ECDSA.prototype.sigError = function() { }; ECDSA.prototype.sign = function() { - var hash = this.hash; + var hashbuf = this.hashbuf; var privkey = this.key.privkey; var k = this.k; var d = privkey.bn; - if (!hash || !privkey || !k || !d) + if (!hashbuf || !privkey || !k || !d) throw new Error('ecdsa: invalid parameters'); var N = Point.getN(); var G = Point.getG(); - var e = BN().fromBuffer(hash); + var e = BN().fromBuffer(hashbuf); do { var Q = G.mul(k); @@ -164,8 +164,8 @@ ECDSA.prototype.signRandomK = function() { ECDSA.prototype.toString = function() { var obj = {}; - if (this.hash) - obj.hash = this.hash.toString('hex'); + if (this.hashbuf) + obj.hashbuf = this.hashbuf.toString('hex'); if (this.key) obj.key = this.key.toString(); if (this.sig) From 8ce3342b6cd837031ec8721bc54f402da9ab83b2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 20 Aug 2014 10:52:26 -0700 Subject: [PATCH 058/280] hash -> hashbuf --- test/test.ecdsa.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index 0a623a6..d1fbc99 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -15,7 +15,7 @@ describe("ECDSA", function() { }); var ecdsa = new ECDSA(); - ecdsa.hash = Hash.sha256(new Buffer('test data')); + ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); ecdsa.key = new Key(); ecdsa.key.privkey = new Privkey(BN.fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))); ecdsa.key.pubkey = new Pubkey(point(BN.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), @@ -31,7 +31,7 @@ describe("ECDSA", function() { }); it('should calulate this known i', function() { - var hash = Hash.sha256(new Buffer('some data')); + var hashbuf = Hash.sha256(new Buffer('some data')); var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10); var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10); var ecdsa = new ECDSA(); @@ -39,7 +39,7 @@ describe("ECDSA", function() { ecdsa.key.privkey = Privkey(); ecdsa.key.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test'))); ecdsa.key.privkey2pubkey(); - ecdsa.hash = hash; + ecdsa.hashbuf = hashbuf; ecdsa.sig = new Signature(r, s); ecdsa.calci(); @@ -54,7 +54,7 @@ describe("ECDSA", function() { var str = ecdsa.toString(); var ecdsa2 = new ECDSA(); ecdsa2.fromString(str); - should.exist(ecdsa.hash); + should.exist(ecdsa.hashbuf); should.exist(ecdsa.key); }); @@ -100,13 +100,13 @@ describe("ECDSA", function() { it('should return an error if the pubkey is invalid', function() { var ecdsa = new ECDSA(); - ecdsa.hash = Hash.sha256(new Buffer('test')); + ecdsa.hashbuf = Hash.sha256(new Buffer('test')); ecdsa.sigError().indexOf("Invalid pubkey").should.equal(0); }); it('should return an error if r, s are invalid', function() { var ecdsa = new ECDSA(); - ecdsa.hash = Hash.sha256(new Buffer('test')); + ecdsa.hashbuf = Hash.sha256(new Buffer('test')); var pk = new Pubkey(); pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); ecdsa.key = new Key(); From 79ba8b246565ff223d5574a532c606be5e907495 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 20 Aug 2014 10:54:39 -0700 Subject: [PATCH 059/280] BN -> BN() --- test/test.ecdsa.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index d1fbc99..76886f4 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -17,9 +17,9 @@ describe("ECDSA", function() { var ecdsa = new ECDSA(); ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); ecdsa.key = new Key(); - ecdsa.key.privkey = new Privkey(BN.fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))); - ecdsa.key.pubkey = new Pubkey(point(BN.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), - BN.fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))); + ecdsa.key.privkey = new Privkey(BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))); + ecdsa.key.pubkey = new Pubkey(point(BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), + BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))); describe('#calci', function() { From c22476d809e9e390d9764eab40bbc61e4e9cad5c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 20 Aug 2014 10:54:58 -0700 Subject: [PATCH 060/280] bn -> BN, BN -> BN() --- test/test.point.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test.point.js b/test/test.point.js index 583d709..15619c1 100644 --- a/test/test.point.js +++ b/test/test.point.js @@ -1,6 +1,6 @@ var should = require('chai').should(); var point = require('../lib/point'); -var bn = require('../lib/bn'); +var BN = require('../lib/bn'); describe('Point', function() { @@ -58,7 +58,7 @@ describe('Point', function() { it('should accurately multiply g by 2', function() { var g = point.getG(); - var b = g.mul(bn(2)); + var b = g.mul(BN(2)); b.getX().toString().should.equal('89565891926547004231252920425935692360644145829622209833684329913297188986597'); b.getY().toString().should.equal('12158399299693830322967808612713398636155367887041628176798871954788371653930'); }); @@ -97,15 +97,15 @@ describe('Point', function() { describe('#validate', function() { it('should validate this valid point', function() { - var x = bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')); - var y = bn.fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')); + var x = BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')); + var y = BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')); var p = point(x, y); should.exist(p.validate()); }); it('should invalidate this invalid point', function() { - var x = bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')); - var y = bn.fromBuffer(new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')); + var x = BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')); + var y = BN().fromBuffer(new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')); var p = point(x, y); (function() { p.validate(); From cde44d689c3b8357f2e7a59ceb216687d293746f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 20 Aug 2014 12:35:55 -0700 Subject: [PATCH 061/280] fix precision error by handling BNs correctly --- lib/bufferreader.js | 41 ++++++++++++++++++++++------------- test/test.bufferreader.js | 45 ++++++++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index fc06b17..afee08f 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -1,3 +1,5 @@ +var BN = require('./bn'); + var BufferReader = function BufferReader(buf, pos) { if (!(this instanceof BufferReader)) return new BufferReader(buf); @@ -45,24 +47,19 @@ BufferReader.prototype.readUInt32LE = function() { return val; }; -//TODO: What if n is so large that it loses precision? -BufferReader.prototype.readUInt64BE = function() { - var val = 0; - for (var i = 0; i < 8; i++) { - val += Math.pow(256, i) * this.buf[this.pos + 8 - 1 - i]; - } +BufferReader.prototype.readUInt64BEBN = function() { + var buf = this.buf.slice(this.pos, this.pos + 8); + var bn = BN().fromBuffer(buf); this.pos = this.pos + 8; - return val; + return bn; }; -//TODO: What if n is so large that it loses precision? -BufferReader.prototype.readUInt64LE = function() { - var val = 0; - for (var i = 0; i < 8; i++) { - val += Math.pow(256, i) * this.buf[this.pos + i]; - } +BufferReader.prototype.readUInt64LEBN = function() { + var buf = this.buf.slice(this.pos, this.pos + 8); + var reversebuf = BufferReader(buf).reverse().read(); + var bn = BN().fromBuffer(reversebuf); this.pos = this.pos + 8; - return val; + return bn; }; BufferReader.prototype.readVarInt = function() { @@ -73,12 +70,26 @@ BufferReader.prototype.readVarInt = function() { case 0xFE: return this.readUInt32LE(); case 0xFF: - return this.readUInt64LE(); + return this.readUInt64LEBN().toNumber(); default: return first; } }; +BufferReader.prototype.readVarIntBN = function() { + var first = this.readUInt8(); + switch (first) { + case 0xFD: + return BN(this.readUInt16LE()); + case 0xFE: + return BN(this.readUInt32LE()); + case 0xFF: + return this.readUInt64LEBN(); + default: + return BN(first); + } +}; + BufferReader.prototype.reverse = function() { var buf = new Buffer(this.buf.length); for (var i = 0; i < buf.length; i++) diff --git a/test/test.bufferreader.js b/test/test.bufferreader.js index bca7b63..f305ecd 100644 --- a/test/test.bufferreader.js +++ b/test/test.bufferreader.js @@ -82,33 +82,33 @@ describe('BufferReader', function() { }); - describe('#readUInt64BE', function() { + describe('#readUInt64BEBN', function() { it('should return 1', function() { var buf = new Buffer(8); buf.fill(0); buf.writeUInt32BE(1, 4); var br = new BufferReader(buf); - br.readUInt64BE().should.equal(1); + br.readUInt64BEBN().toNumber().should.equal(1); }); it('should return 2^64', function() { var buf = new Buffer(8); buf.fill(0xff); var br = new BufferReader(buf); - br.readUInt64BE().should.equal(Math.pow(2, 64)); + br.readUInt64BEBN().toNumber().should.equal(Math.pow(2, 64)); }); }); - describe('#readUInt64LE', function() { + describe('#readUInt64LEBN', function() { it('should return 1', function() { var buf = new Buffer(8); buf.fill(0); buf.writeUInt32LE(1, 0); var br = new BufferReader(buf); - br.readUInt64LE().should.equal(1); + br.readUInt64LEBN().toNumber().should.equal(1); }); it('should return 2^30', function() { @@ -116,21 +116,21 @@ describe('BufferReader', function() { buf.fill(0); buf.writeUInt32LE(Math.pow(2, 30), 0); var br = new BufferReader(buf); - br.readUInt64LE().should.equal(Math.pow(2, 30)); + br.readUInt64LEBN().toNumber().should.equal(Math.pow(2, 30)); }); it('should return 0', function() { var buf = new Buffer(8); buf.fill(0); var br = new BufferReader(buf); - br.readUInt64LE().should.equal(0); + br.readUInt64LEBN().toNumber().should.equal(0); }); it('should return 2^64', function() { var buf = new Buffer(8); buf.fill(0xff); var br = new BufferReader(buf); - br.readUInt64LE().should.equal(Math.pow(2, 64)); + br.readUInt64LEBN().toNumber().should.equal(Math.pow(2, 64)); }); }); @@ -164,6 +164,35 @@ describe('BufferReader', function() { }); + describe('#readVarIntBN', function() { + + it('should read a 1 byte varint', function() { + var buf = new Buffer([50]); + var br = new BufferReader(buf); + br.readVarIntBN().toNumber().should.equal(50); + }); + + it('should read a 3 byte varint', function() { + var buf = new Buffer([253, 253, 0]); + var br = new BufferReader(buf); + br.readVarIntBN().toNumber().should.equal(253); + }); + + it('should read a 5 byte varint', function() { + var buf = new Buffer([254, 0, 0, 0, 0]); + buf.writeUInt32LE(50000, 1); + var br = new BufferReader(buf); + br.readVarIntBN().toNumber().should.equal(50000); + }); + + it('should read a 9 byte varint', function() { + var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]); + var br = new BufferReader(buf); + br.readVarIntBN().toNumber().should.equal(Math.pow(2, 64)); + }); + + }); + describe('#reverse', function() { it('should reverse this [0, 1]', function() { From 912bed1d9c590a967dd5d59df76a266abe65a70f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 20 Aug 2014 12:52:37 -0700 Subject: [PATCH 062/280] support BN in bufferwriter So that the precision of writing a variable sized integer is sufficient. --- lib/bufferwriter.js | 47 ++++++++++++++++++++++++++++++--------- test/test.bufferwriter.js | 37 ++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/lib/bufferwriter.js b/lib/bufferwriter.js index 2082aed..babbfe9 100644 --- a/lib/bufferwriter.js +++ b/lib/bufferwriter.js @@ -1,3 +1,5 @@ +var BN = require('./bn'); + var BufferWriter = function BufferWriter(bufs) { if (!(this instanceof BufferWriter)) return new BufferReader(buf); @@ -48,21 +50,17 @@ BufferWriter.prototype.writeUInt32LE = function(n) { return this; }; -//TODO: What if n is so large that it loses precision? -BufferWriter.prototype.writeUInt64BE = function(n) { - var buf = new Buffer(8); - buf.writeInt32BE(n & -1, 4); - buf.writeUInt32BE(Math.floor(n / 0x100000000), 0); +BufferWriter.prototype.writeUInt64BEBN = function(bn) { + var buf = bn.toBuffer({size: 8}); this.write(buf); return this; }; //TODO: What if n is so large that it loses precision? -BufferWriter.prototype.writeUInt64LE = function(n) { - var buf = new Buffer(8); - buf.writeInt32LE(n & -1, 0); - buf.writeUInt32LE(Math.floor(n / 0x100000000), 4); - this.write(buf); +BufferWriter.prototype.writeUInt64LEBN = function(bn) { + var buf = bn.toBuffer({size: 8}); + var reversebuf = new Buffer(Array.apply(new Array(), buf).reverse()); + this.write(reversebuf); return this; }; @@ -72,6 +70,12 @@ BufferWriter.prototype.writeVarInt = function(n) { return this; }; +BufferWriter.prototype.writeVarIntBN = function(bn) { + var buf = BufferWriter.varIntBufBN(bn); + this.write(buf); + return this; +}; + //TODO: What if n is so large that it loses precision? BufferWriter.varIntBuf = function(n) { var buf = undefined; @@ -95,4 +99,27 @@ BufferWriter.varIntBuf = function(n) { return buf; }; +BufferWriter.varIntBufBN = function(bn) { + var buf = undefined; + var n = bn.toNumber(); + if (n < 253) { + buf = new Buffer(1); + buf.writeUInt8(n, 0); + } else if (n < 0x10000) { + buf = new Buffer(1 + 2); + buf.writeUInt8(253, 0); + buf.writeUInt16LE(n, 1); + } else if (n < 0x100000000) { + buf = new Buffer(1 + 4); + buf.writeUInt8(254, 0); + buf.writeUInt32LE(n, 1); + } else { + var bw = new BufferWriter(); + bw.writeUInt8(255); + bw.writeUInt64LEBN(bn); + var buf = bw.concat(); + } + return buf; +}; + module.exports = BufferWriter; diff --git a/test/test.bufferwriter.js b/test/test.bufferwriter.js index 05be1f5..70d0305 100644 --- a/test/test.bufferwriter.js +++ b/test/test.bufferwriter.js @@ -1,4 +1,5 @@ var BufferWriter = require('../lib/bufferwriter'); +var BN = require('../lib/bn'); var should = require('chai').should(); describe('BufferWriter', function() { @@ -75,20 +76,20 @@ describe('BufferWriter', function() { }); - describe('#writeUInt64BE', function() { + describe('#writeUInt64BEBN', function() { it('should write 1', function() { var bw = new BufferWriter(); - bw.writeUInt64BE(1).concat().toString('hex').should.equal('0000000000000001'); + bw.writeUInt64BEBN(BN(1)).concat().toString('hex').should.equal('0000000000000001'); }); }); - describe('#writeUInt64LE', function() { + describe('#writeUInt64LEBN', function() { it('should write 1', function() { var bw = new BufferWriter(); - bw.writeUInt64LE(1).concat().toString('hex').should.equal('0100000000000000'); + bw.writeUInt64LEBN(BN(1)).concat().toString('hex').should.equal('0100000000000000'); }); }); @@ -121,4 +122,32 @@ describe('BufferWriter', function() { }); + describe('#writeVarIntBN', function() { + + it('should write a 1 byte varInt', function() { + var bw = new BufferWriter(); + bw.writeVarIntBN(BN(1)); + bw.concat().length.should.equal(1); + }); + + it('should write a 3 byte varInt', function() { + var bw = new BufferWriter(); + bw.writeVarIntBN(BN(1000)); + bw.concat().length.should.equal(3); + }); + + it('should write a 5 byte varInt', function() { + var bw = new BufferWriter(); + bw.writeVarIntBN(BN(Math.pow(2, 16 + 1))); + bw.concat().length.should.equal(5); + }); + + it('should write a 9 byte varInt', function() { + var bw = new BufferWriter(); + bw.writeVarIntBN(BN(Math.pow(2, 32 + 1))); + bw.concat().length.should.equal(9); + }); + + }); + }); From 4cff6a41f45b1522f33f497a961676e7aa4b1edb Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 20 Aug 2014 13:03:07 -0700 Subject: [PATCH 063/280] remove "(classname): " from tests ...to reduce the burden on writing new code --- lib/address.js | 8 ++++---- lib/base58check.js | 8 ++++---- lib/bip32.js | 30 +++++++++++++++--------------- lib/ecdsa.js | 2 +- lib/hash.js | 4 ++-- lib/point.js | 6 +++--- lib/privkey.js | 10 +++++----- lib/pubkey.js | 10 +++++----- lib/random.js | 4 ++-- lib/signature.js | 24 ++++++++++++------------ test/test.address.js | 4 ++-- test/test.base58check.js | 8 ++++---- test/test.point.js | 2 +- test/test.pubkey.js | 4 ++-- 14 files changed, 62 insertions(+), 62 deletions(-) diff --git a/lib/address.js b/lib/address.js index 35488bd..264b9ef 100644 --- a/lib/address.js +++ b/lib/address.js @@ -21,7 +21,7 @@ Address.prototype.fromPubkey = function(pubkey, network, compressed) { Address.prototype.fromString = function(str) { var buf = base58check.decode(str); if (buf.length !== 1 + 20) - throw new Error('address: Address buffers must be exactly 21 bytes'); + throw new Error('Address buffers must be exactly 21 bytes'); var version = buf[0]; if (version === constants['mainnet']['pubkeyhash']) { this.network = 'mainnet'; @@ -64,11 +64,11 @@ Address.prototype.toString = function() { 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'); + throw new Error('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"'); + throw new Error('network must be "mainnet" or "testnet"'); if (this.type !== 'pubkeyhash' && this.type !== 'p2sh') - throw new Error('address: type must be "pubkeyhash" or "p2sh"'); + throw new Error('type must be "pubkeyhash" or "p2sh"'); return this; }; diff --git a/lib/base58check.js b/lib/base58check.js index 273cdc3..d211c39 100644 --- a/lib/base58check.js +++ b/lib/base58check.js @@ -9,12 +9,12 @@ var Base58Check = function Base58Check(buf) { Base58Check.decode = function(s) { if (typeof s !== 'string') - throw new Error('Base58Check: Input must be a string'); + throw new Error('Input must be a string'); var buf = base58.decode(s); if (buf.length < 4) - throw new Error("Base58Check: Input string too short"); + throw new Error("Input string too short"); var data = buf.slice(0, -4); var csum = buf.slice(-4); @@ -23,14 +23,14 @@ Base58Check.decode = function(s) { var hash4 = hash.slice(0, 4); if (csum.toString('hex') !== hash4.toString('hex')) - throw new Error("Base58Check: Checksum mismatch"); + throw new Error("Checksum mismatch"); return data; }; Base58Check.encode = function(buf) { if (!Buffer.isBuffer(buf)) - throw new Error('Base58Check: Input must be a buffer'); + throw new Error('Input must be a buffer'); var checkedBuf = new Buffer(buf.length + 4); var hash = sha256sha256(buf); buf.copy(checkedBuf); diff --git a/lib/bip32.js b/lib/bip32.js index 9e20626..953fa57 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -37,14 +37,14 @@ BIP32.prototype.fromRandom = function(network) { BIP32.prototype.fromString = function(str) { var decoded = base58.decode(str); if (decoded.length != 82) - throw new Error('bip32: Not enough data, expected 82 and received ' + decoded.length); + throw new Error('gcNot enough data, expected 82 and received ' + decoded.length); var checksum = decoded.slice(78, 82); var bytes = decoded.slice(0, 78); var hash = Hash.sha256sha256(bytes); if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) - throw new Error('bip32: Invalid checksum'); + throw new Error('gcInvalid checksum'); if (bytes !== undefined && bytes !== null) this.initFromBytes(bytes); @@ -55,11 +55,11 @@ BIP32.prototype.fromSeed = function(bytes, network) { network = 'mainnet'; if (!Buffer.isBuffer(bytes)) - throw new Error('bip32: bytes must be a buffer'); + throw new Error('gcbytes must be a buffer'); if (bytes.length < 128 / 8) - throw new Error('bip32: Need more than 128 bytes of entropy'); + throw new Error('gcNeed more than 128 bytes of entropy'); if (bytes.length > 512 / 8) - throw new Error('bip32: More than 512 bytes of entropy is nonstandard'); + throw new Error('gcMore than 512 bytes of entropy is nonstandard'); var hash = Hash.sha512hmac(bytes, new Buffer('Bitcoin seed')); this.depth = 0x00; @@ -82,7 +82,7 @@ BIP32.prototype.fromSeed = function(bytes, network) { BIP32.prototype.initFromBytes = function(bytes) { // Both pub and private extended keys are 78 bytes if (bytes.length != 78) - throw new Error('bip32: not enough data'); + throw new Error('gcnot enough data'); this.version = u32(bytes.slice(0, 4)); this.depth = u8(bytes.slice(4, 5)); @@ -112,7 +112,7 @@ BIP32.prototype.initFromBytes = function(bytes) { this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); this.hasPrivateKey = false; } else { - throw new Error('bip32: Invalid key'); + throw new Error('gcInvalid key'); } this.buildExtendedPublicKey(); @@ -133,7 +133,7 @@ BIP32.prototype.buildExtendedPublicKey = function() { v = constants.testnet.bip32pubkey; break; default: - throw new Error('bip32: Unknown version'); + throw new Error('gcUnknown version'); } // Version @@ -164,7 +164,7 @@ BIP32.prototype.extendedPublicKeyString = function(format) { } else if (format === 'hex') { return this.extendedPublicKey.toString('hex');; } else { - throw new Error('bip32: bad format'); + throw new Error('gcbad format'); } } @@ -200,7 +200,7 @@ BIP32.prototype.extendedPrivateKeyString = function(format) { } else if (format === 'hex') { return this.extendedPrivateKey.toString('hex'); } else { - throw new Error('bip32: bad format'); + throw new Error('gcbad format'); } } @@ -217,12 +217,12 @@ BIP32.prototype.derive = function(path) { var c = e[i]; if (i == 0) { - if (c != 'm') throw new Error('bip32: invalid path'); + if (c != 'm') throw new Error('gcinvalid path'); continue; } if (parseInt(c.replace("'", "")).toString() !== c.replace("'", "")) - throw new Error('bip32: invalid path'); + throw new Error('gcinvalid path'); var usePrivate = (c.length > 1) && (c[c.length - 1] == '\''); var childIndex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c) & 0x7fffffff; @@ -238,7 +238,7 @@ BIP32.prototype.derive = function(path) { BIP32.prototype.deriveChild = function(i) { if (typeof i !== 'number') - throw new Error('bip32: i must be a number'); + throw new Error('gci must be a number'); var ib = []; ib.push((i >> 24) & 0xff); @@ -254,7 +254,7 @@ BIP32.prototype.deriveChild = function(i) { this.version == constants.testnet.bip32privkey); if (usePrivate && (!this.hasPrivateKey || !isPrivate)) - throw new Error('bip32: Cannot do private key derivation without private key'); + throw new Error('gcCannot do private key derivation without private key'); var ret = null; if (this.hasPrivateKey) { @@ -329,7 +329,7 @@ BIP32.prototype.toString = function() { function uint(f, size) { if (f.length < size) - throw new Error('bip32: not enough data'); + throw new Error('gcnot enough data'); var n = 0; for (var i = 0; i < size; i++) { n *= 256; diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 8debeb8..53959a3 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -141,7 +141,7 @@ ECDSA.prototype.sign = function() { var d = privkey.bn; if (!hashbuf || !privkey || !k || !d) - throw new Error('ecdsa: invalid parameters'); + throw new Error('invalid parameters'); var N = Point.getN(); var G = Point.getG(); diff --git a/lib/hash.js b/lib/hash.js index 3044074..d3251a9 100644 --- a/lib/hash.js +++ b/lib/hash.js @@ -46,12 +46,12 @@ Hash.sha512.blocksize = 1024; Hash.hmac = function(hashf, data, key) { if (!Buffer.isBuffer(data) || !Buffer.isBuffer(key)) - throw new Error('hash: data and key must be buffers'); + throw new Error('data and key must be buffers'); //http://en.wikipedia.org/wiki/Hash-based_message_authentication_code //http://tools.ietf.org/html/rfc4868#section-2 if (!hashf.blocksize) - throw new Error('hash: Blocksize for hash function unknown'); + throw new Error('Blocksize for hash function unknown'); var blocksize = hashf.blocksize/8; diff --git a/lib/point.js b/lib/point.js index 27b4b1f..25fd5c9 100644 --- a/lib/point.js +++ b/lib/point.js @@ -38,12 +38,12 @@ Point.prototype.getY = function() { Point.prototype.validate = function() { var p2 = Point.fromX(this.getY().isOdd(), this.getX()); if (!(p2.y.cmp(this.y) === 0)) - throw new Error('point: Invalid y value of public key'); + throw new Error('Invalid y value of public key'); if (!(this.getX().gt(-1) && this.getX().lt(Point.getN())) ||!(this.getY().gt(-1) && this.getY().lt(Point.getN()))) - throw new Error('point: Point does not lie on the curve'); + throw new Error('Point does not lie on the curve'); if (!(this.mul(Point.getN()).isInfinity())) - throw new Error('point: Point times N must be infinity'); + throw new Error('Point times N must be infinity'); return this; }; diff --git a/lib/privkey.js b/lib/privkey.js index bed0b23..4edaede 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -13,11 +13,11 @@ var Privkey = function Privkey(bn, network, compressed) { Privkey.prototype.validate = function() { if (!this.bn.lt(point.getN())) - throw new Error('privkey: Number must be less than N'); + throw new Error('Number must be less than N'); if (typeof constants[this.network] === undefined) - throw new Error('privkey: Must specify the network ("mainnet" or "testnet")'); + throw new Error('Must specify the network ("mainnet" or "testnet")'); if (typeof this.compressed !== 'boolean') - throw new Error('privkey: Must specify whether the corresponding public key is compressed or not (true or false)'); + throw new Error('Must specify whether the corresponding public key is compressed or not (true or false)'); }; Privkey.prototype.toWIF = function() { @@ -47,14 +47,14 @@ Privkey.prototype.fromWIF = function(str) { else if (buf.length === 1 + 32) this.compressed = false; else - throw new Error('privkey: Length of buffer must be 33 (uncompressed) or 34 (compressed)'); + throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)'); if (buf[0] === constants.mainnet.privkey) this.network = 'mainnet'; else if (buf[0] === constants.testnet.privkey) this.network = 'testnet'; else - throw new Error('privkey: Invalid network'); + throw new Error('Invalid network'); this.bn = Bn.fromBuffer(buf.slice(1, 32 + 1)); }; diff --git a/lib/pubkey.js b/lib/pubkey.js index 1199034..0c93574 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -5,7 +5,7 @@ var Pubkey = function Pubkey(point) { if (!(this instanceof Pubkey)) return new Pubkey(point); if (point && !point.getX() && !point.getY()) - throw new Error('pubkey: Invalid point'); + throw new Error('Invalid point'); this.point = point; }; @@ -14,7 +14,7 @@ Pubkey.prototype.fromDER = function(buf) { var xbuf = buf.slice(1, 33); var ybuf = buf.slice(33, 65); if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) - throw new Error('pubkey: Length of x and y must be 32 bytes'); + throw new Error('Length of x and y must be 32 bytes'); var x = bn(xbuf); var y = bn(ybuf); this.point = Point(x, y); @@ -27,7 +27,7 @@ Pubkey.prototype.fromDER = function(buf) { var x = bn(xbuf); this.fromX(false, x); } else { - throw new Error('pubkey: Invalid DER format pubkey'); + throw new Error('Invalid DER format pubkey'); } return this; }; @@ -38,7 +38,7 @@ Pubkey.prototype.fromString = function(str) { Pubkey.prototype.fromX = function(odd, x) { if (typeof odd !== 'boolean') - throw new Error('pubkey: Must specify whether y is odd or not (true or false)'); + throw new Error('Must specify whether y is odd or not (true or false)'); this.point = Point.fromX(odd, x); }; @@ -48,7 +48,7 @@ Pubkey.prototype.toBuffer = function() { Pubkey.prototype.toDER = function(compressed) { if (typeof compressed !== 'boolean') - throw new Error('pubkey: Must specify whether the public key is compressed or not (true or false)'); + throw new Error('Must specify whether the public key is compressed or not (true or false)'); var x = this.point.getX(); var y = this.point.getY(); diff --git a/lib/random.js b/lib/random.js index 4947e7c..77573a8 100644 --- a/lib/random.js +++ b/lib/random.js @@ -16,14 +16,14 @@ Random.getRandomBufferNode = function(size) { Random.getRandomBufferBrowser = function(size) { if (!window.crypto && !window.msCrypto) - throw new Error('random: window.crypto not available'); + throw new Error('window.crypto not available'); if (window.crypto && window.crypto.getRandomValues) var crypto = window.crypto; else if (window.msCrypto && window.msCrypto.getRandomValues) //internet explorer var crypto = window.msCrypto; else - throw new Error('random: window.crypto.getRandomValues not available'); + throw new Error('window.crypto.getRandomValues not available'); var bbuf = new Uint8Array(size); crypto.getRandomValues(bbuf); diff --git a/lib/signature.js b/lib/signature.js index f87215e..ae5e7d5 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -17,11 +17,11 @@ Signature.prototype.fromCompressed = function(buf) { var b3 = buf.slice(33, 65); if (!(i === 0 || i === 1 || i === 2 || i === 3)) - throw new Error('signature: i must be 0, 1, 2, or 3'); + throw new Error('i must be 0, 1, 2, or 3'); if (b2.length !== 32) - throw new Error('signature: r must be 32 bytes'); + throw new Error('r must be 32 bytes'); if (b3.length !== 32) - throw new Error('signature: s must be 32 bytes'); + throw new Error('s must be 32 bytes'); this.i = i; this.r = BN().fromBuffer(b2); @@ -41,42 +41,42 @@ Signature.prototype.fromString = function(str) { Signature.parseDER = function(buf) { if (!Buffer.isBuffer(buf)) - throw new Error('signature: DER formatted signature should be a buffer'); + throw new Error('DER formatted signature should be a buffer'); var header = buf[0]; if (header !== 0x30) - throw new Error('signature: Header byte should be 0x30'); + throw new Error('Header byte should be 0x30'); var length = buf[1]; if (length !== buf.slice(2).length) - throw new Error('signature: Length byte should length of what follows'); + throw new Error('Length byte should length of what follows'); var rheader = buf[2 + 0]; if (rheader !== 0x02) - throw new Error('signature: Integer byte for r should be 0x02'); + throw new Error('Integer byte for r should be 0x02'); var rlength = buf[2 + 1]; var rbuf = buf.slice(2 + 2, 2 + 2 + rlength); var r = BN().fromBuffer(rbuf); var rneg = buf[2 + 1 + 1] === 0x00 ? true : false; if (rlength !== rbuf.length) - throw new Error('signature: Length of r incorrect'); + throw new Error('Length of r incorrect'); var sheader = buf[2 + 2 + rlength + 0]; if (sheader !== 0x02) - throw new Error('signature: Integer byte for s should be 0x02'); + throw new Error('Integer byte for s should be 0x02'); var slength = buf[2 + 2 + rlength + 1]; var sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength); var s = BN().fromBuffer(sbuf); var sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00 ? true : false; if (slength !== sbuf.length) - throw new Error('signature: Length of s incorrect'); + throw new Error('Length of s incorrect'); var sumlength = 2 + 2 + rlength + 2 + slength; if (length !== sumlength - 2) - throw new Error('signature: Length of signature incorrect'); + throw new Error('Length of signature incorrect'); var obj = { header: header, @@ -99,7 +99,7 @@ Signature.parseDER = function(buf) { Signature.prototype.toCompressed = function(i) { i = typeof i === 'number' ? i : this.i; if (!(i === 0 || i === 1 || i === 2 || i === 3)) - throw new Error('signature: i must be equal to 0, 1, 2, or 3'); + throw new Error('i must be equal to 0, 1, 2, or 3'); var b1 = new Buffer([i]); var b2 = this.r.toBuffer({size: 32}); diff --git a/test/test.address.js b/test/test.address.js index 68881cf..d773a13 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -126,7 +126,7 @@ describe('Address', function() { address.network = 'unknown'; (function() { address.validate(); - }).should.throw('address: network must be "mainnet" or "testnet"'); + }).should.throw('network must be "mainnet" or "testnet"'); }); it('should throw an error on this invalid type', function() { @@ -135,7 +135,7 @@ describe('Address', function() { address.type = 'unknown'; (function() { address.validate(); - }).should.throw('address: type must be "pubkeyhash" or "p2sh"'); + }).should.throw('type must be "pubkeyhash" or "p2sh"'); }); }); diff --git a/test/test.base58check.js b/test/test.base58check.js index c1ef051..ad9b1db 100644 --- a/test/test.base58check.js +++ b/test/test.base58check.js @@ -25,7 +25,7 @@ describe('Base58Check', function() { 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'); + }).should.throw('Input must be a buffer'); }); }); @@ -39,13 +39,13 @@ describe('Base58Check', function() { it('should throw an error when input is not a string', function() { (function() { Base58Check.decode(5); - }).should.throw('Base58Check: Input must be a string'); + }).should.throw('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'); + }).should.throw('Input string too short'); }); it('should throw an error when there is a checksum mismatch', function() { @@ -54,7 +54,7 @@ describe('Base58Check', function() { var enc2 = base58.encode(buf2); (function() { Base58Check.decode(enc2); - }).should.throw('Base58Check: Checksum mismatch'); + }).should.throw('Checksum mismatch'); }); }); diff --git a/test/test.point.js b/test/test.point.js index 15619c1..fe6b644 100644 --- a/test/test.point.js +++ b/test/test.point.js @@ -109,7 +109,7 @@ describe('Point', function() { var p = point(x, y); (function() { p.validate(); - }).should.throw('point: Invalid y value of public key'); + }).should.throw('Invalid y value of public key'); }); }); diff --git a/test/test.pubkey.js b/test/test.pubkey.js index a9bf39e..06e9b7a 100644 --- a/test/test.pubkey.js +++ b/test/test.pubkey.js @@ -119,7 +119,7 @@ describe('Pubkey', function() { pk.fromString(hex); (function() { pk.validate(); - }).should.throw('point: Invalid y value of public key'); + }).should.throw('Invalid y value of public key'); }); it('should not throw an error if pubkey is infinity', function() { @@ -127,7 +127,7 @@ describe('Pubkey', function() { pk.point = Point.getG().mul(Point.getN()); (function() { pk.validate(); - }).should.throw('point: Point cannot be equal to Infinity'); + }).should.throw('Point cannot be equal to Infinity'); }); }); From ddc9e2d2a4cb47d1f30e20316d9edd4220366044 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 21 Aug 2014 11:47:09 -0700 Subject: [PATCH 064/280] handle varInts better --- lib/bufferreader.js | 2 +- lib/bufferwriter.js | 2 -- test/test.bufferreader.js | 7 +++++-- test/test.bufferwriter.js | 10 ++++++++++ 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index afee08f..35a305c 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -70,7 +70,7 @@ BufferReader.prototype.readVarInt = function() { case 0xFE: return this.readUInt32LE(); case 0xFF: - return this.readUInt64LEBN().toNumber(); + throw new Error('number too large to retain precision - use readVarIntBN'); default: return first; } diff --git a/lib/bufferwriter.js b/lib/bufferwriter.js index babbfe9..7c410f5 100644 --- a/lib/bufferwriter.js +++ b/lib/bufferwriter.js @@ -56,7 +56,6 @@ BufferWriter.prototype.writeUInt64BEBN = function(bn) { return this; }; -//TODO: What if n is so large that it loses precision? BufferWriter.prototype.writeUInt64LEBN = function(bn) { var buf = bn.toBuffer({size: 8}); var reversebuf = new Buffer(Array.apply(new Array(), buf).reverse()); @@ -76,7 +75,6 @@ BufferWriter.prototype.writeVarIntBN = function(bn) { return this; }; -//TODO: What if n is so large that it loses precision? BufferWriter.varIntBuf = function(n) { var buf = undefined; if (n < 253) { diff --git a/test/test.bufferreader.js b/test/test.bufferreader.js index f305ecd..cf0a25e 100644 --- a/test/test.bufferreader.js +++ b/test/test.bufferreader.js @@ -1,3 +1,4 @@ +var BufferWriter = require('../lib/bufferwriter'); var BufferReader = require('../lib/bufferreader'); var should = require('chai').should(); @@ -156,10 +157,12 @@ describe('BufferReader', function() { br.readVarInt().should.equal(50000); }); - it('should read a 9 byte varint', function() { + it('should throw an error on a 9 byte varint', function() { var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]); var br = new BufferReader(buf); - br.readVarInt().should.equal(Math.pow(2, 64)); + (function() { + br.readVarInt(); + }).should.throw('number too large to retain precision - use readVarIntBN'); }); }); diff --git a/test/test.bufferwriter.js b/test/test.bufferwriter.js index 70d0305..02a7377 100644 --- a/test/test.bufferwriter.js +++ b/test/test.bufferwriter.js @@ -1,4 +1,5 @@ var BufferWriter = require('../lib/bufferwriter'); +var BufferReader = require('../lib/bufferreader'); var BN = require('../lib/bn'); var should = require('chai').should(); @@ -120,6 +121,15 @@ describe('BufferWriter', function() { bw.concat().length.should.equal(9); }); + it('should read back the same value it wrote for a 9 byte varInt', function() { + var bw = new BufferWriter(); + var n = Math.pow(2, 53); + n.should.equal(n + 1); //javascript number precision limit + bw.writeVarInt(n); + var br = new BufferReader(bw.concat()); + br.readVarIntBN().toNumber().should.equal(n); + }); + }); describe('#writeVarIntBN', function() { From 2131dbdfeeb2651d173c50b11b099413a07ac946 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 21 Aug 2014 11:52:43 -0700 Subject: [PATCH 065/280] remove unnecessary tab --- lib/bufferwriter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bufferwriter.js b/lib/bufferwriter.js index 7c410f5..85a18fe 100644 --- a/lib/bufferwriter.js +++ b/lib/bufferwriter.js @@ -99,7 +99,7 @@ BufferWriter.varIntBuf = function(n) { BufferWriter.varIntBufBN = function(bn) { var buf = undefined; - var n = bn.toNumber(); + var n = bn.toNumber(); if (n < 253) { buf = new Buffer(1); buf.writeUInt8(n, 0); From 4f90478aa6475ff95a617da2432dec657eebe428 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 21 Aug 2014 12:59:27 -0700 Subject: [PATCH 066/280] keep track of advantages over old bitcore --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index b557c8b..f0798aa 100644 --- a/README.md +++ b/README.md @@ -29,3 +29,11 @@ should work both in node, and be automatically work in the browser with used in conjunction with browserify). 7) Minimize the use of dependencies so that all code can be easily audited. + +------------------------- +Features over bitcore: +* Stealth addresses +* Proper handling of reading and writing big varInts +* Browserifiable +* A proper point class +* Better test coverage From e6d89a76d76706a27e745ce5eaf3b8c04c14af36 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 21 Aug 2014 14:00:18 -0700 Subject: [PATCH 067/280] use standard language - hash -> hashbuf --- lib/address.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/address.js b/lib/address.js index 264b9ef..ff1ffa9 100644 --- a/lib/address.js +++ b/lib/address.js @@ -3,8 +3,8 @@ var constants = require('./constants'); var Hash = require('./hash'); var Pubkey = require('./pubkey'); -function Address(hash, network, type) { - this.hash = hash; +function Address(hashbuf, network, type) { + this.hashbuf = hashbuf; this.network = network; this.type = type; }; @@ -12,7 +12,7 @@ function Address(hash, network, type) { Address.prototype.fromPubkey = function(pubkey, network, compressed) { if (typeof compressed === 'undefined') compressed = true; - this.hash = Hash.sha256ripemd160(pubkey.toDER(compressed)); + this.hashbuf = Hash.sha256ripemd160(pubkey.toDER(compressed)); this.network = network || 'mainnet'; this.type = 'pubkeyhash'; return this; @@ -40,7 +40,7 @@ Address.prototype.fromString = function(str) { this.type = 'unknown'; } - this.hash = buf.slice(1); + this.hashbuf = buf.slice(1); } Address.prototype.isValid = function() { @@ -54,7 +54,7 @@ Address.prototype.isValid = function() { Address.prototype.toBuffer = function() { version = new Buffer([constants[this.network][this.type]]); - var buf = Buffer.concat([version, this.hash]); + var buf = Buffer.concat([version, this.hashbuf]); return buf; }; @@ -63,7 +63,7 @@ Address.prototype.toString = function() { }; Address.prototype.validate = function() { - if (!Buffer.isBuffer(this.hash) || this.hash.length !== 20) + if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 20) throw new Error('hash must be a buffer of 20 bytes'); if (this.network !== 'mainnet' && this.network !== 'testnet') throw new Error('network must be "mainnet" or "testnet"'); From 15f9a99e65166a9222db069cc4e6b28a6b4ef62d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 21 Aug 2014 15:50:38 -0700 Subject: [PATCH 068/280] message signing --- lib/address.js | 2 + lib/ecdsa.js | 6 ++- lib/message.js | 90 ++++++++++++++++++++++++++++++++++++++++++++ test/test.message.js | 49 ++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 lib/message.js create mode 100644 test/test.message.js diff --git a/lib/address.js b/lib/address.js index ff1ffa9..c124cbc 100644 --- a/lib/address.js +++ b/lib/address.js @@ -4,6 +4,8 @@ var Hash = require('./hash'); var Pubkey = require('./pubkey'); function Address(hashbuf, network, type) { + if (!(this instanceof Address)) + return new Address(hashbuf, network, type); this.hashbuf = hashbuf; this.network = network; this.type = type; diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 53959a3..ea62070 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -21,7 +21,6 @@ ECDSA.prototype.calci = function() { try { var Qprime = this.sig2pubkey(); } catch (e) { - console.log(e); continue; } @@ -140,7 +139,10 @@ ECDSA.prototype.sign = function() { var k = this.k; var d = privkey.bn; - if (!hashbuf || !privkey || !k || !d) + if (!k) + throw new Error('You must specify k - perhaps you should run signRandomK instead'); + + if (!hashbuf || !privkey || !d) throw new Error('invalid parameters'); var N = Point.getN(); diff --git a/lib/message.js b/lib/message.js new file mode 100644 index 0000000..bb36f11 --- /dev/null +++ b/lib/message.js @@ -0,0 +1,90 @@ +var ECDSA = require('./ecdsa'); +var Key = require('./key'); +var Privkey = require('./privkey'); +var Pubkey = require('./pubkey'); +var BufferWriter = require('./bufferwriter'); +var Hash = require('./hash'); +var Address = require('./address'); + +var Message = function Message(messagebuf, key, sig, address, verified) { + if (!(this instanceof Message)) + return new Message(messagebuf, key, sig); + this.messagebuf = messagebuf; + this.key = key; + this.sig = sig; + this.address = address; + this.verified = verified; +}; + +Message.magicBytes = new Buffer('Bitcoin Signed Message:\n'); + +Message.magicHash = function(messagebuf) { + if (!Buffer.isBuffer(messagebuf)) + throw new Error('messagebuf must be a buffer'); + var bw = new BufferWriter(); + bw.writeVarInt(Message.magicBytes.length); + bw.write(Message.magicBytes); + bw.writeVarInt(messagebuf.length); + bw.write(messagebuf); + var buf = bw.concat(); + + var hashbuf = Hash.sha256sha256(buf); + + return hashbuf; +}; + +Message.prototype.sign = function() { + var hashbuf = Message.magicHash(this.messagebuf); + var ecdsa = ECDSA(hashbuf, this.key); + ecdsa.signRandomK(); + ecdsa.calci(); + this.sig = ecdsa.sig; + return this; +}; + +/* +Message.sign = function(messagebuf, key) { + var m = Message(messagebuf, key); + var sig = m.sign(); + var sigbuf = sig.toCompressed(); + var base64 = sigbuf.toString('base64'); + return base64; +}; +*/ + +Message.prototype.verify = function() { + var hashbuf = Message.magicHash(this.messagebuf); + + var ecdsa = new ECDSA(); + ecdsa.hashbuf = hashbuf; + ecdsa.sig = this.sig; + ecdsa.key = new Key(); + ecdsa.key.pubkey = ecdsa.sig2pubkey(); + + if (!ecdsa.verify()) { + console.log(ecdsa.sigError()); + this.verified = false; + return this; + } + + var address = Address().fromPubkey(ecdsa.key.pubkey); + //TODO: what if livenet/testnet mismatch? + if (address.hashbuf.toString('hex') === this.address.hashbuf.toString('hex')) + this.verified = true; + else + this.verified = false; + + return this; +}; + +/* +Message.verify = function(messagebuf, sigstr, address) { + var sigbuf = new Buffer(sigstr, 'base64'); + var message = new Message(); + message.messagebuf = messagebuf; + message.sig = Signature().fromCompressed(sigbuf); + message.address = address; +}; +*/ + +module.exports = Message; diff --git a/test/test.message.js b/test/test.message.js new file mode 100644 index 0000000..34f9063 --- /dev/null +++ b/test/test.message.js @@ -0,0 +1,49 @@ +var Address = require('../lib/address'); +var Message = require('../lib/message'); +var Key = require('../lib/key'); +var should = require('chai').should(); + +describe('Message', function() { + + it('should make a new message', function() { + var message = new Message(); + should.exist(message); + }); + + it('should make a new message when called without "new"', function() { + var message = Message(); + should.exist(message); + }); + + describe('#sign', function() { + var messagebuf = new Buffer('this is my message'); + var key = Key().fromRandom(); + + it('should sign a message', function() { + var message = new Message(); + message.messagebuf = messagebuf; + message.key = key; + message.sign(); + var sig = message.sig; + should.exist(sig); + }); + + }); + + describe('#verify', function() { + var messagebuf = new Buffer('this is my message'); + var key = Key().fromRandom(); + + it('should verify a message that was just signed', function() { + var message = new Message(); + message.messagebuf = messagebuf; + message.key = key; + message.address = Address().fromPubkey(key.pubkey); + message.sign(); + message.verify(); + message.verified.should.equal(true); + }); + + }); + +}); From 95a0bccda64a239963b5f27ef3d4cfd09d3c357a Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 21 Aug 2014 15:52:27 -0700 Subject: [PATCH 069/280] new principle for bitcore2 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f0798aa..4b3b62f 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ conjunction with browserify). 7) Minimize the use of dependencies so that all code can be easily audited. +8) All instance methods modify the state of the object and return the object. +To access the result of an instance method, you must access the object +property(s) that it modifies. + ------------------------- Features over bitcore: * Stealth addresses From 3b3ebb045887171d76cfc4b2b5a915b77eb0d3d4 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 21 Aug 2014 16:21:21 -0700 Subject: [PATCH 070/280] sign convenience function --- lib/ecdsa.js | 5 +++-- lib/message.js | 10 ++++------ test/test.message.js | 12 ++++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index ea62070..6e2a630 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -6,13 +6,14 @@ var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var Random = require('./random'); -var ECDSA = function ECDSA(hashbuf, key, sig, k) { +var ECDSA = function ECDSA(hashbuf, key, sig, k, verified) { if (!(this instanceof ECDSA)) - return new ECDSA(hashbuf, key, sig, k); + return new ECDSA(hashbuf, key, sig, k, verified); this.hashbuf = hashbuf; this.key = key; this.sig = sig; this.k = k; + this.verified = verified; }; ECDSA.prototype.calci = function() { diff --git a/lib/message.js b/lib/message.js index bb36f11..4ecb13e 100644 --- a/lib/message.js +++ b/lib/message.js @@ -42,15 +42,13 @@ Message.prototype.sign = function() { return this; }; -/* Message.sign = function(messagebuf, key) { var m = Message(messagebuf, key); - var sig = m.sign(); - var sigbuf = sig.toCompressed(); - var base64 = sigbuf.toString('base64'); - return base64; + m.sign(); + var sigbuf = m.sig.toCompressed(); + var sigstr = sigbuf.toString('base64'); + return sigstr; }; -*/ Message.prototype.verify = function() { var hashbuf = Message.magicHash(this.messagebuf); diff --git a/test/test.message.js b/test/test.message.js index 34f9063..82f19ba 100644 --- a/test/test.message.js +++ b/test/test.message.js @@ -46,4 +46,16 @@ describe('Message', function() { }); + describe('@sign', function() { + var messagebuf = new Buffer('this is my message'); + var key = Key().fromRandom(); + + it('should return a base64 string', function() { + var sigstr = Message.sign(messagebuf, key); + var sigbuf = new Buffer(sigstr, 'base64'); + sigbuf.length.should.equal(1 + 32 + 32); + }); + + }); + }); From 6176ad4a98126e31e7d7d30621e36d31dcebd1bb Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 22 Aug 2014 16:15:44 -0700 Subject: [PATCH 071/280] verify signed messages ...and fix bug where i (recover param) was stored incorrectly --- lib/address.js | 2 ++ lib/message.js | 6 +++--- lib/signature.js | 10 ++++++++-- test/test.message.js | 19 +++++++++++++++++++ test/test.signature.js | 2 +- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/lib/address.js b/lib/address.js index c124cbc..43be364 100644 --- a/lib/address.js +++ b/lib/address.js @@ -43,6 +43,8 @@ Address.prototype.fromString = function(str) { } this.hashbuf = buf.slice(1); + + return this; } Address.prototype.isValid = function() { diff --git a/lib/message.js b/lib/message.js index 4ecb13e..544d177 100644 --- a/lib/message.js +++ b/lib/message.js @@ -5,6 +5,7 @@ var Pubkey = require('./pubkey'); var BufferWriter = require('./bufferwriter'); var Hash = require('./hash'); var Address = require('./address'); +var Signature = require('./signature'); var Message = function Message(messagebuf, key, sig, address, verified) { if (!(this instanceof Message)) @@ -60,7 +61,6 @@ Message.prototype.verify = function() { ecdsa.key.pubkey = ecdsa.sig2pubkey(); if (!ecdsa.verify()) { - console.log(ecdsa.sigError()); this.verified = false; return this; } @@ -75,14 +75,14 @@ Message.prototype.verify = function() { return this; }; -/* Message.verify = function(messagebuf, sigstr, address) { var sigbuf = new Buffer(sigstr, 'base64'); var message = new Message(); message.messagebuf = messagebuf; message.sig = Signature().fromCompressed(sigbuf); message.address = address; + + return message.verify().verified; }; -*/ module.exports = Message; diff --git a/lib/signature.js b/lib/signature.js index ae5e7d5..31e88d4 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -12,7 +12,7 @@ var Signature = function Signature(r, s, i) { }; Signature.prototype.fromCompressed = function(buf) { - var i = buf.slice(0, 1)[0]; + var i = buf.slice(0, 1)[0] - 27 - 4; //TODO: handle uncompressed pubkeys var b2 = buf.slice(1, 33); var b3 = buf.slice(33, 65); @@ -26,17 +26,23 @@ Signature.prototype.fromCompressed = function(buf) { this.i = i; this.r = BN().fromBuffer(b2); this.s = BN().fromBuffer(b3); + + return this; }; Signature.prototype.fromDER = function(buf) { var obj = Signature.parseDER(buf); this.r = obj.r; this.s = obj.s; + + return this; }; Signature.prototype.fromString = function(str) { var buf = new Buffer(str, 'hex'); this.fromDER(buf); + + return this; }; Signature.parseDER = function(buf) { @@ -101,7 +107,7 @@ Signature.prototype.toCompressed = function(i) { if (!(i === 0 || i === 1 || i === 2 || i === 3)) throw new Error('i must be equal to 0, 1, 2, or 3'); - var b1 = new Buffer([i]); + var b1 = new Buffer([i + 27 + 4]); //TODO: handle uncompressed pubkeys var b2 = this.r.toBuffer({size: 32}); var b3 = this.s.toBuffer({size: 32}); return Buffer.concat([b1, b2, b3]); diff --git a/test/test.message.js b/test/test.message.js index 82f19ba..00e7bff 100644 --- a/test/test.message.js +++ b/test/test.message.js @@ -58,4 +58,23 @@ describe('Message', function() { }); + describe('@verify', function() { + var messagebuf = new Buffer('this is my message'); + var key = Key().fromRandom(); + + it('should verify a signed message', function() { + var sigstr = Message.sign(messagebuf, key); + var addr = Address().fromPubkey(key.pubkey); + Message.verify(messagebuf, sigstr, addr).should.equal(true); + }); + + it('should verify this known good signature', function() { + var addrstr = '1CKTmxj6DjGrGTfbZzVxnY4Besbv8oxSZb'; + var address = Address().fromString(addrstr); + var sigstr = 'IOrTlbNBI0QO990xOw4HAjnvRl/1zR+oBMS6HOjJgfJqXp/1EnFrcJly0UcNelqJNIAH4f0abxOZiSpYmenMH4M='; + Message.verify(messagebuf, sigstr, address); + }); + + }); + }); diff --git a/test/test.signature.js b/test/test.signature.js index 870dd09..e74e802 100644 --- a/test/test.signature.js +++ b/test/test.signature.js @@ -15,7 +15,7 @@ describe('Signature', function() { var blank = new Buffer(32); blank.fill(0); var compressed = Buffer.concat([ - new Buffer([0]), + new Buffer([0 + 27 + 4]), blank, blank ]); From b3423967317cda6591cbf8523d7990796ef57843 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 22 Aug 2014 16:18:34 -0700 Subject: [PATCH 072/280] expose Message --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 867c47f..b73f98a 100644 --- a/index.js +++ b/index.js @@ -13,6 +13,7 @@ bitcore.ECDSA = require('./lib/ecdsa'); bitcore.Hash = require('./lib/hash'); bitcore.KDF = require('./lib/kdf'); bitcore.Key = require('./lib/key'); +bitcore.Message = require('./lib/message'); bitcore.Point = require('./lib/point'); bitcore.Privkey = require('./lib/privkey'); bitcore.Pubkey = require('./lib/pubkey'); From 8e6a28162b24209678cb095f586c12c91f6fceca Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 22 Aug 2014 16:34:45 -0700 Subject: [PATCH 073/280] it is a "Compact" signature, not "Compressed" --- lib/message.js | 4 ++-- lib/signature.js | 4 ++-- test/test.signature.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/message.js b/lib/message.js index 544d177..a3b8018 100644 --- a/lib/message.js +++ b/lib/message.js @@ -46,7 +46,7 @@ Message.prototype.sign = function() { Message.sign = function(messagebuf, key) { var m = Message(messagebuf, key); m.sign(); - var sigbuf = m.sig.toCompressed(); + var sigbuf = m.sig.toCompact(); var sigstr = sigbuf.toString('base64'); return sigstr; }; @@ -79,7 +79,7 @@ Message.verify = function(messagebuf, sigstr, address) { var sigbuf = new Buffer(sigstr, 'base64'); var message = new Message(); message.messagebuf = messagebuf; - message.sig = Signature().fromCompressed(sigbuf); + message.sig = Signature().fromCompact(sigbuf); message.address = address; return message.verify().verified; diff --git a/lib/signature.js b/lib/signature.js index 31e88d4..965bcaa 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -11,7 +11,7 @@ var Signature = function Signature(r, s, i) { this.i = i; //public key recovery parameter in range [0, 3] }; -Signature.prototype.fromCompressed = function(buf) { +Signature.prototype.fromCompact = function(buf) { var i = buf.slice(0, 1)[0] - 27 - 4; //TODO: handle uncompressed pubkeys var b2 = buf.slice(1, 33); var b3 = buf.slice(33, 65); @@ -102,7 +102,7 @@ Signature.parseDER = function(buf) { return obj; }; -Signature.prototype.toCompressed = function(i) { +Signature.prototype.toCompact = function(i) { i = typeof i === 'number' ? i : this.i; if (!(i === 0 || i === 1 || i === 2 || i === 3)) throw new Error('i must be equal to 0, 1, 2, or 3'); diff --git a/test/test.signature.js b/test/test.signature.js index e74e802..d52bcd5 100644 --- a/test/test.signature.js +++ b/test/test.signature.js @@ -9,7 +9,7 @@ describe('Signature', function() { should.exist(sig); }); - describe('#fromCompressed', function() { + describe('#fromCompact', function() { it('should create a signature from a compressed signature', function() { var blank = new Buffer(32); @@ -20,7 +20,7 @@ describe('Signature', function() { blank ]); var sig = new Signature(); - sig.fromCompressed(compressed); + sig.fromCompact(compressed); sig.r.cmp(0).should.equal(0); sig.s.cmp(0).should.equal(0); }); From ee0dff9e2e234285d2c233d5646c1aa0b55d7b8f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 22 Aug 2014 16:59:18 -0700 Subject: [PATCH 074/280] another reason to perfer bitcore2 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4b3b62f..da70ff3 100644 --- a/README.md +++ b/README.md @@ -41,3 +41,4 @@ Features over bitcore: * Browserifiable * A proper point class * Better test coverage +* Proper message signing and verification From 7c945cdc01ae02ca93daca75aadebe46db4254cd Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 22 Aug 2014 17:43:22 -0700 Subject: [PATCH 075/280] add "compressed" feature to pubkeys ...not just privkeys. since, of course, they can be compressed or uncompressed. --- lib/address.js | 6 ++---- lib/key.js | 6 +++--- lib/pubkey.js | 13 ++++++++++--- test/test.address.js | 3 ++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/address.js b/lib/address.js index 43be364..608d0b8 100644 --- a/lib/address.js +++ b/lib/address.js @@ -11,10 +11,8 @@ function Address(hashbuf, network, type) { this.type = type; }; -Address.prototype.fromPubkey = function(pubkey, network, compressed) { - if (typeof compressed === 'undefined') - compressed = true; - this.hashbuf = Hash.sha256ripemd160(pubkey.toDER(compressed)); +Address.prototype.fromPubkey = function(pubkey, network) { + this.hashbuf = Hash.sha256ripemd160(pubkey.toBuffer()); this.network = network || 'mainnet'; this.type = 'pubkeyhash'; return this; diff --git a/lib/key.js b/lib/key.js index c48d3f6..a7a3aa5 100644 --- a/lib/key.js +++ b/lib/key.js @@ -34,12 +34,12 @@ Key.prototype.fromString = function(str) { } }; -Key.prototype.getAddress = function(network, compressed) { - return (new Address()).fromPubkey(this.pubkey, network, compressed); +Key.prototype.getAddress = function(network) { + return Address().fromPubkey(this.pubkey, network); }; Key.prototype.privkey2pubkey = function() { - this.pubkey = new Pubkey(point.getG().mul(this.privkey.bn)); + this.pubkey = new Pubkey(point.getG().mul(this.privkey.bn), this.privkey.compressed); }; Key.prototype.toString = function() { diff --git a/lib/pubkey.js b/lib/pubkey.js index 0c93574..8a659a2 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -1,12 +1,13 @@ var Point = require('./point'); var bn = require('./bn'); -var Pubkey = function Pubkey(point) { +var Pubkey = function Pubkey(point, compressed) { if (!(this instanceof Pubkey)) return new Pubkey(point); if (point && !point.getX() && !point.getY()) throw new Error('Invalid point'); this.point = point; + this.compressed = compressed; }; Pubkey.prototype.fromDER = function(buf) { @@ -18,14 +19,17 @@ Pubkey.prototype.fromDER = function(buf) { var x = bn(xbuf); var y = bn(ybuf); this.point = Point(x, y); + this.compressed = false; } else if (buf[0] == 0x03) { var xbuf = buf.slice(1); var x = bn(xbuf); this.fromX(true, x); + this.compressed = true; } else if (buf[0] == 0x02) { var xbuf = buf.slice(1); var x = bn(xbuf); this.fromX(false, x); + this.compressed = true; } else { throw new Error('Invalid DER format pubkey'); } @@ -43,10 +47,12 @@ Pubkey.prototype.fromX = function(odd, x) { }; Pubkey.prototype.toBuffer = function() { - return this.toDER(true); + var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; + return this.toDER(compressed); }; Pubkey.prototype.toDER = function(compressed) { + compressed = typeof this.compressed === 'undefined' ? compressed : this.compressed; if (typeof compressed !== 'boolean') throw new Error('Must specify whether the public key is compressed or not (true or false)'); @@ -70,7 +76,8 @@ Pubkey.prototype.toDER = function(compressed) { }; Pubkey.prototype.toString = function() { - return this.toDER(true).toString('hex'); + var compressed = typeof this.compressed === 'undefined' ? true : this.compressed; + return this.toDER(compressed).toString('hex'); }; //https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf diff --git a/test/test.address.js b/test/test.address.js index d773a13..bdd8c93 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -26,7 +26,8 @@ describe('Address', function() { var pubkey = new Pubkey(); pubkey.fromDER(new Buffer('0285e9737a74c30a873f74df05124f2aa6f53042c2fc0a130d6cbd7d16b944b004', 'hex')); var address = new Address(); - address.fromPubkey(pubkey, 'mainnet', false); + pubkey.compressed = false; + address.fromPubkey(pubkey, 'mainnet'); address.toString().should.equal('16JXnhxjJUhxfyx4y6H4sFcxrgt8kQ8ewX'); }); From a80d512570b20ab62e606a9fc5c4e5a5451921fb Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 22 Aug 2014 18:00:55 -0700 Subject: [PATCH 076/280] add tests for preservation of compressed in privkey2pubkey --- test/test.key.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/test.key.js b/test/test.key.js index f2c098f..b7e76c3 100644 --- a/test/test.key.js +++ b/test/test.key.js @@ -99,6 +99,24 @@ describe('Key', function() { key.pubkey.toString().should.equal(pubhex); }); + it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { + var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; + var key = new Key(); + key.privkey = new Privkey(bn(new Buffer(privhex, 'hex'))); + key.privkey.compressed = true; + key.privkey2pubkey(); + key.pubkey.compressed.should.equal(true); + }); + + it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { + var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; + var key = new Key(); + key.privkey = new Privkey(bn(new Buffer(privhex, 'hex'))); + key.privkey.compressed = false; + key.privkey2pubkey(); + key.pubkey.compressed.should.equal(false); + }); + }); describe("#toString()", function() { From bc94a5cb5934bec7ecca085895365ce0244381e9 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 22 Aug 2014 19:43:32 -0700 Subject: [PATCH 077/280] sign/verify with uncompressed pubkeys --- lib/ecdsa.js | 3 ++- lib/message.js | 2 +- lib/signature.js | 22 ++++++++++++++++++---- test/test.message.js | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 6e2a630..5c2fadb 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -97,6 +97,7 @@ ECDSA.prototype.sig2pubkey = function() { var Q = R.mul(s).add(G.mul(eNeg)).mul(rInv); var pubkey = new Pubkey(Q); + pubkey.compressed = this.sig.compressed; pubkey.validate(); return pubkey; @@ -156,7 +157,7 @@ ECDSA.prototype.sign = function() { var s = k.invm(N).mul(e.add(d.mul(r))).mod(N); } while (r.cmp(0) <= 0 || s.cmp(0) <= 0); - this.sig = new Signature(r, s); + this.sig = new Signature(r, s, undefined, this.key.pubkey.compressed); return this.sig; }; diff --git a/lib/message.js b/lib/message.js index a3b8018..126a241 100644 --- a/lib/message.js +++ b/lib/message.js @@ -65,7 +65,7 @@ Message.prototype.verify = function() { return this; } - var address = Address().fromPubkey(ecdsa.key.pubkey); + var address = Address().fromPubkey(ecdsa.key.pubkey, undefined, this.sig.compressed); //TODO: what if livenet/testnet mismatch? if (address.hashbuf.toString('hex') === this.address.hashbuf.toString('hex')) this.verified = true; diff --git a/lib/signature.js b/lib/signature.js index 965bcaa..775d9b4 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -2,17 +2,25 @@ var BN = require('./bn'); var Point = require('./point'); var Pubkey = require('./pubkey'); -var Signature = function Signature(r, s, i) { +var Signature = function Signature(r, s, i, compressed) { if (!(this instanceof Signature)) - return new Signature(r, s, i); + return new Signature(r, s, i, compressed); this.r = r; this.s = s; this.i = i; //public key recovery parameter in range [0, 3] + this.compressed = compressed; }; Signature.prototype.fromCompact = function(buf) { + var compressed = true; + if (i < 0) { + var compressed = false; + i = i + 4; + } + var i = buf.slice(0, 1)[0] - 27 - 4; //TODO: handle uncompressed pubkeys + var b2 = buf.slice(1, 33); var b3 = buf.slice(33, 65); @@ -23,6 +31,7 @@ Signature.prototype.fromCompact = function(buf) { if (b3.length !== 32) throw new Error('s must be 32 bytes'); + this.compressed = compressed; this.i = i; this.r = BN().fromBuffer(b2); this.s = BN().fromBuffer(b3); @@ -102,12 +111,17 @@ Signature.parseDER = function(buf) { return obj; }; -Signature.prototype.toCompact = function(i) { +Signature.prototype.toCompact = function(i, compressed) { i = typeof i === 'number' ? i : this.i; + compressed = typeof compressed === 'boolean' ? compressed : this.compressed; + if (!(i === 0 || i === 1 || i === 2 || i === 3)) throw new Error('i must be equal to 0, 1, 2, or 3'); - var b1 = new Buffer([i + 27 + 4]); //TODO: handle uncompressed pubkeys + var val = i + 27 + 4; + if (compressed === false) + val = val - 4; + var b1 = new Buffer([val]); var b2 = this.r.toBuffer({size: 32}); var b3 = this.s.toBuffer({size: 32}); return Buffer.concat([b1, b2, b3]); diff --git a/test/test.message.js b/test/test.message.js index 00e7bff..91b1123 100644 --- a/test/test.message.js +++ b/test/test.message.js @@ -56,6 +56,24 @@ describe('Message', function() { sigbuf.length.should.equal(1 + 32 + 32); }); + it('should sign with a compressed pubkey', function() { + var key = Key().fromRandom(); + key.pubkey.compressed = true; + var sigstr = Message.sign(messagebuf, key); + var sigbuf = new Buffer(sigstr, 'base64'); + sigbuf[0].should.be.above(27 + 4 - 1); + sigbuf[0].should.be.below(27 + 4 + 4 - 1); + }); + + it('should sign with an uncompressed pubkey', function() { + var key = Key().fromRandom(); + key.pubkey.compressed = false; + var sigstr = Message.sign(messagebuf, key); + var sigbuf = new Buffer(sigstr, 'base64'); + sigbuf[0].should.be.above(27 - 1); + sigbuf[0].should.be.below(27 + 4 - 1); + }); + }); describe('@verify', function() { From c345d5dd1934120f36c3494f3e9efb8d09ef30f8 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 12:50:02 -0700 Subject: [PATCH 078/280] improve README slightly --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da70ff3..b71586c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ bitcore2 ======== -Bitcore 2 is a rewrite of bitcore to satisfy several goals: +Bitcore 2 is a rewrite of bitcore. It is alpha quality. It will ultimately be +merged into upstream bitcore. + +Our goals: 1) Support ease-of-use by being internally consistent. It should not be necessary to read the source code of a class or function to know how to use it. From e6a88118aea29af865598c2db21d0884a6a46a2c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 12:50:21 -0700 Subject: [PATCH 079/280] fix error message --- lib/ecdsa.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 5c2fadb..48117f1 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -60,7 +60,7 @@ ECDSA.prototype.randomK = function() { ECDSA.prototype.sig2pubkey = function() { var i = this.sig.i; if (!(i === 0 || i === 1 || i === 2 || i === 3)) - throw new Error('signature: i must be equal to 0, 1, 2, or 3'); + throw new Error('i must be equal to 0, 1, 2, or 3'); var e = BN().fromBuffer(this.hashbuf); var r = this.sig.r; From ea0ee1b687a6a76075849e0c155480a1e87ae493 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 14:26:17 -0700 Subject: [PATCH 080/280] add info on public key recovery --- lib/ecdsa.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 48117f1..6c90e16 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -57,6 +57,9 @@ ECDSA.prototype.randomK = function() { return this; }; +// Information about public key recovery: +// https://bitcointalk.org/index.php?topic=6430.0 +// http://stackoverflow.com/questions/19665491/how-do-i-get-an-ecdsa-public-key-from-just-a-bitcoin-signature-sec1-4-1-6-k ECDSA.prototype.sig2pubkey = function() { var i = this.sig.i; if (!(i === 0 || i === 1 || i === 2 || i === 3)) From b81e4c75fbbe32ceb779c07d7f4f281d170578a0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 14:27:47 -0700 Subject: [PATCH 081/280] put static methods at the top --- lib/message.js | 36 ++++++++++++------------- test/test.message.js | 62 ++++++++++++++++++++++---------------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/lib/message.js b/lib/message.js index 126a241..c345dfb 100644 --- a/lib/message.js +++ b/lib/message.js @@ -34,6 +34,24 @@ Message.magicHash = function(messagebuf) { return hashbuf; }; +Message.sign = function(messagebuf, key) { + var m = Message(messagebuf, key); + m.sign(); + var sigbuf = m.sig.toCompact(); + var sigstr = sigbuf.toString('base64'); + return sigstr; +}; + +Message.verify = function(messagebuf, sigstr, address) { + var sigbuf = new Buffer(sigstr, 'base64'); + var message = new Message(); + message.messagebuf = messagebuf; + message.sig = Signature().fromCompact(sigbuf); + message.address = address; + + return message.verify().verified; +}; + Message.prototype.sign = function() { var hashbuf = Message.magicHash(this.messagebuf); var ecdsa = ECDSA(hashbuf, this.key); @@ -43,14 +61,6 @@ Message.prototype.sign = function() { return this; }; -Message.sign = function(messagebuf, key) { - var m = Message(messagebuf, key); - m.sign(); - var sigbuf = m.sig.toCompact(); - var sigstr = sigbuf.toString('base64'); - return sigstr; -}; - Message.prototype.verify = function() { var hashbuf = Message.magicHash(this.messagebuf); @@ -75,14 +85,4 @@ Message.prototype.verify = function() { return this; }; -Message.verify = function(messagebuf, sigstr, address) { - var sigbuf = new Buffer(sigstr, 'base64'); - var message = new Message(); - message.messagebuf = messagebuf; - message.sig = Signature().fromCompact(sigbuf); - message.address = address; - - return message.verify().verified; -}; - module.exports = Message; diff --git a/test/test.message.js b/test/test.message.js index 91b1123..311dbb3 100644 --- a/test/test.message.js +++ b/test/test.message.js @@ -15,37 +15,6 @@ describe('Message', function() { should.exist(message); }); - describe('#sign', function() { - var messagebuf = new Buffer('this is my message'); - var key = Key().fromRandom(); - - it('should sign a message', function() { - var message = new Message(); - message.messagebuf = messagebuf; - message.key = key; - message.sign(); - var sig = message.sig; - should.exist(sig); - }); - - }); - - describe('#verify', function() { - var messagebuf = new Buffer('this is my message'); - var key = Key().fromRandom(); - - it('should verify a message that was just signed', function() { - var message = new Message(); - message.messagebuf = messagebuf; - message.key = key; - message.address = Address().fromPubkey(key.pubkey); - message.sign(); - message.verify(); - message.verified.should.equal(true); - }); - - }); - describe('@sign', function() { var messagebuf = new Buffer('this is my message'); var key = Key().fromRandom(); @@ -95,4 +64,35 @@ describe('Message', function() { }); + describe('#sign', function() { + var messagebuf = new Buffer('this is my message'); + var key = Key().fromRandom(); + + it('should sign a message', function() { + var message = new Message(); + message.messagebuf = messagebuf; + message.key = key; + message.sign(); + var sig = message.sig; + should.exist(sig); + }); + + }); + + describe('#verify', function() { + var messagebuf = new Buffer('this is my message'); + var key = Key().fromRandom(); + + it('should verify a message that was just signed', function() { + var message = new Message(); + message.messagebuf = messagebuf; + message.key = key; + message.address = Address().fromPubkey(key.pubkey); + message.sign(); + message.verify(); + message.verified.should.equal(true); + }); + + }); + }); From 76e8136b45023b7d0490da88f47353a86258a807 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 14:29:35 -0700 Subject: [PATCH 082/280] prefix static methods with @ in tests ...instance methods prefixed with # --- test/test.base58.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test.base58.js b/test/test.base58.js index d05b944..236f5d0 100644 --- a/test/test.base58.js +++ b/test/test.base58.js @@ -15,7 +15,7 @@ describe('Base58', function() { should.exist(b58); }); - describe('#encode', function() { + describe('@encode', function() { it('should encode the buffer accurately', function() { Base58.encode(buf).should.equal(enc); @@ -29,7 +29,7 @@ describe('Base58', function() { }); - describe('#decode', function() { + describe('@decode', function() { it('should decode this encoded value correctly', function() { Base58.decode(enc).toString('hex').should.equal(buf.toString('hex')); From f3dcb5bba18efef9347ff81807b38668961ccb09 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 14:31:30 -0700 Subject: [PATCH 083/280] typo in error message --- lib/bip32.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bip32.js b/lib/bip32.js index 953fa57..4e84fff 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -329,7 +329,7 @@ BIP32.prototype.toString = function() { function uint(f, size) { if (f.length < size) - throw new Error('gcnot enough data'); + throw new Error('not enough data'); var n = 0; for (var i = 0; i < size; i++) { n *= 256; From a0ad59fbac73619483f5cedf6fca58161065a5d5 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 14:51:55 -0700 Subject: [PATCH 084/280] static tests start with @ --- test/test.base58check.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test.base58check.js b/test/test.base58check.js index ad9b1db..58e2bb6 100644 --- a/test/test.base58check.js +++ b/test/test.base58check.js @@ -16,7 +16,7 @@ describe('Base58Check', function() { should.exist(b58); }); - describe('#encode', function() { + describe('@encode', function() { it('should encode the buffer accurately', function() { Base58Check.encode(buf).should.equal(enc); @@ -30,7 +30,7 @@ describe('Base58Check', function() { }); - describe('#decode', function() { + describe('@decode', function() { it('should decode this encoded value correctly', function() { Base58Check.decode(enc).toString('hex').should.equal(buf.toString('hex')); From 1dead4cbc4ae07099f2f5d6d4725a67d443ad50c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 17:33:09 -0700 Subject: [PATCH 085/280] AES --- lib/expmt/aes.js | 47 ++++++++++++++++++++++++++ package.json | 1 + test/test.aes.js | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 lib/expmt/aes.js create mode 100644 test/test.aes.js diff --git a/lib/expmt/aes.js b/lib/expmt/aes.js new file mode 100644 index 0000000..4117ed4 --- /dev/null +++ b/lib/expmt/aes.js @@ -0,0 +1,47 @@ +var aes = require('aes'); + +var AES = function AES() { +}; + +AES.encrypt = function(messagebuf, keybuf) { + var key = AES.buf2words(keybuf); + var message = AES.buf2words(messagebuf); + var a = new aes(key); + var enc = a.encrypt(message); + var encbuf = AES.words2buf(enc); + return encbuf; +}; + +AES.decrypt = function(encbuf, keybuf) { + var enc = AES.buf2words(encbuf); + var key = AES.buf2words(keybuf); + var a = new aes(key); + var message = a.decrypt(enc); + var messagebuf = AES.words2buf(message); + return messagebuf; +}; + +AES.buf2words = function(buf) { + if (buf.length % 4) + throw new Error('buf length must be a multiple of 4'); + + var words = []; + + for (var i = 0; i < buf.length / 4; i++) { + words.push(buf.readUInt32BE(i * 4)); + }; + + return words; +}; + +AES.words2buf = function(words) { + var buf = new Buffer(words.length * 4); + + for (var i = 0; i < words.length; i++) { + buf.writeUInt32BE(words[i], i * 4); + }; + + return buf; +}; + +module.exports = AES; diff --git a/package.json b/package.json index 71685fc..758d541 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "multisig" ], "dependencies": { + "aes": "=0.1.0", "bn.js": "=0.13.3", "bs58": "=1.2.1", "elliptic": "=0.15.7", diff --git a/test/test.aes.js b/test/test.aes.js new file mode 100644 index 0000000..be09a4c --- /dev/null +++ b/test/test.aes.js @@ -0,0 +1,85 @@ +var should = require('chai').should(); +var Hash = require('../lib/hash'); +var AES = require('../lib/expmt/aes'); + +describe('AES', function() { + var m128 = Hash.sha256(new Buffer('test1')).slice(0, 128 / 8); + + var k128 = Hash.sha256(new Buffer('test2')).slice(0, 128 / 8); + var k192 = Hash.sha256(new Buffer('test2')).slice(0, 192 / 8); + var k256 = Hash.sha256(new Buffer('test2')).slice(0, 256 / 8); + + var e128 = new Buffer('3477e13884125038f4dc24e9d2cfbbc7', 'hex'); + var e192 = new Buffer('b670954c0e2da1aaa5f9063de04eb961', 'hex'); + var e256 = new Buffer('dd2ce24581183a4a7c0b1068f8bc79f0', 'hex'); + + + describe('@encrypt', function() { + + it('should encrypt with a 128 bit key', function() { + var encbuf = AES.encrypt(m128, k128); + encbuf.toString('hex').should.equal(e128.toString('hex')); + }); + + it('should encrypt with a 192 bit key', function() { + var encbuf = AES.encrypt(m128, k192); + encbuf.toString('hex').should.equal(e192.toString('hex')); + }); + + it('should encrypt with a 256 bit key', function() { + var encbuf = AES.encrypt(m128, k256); + encbuf.toString('hex').should.equal(e256.toString('hex')); + }); + + }); + + describe('@decrypt', function() { + + it('should encrypt/decrypt with a 128 bit key', function() { + var encbuf = AES.encrypt(m128, k128); + var m = AES.decrypt(encbuf, k128); + m.toString('hex').should.equal(m128.toString('hex')); + }); + + it('should encrypt/decrypt with a 192 bit key', function() { + var encbuf = AES.encrypt(m128, k192); + var m = AES.decrypt(encbuf, k192); + m.toString('hex').should.equal(m128.toString('hex')); + }); + + it('should encrypt/decrypt with a 256 bit key', function() { + var encbuf = AES.encrypt(m128, k256); + var m = AES.decrypt(encbuf, k256); + m.toString('hex').should.equal(m128.toString('hex')); + }); + + }); + + describe('@buf2words', function() { + + it('should convert this 4 length buffer into an array', function() { + var buf = new Buffer([0, 0, 0, 0]); + var words = AES.buf2words(buf); + words.length.should.equal(1); + }); + + it('should throw an error on this 5 length buffer', function() { + var buf = new Buffer([0, 0, 0, 0, 0]); + (function() { + var words = AES.buf2words(buf); + }).should.throw(); + }); + + }); + + describe('@words2buf', function() { + + it('should convert this array into a buffer', function() { + var a = [100, 0]; + var buf = AES.words2buf(a); + buf.length.should.equal(8); + }); + + }); + +}); From 1b1ecd989a6a94c4782edc02fbee515648901565 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 19:38:20 -0700 Subject: [PATCH 086/280] pkcs7 This is a standard algorithm for the purposes of padding a block for a block cipher. It will be used in CBC, which in turned will be used with AES for ECIES. --- lib/expmt/cbc.js | 14 ++++++++++++++ test/test.cbc.js | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 lib/expmt/cbc.js create mode 100644 test/test.cbc.js diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js new file mode 100644 index 0000000..bc4f060 --- /dev/null +++ b/lib/expmt/cbc.js @@ -0,0 +1,14 @@ +var Random = require('../random'); + +var CBC = function CBC() { +}; + +CBC.pkcs7pad = function(buf, blocksize) { + var bytesize = blocksize / 8; + var padbytesize = bytesize - buf.length; + var pad = new Buffer(padbytesize); + pad.fill(padbytesize); + return Buffer.concat([buf, pad]); +}; + +module.exports = CBC; diff --git a/test/test.cbc.js b/test/test.cbc.js new file mode 100644 index 0000000..e662feb --- /dev/null +++ b/test/test.cbc.js @@ -0,0 +1,21 @@ +var should = require('chai').should(); +var CBC = require('../lib/expmt/cbc'); + +describe('CBC', function() { + + describe('@pkcs7pad', function() { + + it('should pad this 32 bit buffer to 128 bits with the number 128/8 - 32/8', function() { + var buf = new Buffer(32 / 8); + buf.fill(0); + var padbuf = CBC.pkcs7pad(buf, 128); + padbuf.length.should.equal(128 / 8); + padbuf[32 / 8].should.equal(128 / 8 - 32 / 8); + padbuf[32 / 8 + 1].should.equal(128 / 8 - 32 / 8); + // ... + padbuf[32 / 8 + 128 / 8 - 32 / 8 - 1].should.equal(128 / 8 - 32 / 8); + }); + + }); + +}); From db7ae4c6d1fbb6d45018c5359e12bd22df580117 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 20:37:16 -0700 Subject: [PATCH 087/280] static tests start with @ --- test/test.random.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test.random.js b/test/test.random.js index 242f978..4b6d026 100644 --- a/test/test.random.js +++ b/test/test.random.js @@ -3,7 +3,7 @@ var Random = require('../lib/random'); describe('Random', function() { - describe('#getRandomBuffer', function() { + describe('@getRandomBuffer', function() { it('should return a buffer', function() { var bytes = Random.getRandomBuffer(8); @@ -28,7 +28,7 @@ describe('Random', function() { }); - describe('#getPseudoRandomBuffer', function() { + describe('@getPseudoRandomBuffer', function() { it('should generate 7 random bytes', function() { var buf = Random.getPseudoRandomBuffer(7); From 63d0fa96b0e0eb9e1f895623f6add76c6e8ce497 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 20:38:49 -0700 Subject: [PATCH 088/280] add repository field ...so npm quits complaining --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 758d541..c6867a8 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,10 @@ "merge", "multisig" ], + "repository": { + "type": "git", + "url": "https://github.com/ryanxcharles/bitcore2.git" + }, "dependencies": { "aes": "=0.1.0", "bn.js": "=0.13.3", From e097fe23ec43cf4d4db30b0d14b52cc3894bbae3 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 20:41:36 -0700 Subject: [PATCH 089/280] add contributors ...copied from bitcore. Should also add bitcoinjs-lib contributors. --- package.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/package.json b/package.json index c6867a8..5282640 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,36 @@ "scripts": { "test": "mocha" }, + "contributors": [ + { + "name": "Gordon Hall", + "email": "gordon@bitpay.com" + }, + { + "name": "Jeff Garzik", + "email": "jgarzik@bitpay.com" + }, + { + "name": "Manuel Araoz", + "email": "manuelaraoz@gmail.com" + }, + { + "name": "Matias Alejo Garcia", + "email": "ematiu@gmail.com" + }, + { + "name": "Ryan X. Charles", + "email": "ryanxcharles@gmail.com" + }, + { + "name": "Stefan Thomas", + "email": "moon@justmoon.net" + }, + { + "name": "Stephen Pair", + "email": "stephen@bitpay.com" + } + ], "keywords": [ "bitcoin", "bip32", From 38d9ab65af0984d3f0ab0bcc5c27586a1bec42e2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 20:51:56 -0700 Subject: [PATCH 090/280] xor buffers ... will be useful for CBC --- lib/expmt/cbc.js | 20 +++++++++++++++++++- test/test.cbc.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js index bc4f060..cf95d85 100644 --- a/lib/expmt/cbc.js +++ b/lib/expmt/cbc.js @@ -1,6 +1,11 @@ var Random = require('../random'); -var CBC = function CBC() { +var CBC = function CBC(blockcipherf, keybuf, ivbuf) { + if (!(this instanceof CBC)) + return new CBC(blockcipherf, keybuf, ivbuf); + this.blockcipherf = blockcipherf; + this.keybuf = keybuf; + this.ivbuf = ivbuf; }; CBC.pkcs7pad = function(buf, blocksize) { @@ -11,4 +16,17 @@ CBC.pkcs7pad = function(buf, blocksize) { return Buffer.concat([buf, pad]); }; +CBC.xorbufs = function(buf1, buf2) { + if (buf1.length !== buf2.length) + throw new Error('bufs must have the same length'); + + var buf = new Buffer(buf1.length); + + for (var i = 0; i < buf1.length; i++) { + buf[i] = buf1[i] ^ buf2[i]; + } + + return buf; +}; + module.exports = CBC; diff --git a/test/test.cbc.js b/test/test.cbc.js index e662feb..2489131 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -2,6 +2,16 @@ var should = require('chai').should(); var CBC = require('../lib/expmt/cbc'); describe('CBC', function() { + + it('should return a new CBC', function() { + var cbc = new CBC(); + should.exist(cbc); + }) + + it('should return a new CBC when called without "new"', function() { + var cbc = new CBC(); + should.exist(cbc); + }); describe('@pkcs7pad', function() { @@ -18,4 +28,22 @@ describe('CBC', function() { }); + describe('@xorbufs', function() { + + it('should xor 1 and 0', function() { + var buf1 = new Buffer([1]); + var buf2 = new Buffer([0]); + var buf = CBC.xorbufs(buf1, buf2); + buf[0].should.equal(1); + }); + + it('should xor 1 and 1', function() { + var buf1 = new Buffer([1]); + var buf2 = new Buffer([1]); + var buf = CBC.xorbufs(buf1, buf2); + buf[0].should.equal(0); + }); + + }); + }); From ad78e8ab7597877336226a1a9a03cce6310084b8 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 21:25:04 -0700 Subject: [PATCH 091/280] expose AES and CBC --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index b73f98a..9d93f4b 100644 --- a/index.js +++ b/index.js @@ -22,6 +22,8 @@ bitcore.Signature = require('./lib/signature'); //experimental, nonstandard, or unstable features bitcore.expmt = {}; +bitcore.expmt.AES = require('./lib/expmt/aes'); +bitcore.expmt.CBC = require('./lib/expmt/cbc'); bitcore.expmt.Stealth = require('./lib/expmt/stealth'); //dependencies, subject to change From e8995c9344dac0ba8291e718619a20a4fda63ce2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 24 Aug 2014 21:25:47 -0700 Subject: [PATCH 092/280] expose aes dependency --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 9d93f4b..a18dc67 100644 --- a/index.js +++ b/index.js @@ -28,6 +28,7 @@ bitcore.expmt.Stealth = require('./lib/expmt/stealth'); //dependencies, subject to change bitcore.deps = {}; +bitcore.deps.aes = require('aes'); bitcore.deps.bnjs = require('bn.js'); bitcore.deps.bs58 = require('bs58'); bitcore.deps.Buffer = Buffer; From 12f29a9b5286e14fd5c5969ad22a77aaba6fa825 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 25 Aug 2014 16:25:01 -0700 Subject: [PATCH 093/280] CBC encryption --- lib/expmt/cbc.js | 47 ++++++++++++++++++++++++ test/test.cbc.js | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js index cf95d85..47c4c5f 100644 --- a/lib/expmt/cbc.js +++ b/lib/expmt/cbc.js @@ -1,13 +1,60 @@ var Random = require('../random'); +// http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 var CBC = function CBC(blockcipherf, keybuf, ivbuf) { if (!(this instanceof CBC)) return new CBC(blockcipherf, keybuf, ivbuf); + this.blockcipherf = blockcipherf; this.keybuf = keybuf; this.ivbuf = ivbuf; }; +CBC.buf2blockbufs = function(buf, blocksize) { + var bytesize = blocksize / 8; + var blockbufs = []; + + for (var i = 0; i <= buf.length / bytesize; i++) { + var blockbuf = buf.slice(i * bytesize, i * bytesize + bytesize); + + if (blockbuf.length < blocksize) + blockbuf = CBC.pkcs7pad(blockbuf, blocksize); + + blockbufs.push(blockbuf); + } + + return blockbufs; +}; + +CBC.encrypt = function(messagebuf, ivbuf, blockcipherf, keybuf) { + var blocksize = ivbuf.length * 8; + var blockbufs = CBC.buf2blockbufs(messagebuf, blocksize); + var encbufs = CBC.encryptblocks(blockbufs, ivbuf, blockcipherf, keybuf); + var enc = Buffer.concat(encbufs); + return enc; +}; + +CBC.encryptblock = function(blockbuf, ivbuf, blockcipherf, keybuf) { + var xorbuf = CBC.xorbufs(blockbuf, ivbuf); + var encbuf = blockcipherf(xorbuf, keybuf); + return encbuf; +}; + +CBC.encryptblocks = function(blockbufs, ivbuf, blockcipherf, keybuf) { + var encbufs = []; + + for (var i = 0; i < blockbufs.length; i++) { + var blockbuf = blockbufs[i]; + var encbuf = CBC.encryptblock(blockbuf, ivbuf, blockcipherf, keybuf); + + encbufs.push(encbuf); + + ivbuf = encbuf; + } + + return encbufs; +}; + CBC.pkcs7pad = function(buf, blocksize) { var bytesize = blocksize / 8; var padbytesize = bytesize - buf.length; diff --git a/test/test.cbc.js b/test/test.cbc.js index 2489131..75a757a 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -12,6 +12,101 @@ describe('CBC', function() { var cbc = new CBC(); should.exist(cbc); }); + + describe('@buf2blockbufs', function() { + + it('should convert this buffer into one block', function() { + var buf = new Buffer(16 - 1); + buf.fill(0); + var blockbufs = CBC.buf2blockbufs(buf, 16 * 8); + blockbufs.length.should.equal(1); + blockbufs[0].toString('hex').should.equal('00000000000000000000000000000001'); + }); + + it('should convert this buffer into two blocks', function() { + var buf = new Buffer(16); + buf.fill(0); + var blockbufs = CBC.buf2blockbufs(buf, 16 * 8); + blockbufs.length.should.equal(2); + blockbufs[0].toString('hex').should.equal('00000000000000000000000000000000'); + blockbufs[1].toString('hex').should.equal('10101010101010101010101010101010'); + }); + + }); + + describe('@encrypt', function() { + + it('should return this known value', function() { + var messagebuf1 = new Buffer(128 / 8); + messagebuf1.fill(0); + var messagebuf2 = new Buffer(128 / 8); + messagebuf2.fill(0x10); + var messagebuf = Buffer.concat([messagebuf1, messagebuf2]); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var keybuf = new Buffer(128 / 8); + keybuf.fill(0); + var blockcipherf = function(messagebuf, keybuf) { + return messagebuf; + }; + var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipherf, keybuf); + encbuf.toString('hex').should.equal('101010101010101010101010101010100000000000000000000000000000000010101010101010101010101010101010'); + }); + + }); + + describe('@encryptblock', function() { + + it('should return this known value', function() { + var messagebuf = new Buffer(128 / 8); + messagebuf.fill(0); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var keybuf = new Buffer(128 / 8); + keybuf.fill(0); + var blockcipherf = function(messagebuf, keybuf) { + return messagebuf; + }; + var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipherf, keybuf); + enc.toString('hex').should.equal(ivbuf.toString('hex')); + }); + + it('should return this other known value', function() { + var messagebuf = new Buffer(128 / 8); + messagebuf.fill(0x10); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var keybuf = new Buffer(128 / 8); + keybuf.fill(0); + var blockcipherf = function(messagebuf, keybuf) { + return messagebuf; + }; + var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipherf, keybuf); + enc.toString('hex').should.equal('00000000000000000000000000000000'); + }); + + }); + + describe('@encryptblocks', function() { + + it('should return this known value', function() { + var messagebuf1 = new Buffer(128 / 8); + messagebuf1.fill(0); + var messagebuf2 = new Buffer(128 / 8); + messagebuf2.fill(0x10); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var keybuf = new Buffer(128 / 8); + keybuf.fill(0); + var blockcipherf = function(messagebuf, keybuf) { + return messagebuf; + }; + var encbufs = CBC.encryptblocks([messagebuf1, messagebuf2], ivbuf, blockcipherf, keybuf); + encbufs[0].toString('hex').should.equal('10101010101010101010101010101010'); + encbufs[1].toString('hex').should.equal('00000000000000000000000000000000'); + }); + + }); describe('@pkcs7pad', function() { From a09cac4ba6344b683f3aa78d723bd9b469d78496 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 25 Aug 2014 16:30:04 -0700 Subject: [PATCH 094/280] block cipher should support "encrypt" and "decrypt" --- lib/expmt/cbc.js | 18 +++++++++--------- test/test.cbc.js | 20 ++++++++++++-------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js index 47c4c5f..2596e24 100644 --- a/lib/expmt/cbc.js +++ b/lib/expmt/cbc.js @@ -1,11 +1,11 @@ var Random = require('../random'); // http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 -var CBC = function CBC(blockcipherf, keybuf, ivbuf) { +var CBC = function CBC(blockcipher, keybuf, ivbuf) { if (!(this instanceof CBC)) - return new CBC(blockcipherf, keybuf, ivbuf); + return new CBC(blockcipher, keybuf, ivbuf); - this.blockcipherf = blockcipherf; + this.blockcipher = blockcipher; this.keybuf = keybuf; this.ivbuf = ivbuf; }; @@ -26,26 +26,26 @@ CBC.buf2blockbufs = function(buf, blocksize) { return blockbufs; }; -CBC.encrypt = function(messagebuf, ivbuf, blockcipherf, keybuf) { +CBC.encrypt = function(messagebuf, ivbuf, blockcipher, keybuf) { var blocksize = ivbuf.length * 8; var blockbufs = CBC.buf2blockbufs(messagebuf, blocksize); - var encbufs = CBC.encryptblocks(blockbufs, ivbuf, blockcipherf, keybuf); + var encbufs = CBC.encryptblocks(blockbufs, ivbuf, blockcipher, keybuf); var enc = Buffer.concat(encbufs); return enc; }; -CBC.encryptblock = function(blockbuf, ivbuf, blockcipherf, keybuf) { +CBC.encryptblock = function(blockbuf, ivbuf, blockcipher, keybuf) { var xorbuf = CBC.xorbufs(blockbuf, ivbuf); - var encbuf = blockcipherf(xorbuf, keybuf); + var encbuf = blockcipher.encrypt(xorbuf, keybuf); return encbuf; }; -CBC.encryptblocks = function(blockbufs, ivbuf, blockcipherf, keybuf) { +CBC.encryptblocks = function(blockbufs, ivbuf, blockcipher, keybuf) { var encbufs = []; for (var i = 0; i < blockbufs.length; i++) { var blockbuf = blockbufs[i]; - var encbuf = CBC.encryptblock(blockbuf, ivbuf, blockcipherf, keybuf); + var encbuf = CBC.encryptblock(blockbuf, ivbuf, blockcipher, keybuf); encbufs.push(encbuf); diff --git a/test/test.cbc.js b/test/test.cbc.js index 75a757a..7c54ce8 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -46,10 +46,11 @@ describe('CBC', function() { ivbuf.fill(0x10); var keybuf = new Buffer(128 / 8); keybuf.fill(0); - var blockcipherf = function(messagebuf, keybuf) { + var blockcipher = {}; + blockcipher.encrypt = function(messagebuf, keybuf) { return messagebuf; }; - var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipherf, keybuf); + var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, keybuf); encbuf.toString('hex').should.equal('101010101010101010101010101010100000000000000000000000000000000010101010101010101010101010101010'); }); @@ -64,10 +65,11 @@ describe('CBC', function() { ivbuf.fill(0x10); var keybuf = new Buffer(128 / 8); keybuf.fill(0); - var blockcipherf = function(messagebuf, keybuf) { + var blockcipher = {}; + blockcipher.encrypt = function(messagebuf, keybuf) { return messagebuf; }; - var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipherf, keybuf); + var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipher, keybuf); enc.toString('hex').should.equal(ivbuf.toString('hex')); }); @@ -78,10 +80,11 @@ describe('CBC', function() { ivbuf.fill(0x10); var keybuf = new Buffer(128 / 8); keybuf.fill(0); - var blockcipherf = function(messagebuf, keybuf) { + var blockcipher = {}; + blockcipher.encrypt = function(messagebuf, keybuf) { return messagebuf; }; - var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipherf, keybuf); + var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipher, keybuf); enc.toString('hex').should.equal('00000000000000000000000000000000'); }); @@ -98,10 +101,11 @@ describe('CBC', function() { ivbuf.fill(0x10); var keybuf = new Buffer(128 / 8); keybuf.fill(0); - var blockcipherf = function(messagebuf, keybuf) { + var blockcipher = {} + blockcipher.encrypt = function(messagebuf, keybuf) { return messagebuf; }; - var encbufs = CBC.encryptblocks([messagebuf1, messagebuf2], ivbuf, blockcipherf, keybuf); + var encbufs = CBC.encryptblocks([messagebuf1, messagebuf2], ivbuf, blockcipher, keybuf); encbufs[0].toString('hex').should.equal('10101010101010101010101010101010'); encbufs[1].toString('hex').should.equal('00000000000000000000000000000000'); }); From 97e40f9ac693364b47d5828a20f339e8ee87f03f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 25 Aug 2014 16:38:53 -0700 Subject: [PATCH 095/280] rename keybuf -> cipherkeybuf ...since this use of "key" is very different than the public/private key pair that is key.js --- lib/expmt/cbc.js | 18 +++++++++--------- test/test.cbc.js | 32 ++++++++++++++++---------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js index 2596e24..e507da4 100644 --- a/lib/expmt/cbc.js +++ b/lib/expmt/cbc.js @@ -1,12 +1,12 @@ var Random = require('../random'); // http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 -var CBC = function CBC(blockcipher, keybuf, ivbuf) { +var CBC = function CBC(blockcipher, cipherkeybuf, ivbuf) { if (!(this instanceof CBC)) - return new CBC(blockcipher, keybuf, ivbuf); + return new CBC(blockcipher, cipherkeybuf, ivbuf); this.blockcipher = blockcipher; - this.keybuf = keybuf; + this.cipherkeybuf = cipherkeybuf; this.ivbuf = ivbuf; }; @@ -26,26 +26,26 @@ CBC.buf2blockbufs = function(buf, blocksize) { return blockbufs; }; -CBC.encrypt = function(messagebuf, ivbuf, blockcipher, keybuf) { +CBC.encrypt = function(messagebuf, ivbuf, blockcipher, cipherkeybuf) { var blocksize = ivbuf.length * 8; var blockbufs = CBC.buf2blockbufs(messagebuf, blocksize); - var encbufs = CBC.encryptblocks(blockbufs, ivbuf, blockcipher, keybuf); + var encbufs = CBC.encryptblocks(blockbufs, ivbuf, blockcipher, cipherkeybuf); var enc = Buffer.concat(encbufs); return enc; }; -CBC.encryptblock = function(blockbuf, ivbuf, blockcipher, keybuf) { +CBC.encryptblock = function(blockbuf, ivbuf, blockcipher, cipherkeybuf) { var xorbuf = CBC.xorbufs(blockbuf, ivbuf); - var encbuf = blockcipher.encrypt(xorbuf, keybuf); + var encbuf = blockcipher.encrypt(xorbuf, cipherkeybuf); return encbuf; }; -CBC.encryptblocks = function(blockbufs, ivbuf, blockcipher, keybuf) { +CBC.encryptblocks = function(blockbufs, ivbuf, blockcipher, cipherkeybuf) { var encbufs = []; for (var i = 0; i < blockbufs.length; i++) { var blockbuf = blockbufs[i]; - var encbuf = CBC.encryptblock(blockbuf, ivbuf, blockcipher, keybuf); + var encbuf = CBC.encryptblock(blockbuf, ivbuf, blockcipher, cipherkeybuf); encbufs.push(encbuf); diff --git a/test/test.cbc.js b/test/test.cbc.js index 7c54ce8..2c4bb41 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -44,13 +44,13 @@ describe('CBC', function() { var messagebuf = Buffer.concat([messagebuf1, messagebuf2]); var ivbuf = new Buffer(128 / 8); ivbuf.fill(0x10); - var keybuf = new Buffer(128 / 8); - keybuf.fill(0); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); var blockcipher = {}; - blockcipher.encrypt = function(messagebuf, keybuf) { + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { return messagebuf; }; - var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, keybuf); + var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf); encbuf.toString('hex').should.equal('101010101010101010101010101010100000000000000000000000000000000010101010101010101010101010101010'); }); @@ -63,13 +63,13 @@ describe('CBC', function() { messagebuf.fill(0); var ivbuf = new Buffer(128 / 8); ivbuf.fill(0x10); - var keybuf = new Buffer(128 / 8); - keybuf.fill(0); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); var blockcipher = {}; - blockcipher.encrypt = function(messagebuf, keybuf) { + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { return messagebuf; }; - var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipher, keybuf); + var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipher, cipherkeybuf); enc.toString('hex').should.equal(ivbuf.toString('hex')); }); @@ -78,13 +78,13 @@ describe('CBC', function() { messagebuf.fill(0x10); var ivbuf = new Buffer(128 / 8); ivbuf.fill(0x10); - var keybuf = new Buffer(128 / 8); - keybuf.fill(0); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); var blockcipher = {}; - blockcipher.encrypt = function(messagebuf, keybuf) { + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { return messagebuf; }; - var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipher, keybuf); + var enc = CBC.encryptblock(messagebuf, ivbuf, blockcipher, cipherkeybuf); enc.toString('hex').should.equal('00000000000000000000000000000000'); }); @@ -99,13 +99,13 @@ describe('CBC', function() { messagebuf2.fill(0x10); var ivbuf = new Buffer(128 / 8); ivbuf.fill(0x10); - var keybuf = new Buffer(128 / 8); - keybuf.fill(0); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); var blockcipher = {} - blockcipher.encrypt = function(messagebuf, keybuf) { + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { return messagebuf; }; - var encbufs = CBC.encryptblocks([messagebuf1, messagebuf2], ivbuf, blockcipher, keybuf); + var encbufs = CBC.encryptblocks([messagebuf1, messagebuf2], ivbuf, blockcipher, cipherkeybuf); encbufs[0].toString('hex').should.equal('10101010101010101010101010101010'); encbufs[1].toString('hex').should.equal('00000000000000000000000000000000'); }); From eddeb60d7ddd52893bfcab7ecbf56ea68f6f783c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 25 Aug 2014 20:25:18 -0700 Subject: [PATCH 096/280] decrypt block --- lib/expmt/cbc.js | 22 ++++++++++++++++++++++ test/test.cbc.js | 23 +++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js index e507da4..eb24603 100644 --- a/lib/expmt/cbc.js +++ b/lib/expmt/cbc.js @@ -1,5 +1,6 @@ var Random = require('../random'); +// Cipher Block Chaining // http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 var CBC = function CBC(blockcipher, cipherkeybuf, ivbuf) { if (!(this instanceof CBC)) @@ -40,6 +41,12 @@ CBC.encryptblock = function(blockbuf, ivbuf, blockcipher, cipherkeybuf) { return encbuf; }; +CBC.decryptblock = function(encbuf, ivbuf, blockcipher, cipherkeybuf) { + var xorbuf = blockcipher.decrypt(encbuf, cipherkeybuf); + var blockbuf = CBC.xorbufs(xorbuf, ivbuf); + return blockbuf; +}; + CBC.encryptblocks = function(blockbufs, ivbuf, blockcipher, cipherkeybuf) { var encbufs = []; @@ -55,6 +62,21 @@ CBC.encryptblocks = function(blockbufs, ivbuf, blockcipher, cipherkeybuf) { return encbufs; }; +CBC.decryptblocks = function(encbufs, ivbuf, blockcipher, cipherkeybuf) { + var blockbufs = []; + + for (var i = 0; i < encbufs.length; i++) { + var encbuf = encbufs[i]; + var blockbuf = CBC.decryptblock(encbuf, ivbuf, blockcipher, cipherkeybuf); + + blockbufs.push(blockbuf); + + ivbuf = encbuf; + } + + return encbufs; +}; + CBC.pkcs7pad = function(buf, blocksize) { var bytesize = blocksize / 8; var padbytesize = bytesize - buf.length; diff --git a/test/test.cbc.js b/test/test.cbc.js index 2c4bb41..0f56d57 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -90,6 +90,29 @@ describe('CBC', function() { }); + describe('@decryptblock', function() { + + it('should decrypt an encrypted block', function() { + var messagebuf = new Buffer(128 / 8); + messagebuf.fill(0); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); + var blockcipher = {}; + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + blockcipher.decrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + var encbuf = CBC.encryptblock(messagebuf, ivbuf, blockcipher, cipherkeybuf); + var buf = CBC.decryptblock(encbuf, ivbuf, blockcipher, cipherkeybuf); + buf.toString('hex').should.equal(messagebuf.toString('hex')); + }); + + }); + describe('@encryptblocks', function() { it('should return this known value', function() { From a6e74666c88971f526e3c235f660daf7dd87f986 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 25 Aug 2014 20:38:39 -0700 Subject: [PATCH 097/280] decrypt blocks --- lib/expmt/cbc.js | 2 +- test/test.cbc.js | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js index eb24603..c7224c1 100644 --- a/lib/expmt/cbc.js +++ b/lib/expmt/cbc.js @@ -74,7 +74,7 @@ CBC.decryptblocks = function(encbufs, ivbuf, blockcipher, cipherkeybuf) { ivbuf = encbuf; } - return encbufs; + return blockbufs; }; CBC.pkcs7pad = function(buf, blocksize) { diff --git a/test/test.cbc.js b/test/test.cbc.js index 0f56d57..dcd033e 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -135,6 +135,32 @@ describe('CBC', function() { }); + describe('@decryptblocks', function() { + + it('should decrypt encrypted blocks', function() { + var messagebuf1 = new Buffer(128 / 8); + messagebuf1.fill(0); + var messagebuf2 = new Buffer(128 / 8); + messagebuf2.fill(0x10); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); + var blockcipher = {} + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + blockcipher.decrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + var encbufs = CBC.encryptblocks([messagebuf1, messagebuf2], ivbuf, blockcipher, cipherkeybuf); + var bufs = CBC.decryptblocks(encbufs, ivbuf, blockcipher, cipherkeybuf); + bufs[0].toString('hex').should.equal(messagebuf1.toString('hex')); + bufs[1].toString('hex').should.equal(messagebuf2.toString('hex')); + }); + + }); + describe('@pkcs7pad', function() { it('should pad this 32 bit buffer to 128 bits with the number 128/8 - 32/8', function() { From ae319ffcbc533d61246c56557b92d800072eb255 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 25 Aug 2014 20:53:29 -0700 Subject: [PATCH 098/280] unpad --- lib/expmt/cbc.js | 15 ++++++++++++++- test/test.cbc.js | 12 ++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js index c7224c1..8a92e24 100644 --- a/lib/expmt/cbc.js +++ b/lib/expmt/cbc.js @@ -82,7 +82,20 @@ CBC.pkcs7pad = function(buf, blocksize) { var padbytesize = bytesize - buf.length; var pad = new Buffer(padbytesize); pad.fill(padbytesize); - return Buffer.concat([buf, pad]); + var paddedbuf = Buffer.concat([buf, pad]); + return paddedbuf; +}; + +CBC.pkcs7unpad = function(paddedbuf, blocksize) { + var bytesize = blocksize / 8; + var padbytesize = bytesize - paddedbuf.length; + var padlength = paddedbuf[paddedbuf.length - 1]; + var padbuf = paddedbuf.slice(paddedbuf.length - padlength, paddedbuf.length); + var padbuf2 = new Buffer(padlength); + padbuf2.fill(padlength); + if (padbuf.toString('hex') !== padbuf2.toString('hex')) + throw new Error('invalid padding'); + return paddedbuf.slice(0, paddedbuf.length - padlength); }; CBC.xorbufs = function(buf1, buf2) { diff --git a/test/test.cbc.js b/test/test.cbc.js index dcd033e..f93e805 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -176,6 +176,18 @@ describe('CBC', function() { }); + describe('@pkcs7unpad', function() { + + it('should unpad this padded 32 bit buffer', function() { + var buf = new Buffer(32 / 8); + buf.fill(0); + var paddedbuf = CBC.pkcs7pad(buf, 128); + var unpaddedbuf = CBC.pkcs7unpad(paddedbuf, 128); + unpaddedbuf.toString('hex').should.equal(buf.toString('hex')); + }); + + }); + describe('@xorbufs', function() { it('should xor 1 and 0', function() { From dfa190b5a5ee4344c60207d01bcc0e5293c6399a Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 25 Aug 2014 21:02:00 -0700 Subject: [PATCH 099/280] blockbufs2buf --- lib/expmt/cbc.js | 12 ++++++++++++ test/test.cbc.js | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js index 8a92e24..6835924 100644 --- a/lib/expmt/cbc.js +++ b/lib/expmt/cbc.js @@ -27,6 +27,18 @@ CBC.buf2blockbufs = function(buf, blocksize) { return blockbufs; }; +CBC.blockbufs2buf = function(blockbufs, blocksize) { + var bytesize = blocksize / 8; + + var last = blockbufs[blockbufs.length - 1]; + last = CBC.pkcs7unpad(last); + blockbufs[blockbufs.length - 1] = last; + + var buf = Buffer.concat(blockbufs); + + return buf; +}; + CBC.encrypt = function(messagebuf, ivbuf, blockcipher, cipherkeybuf) { var blocksize = ivbuf.length * 8; var blockbufs = CBC.buf2blockbufs(messagebuf, blocksize); diff --git a/test/test.cbc.js b/test/test.cbc.js index f93e805..c03de0c 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -34,6 +34,26 @@ describe('CBC', function() { }); + describe('@buf2blockbufs', function() { + + it('should convert this buffer into one block and back into the same buffer', function() { + var buf = new Buffer(16 - 1); + buf.fill(0); + var blockbufs = CBC.buf2blockbufs(buf, 16 * 8); + var buf2 = CBC.blockbufs2buf(blockbufs, 16 * 8); + buf2.toString('hex').should.equal(buf.toString('hex')); + }); + + it('should convert this buffer into two blocks and back into the same buffer', function() { + var buf = new Buffer(16); + buf.fill(0); + var blockbufs = CBC.buf2blockbufs(buf, 16 * 8); + var buf2 = CBC.blockbufs2buf(blockbufs, 16 * 8); + buf2.toString('hex').should.equal(buf.toString('hex')); + }); + + }); + describe('@encrypt', function() { it('should return this known value', function() { From 73b904ce219ed77934d9c1c5e0bfbe86f3dd4d90 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 25 Aug 2014 21:16:33 -0700 Subject: [PATCH 100/280] CBC decrypt --- lib/expmt/cbc.js | 16 ++++++++++-- test/test.cbc.js | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/lib/expmt/cbc.js b/lib/expmt/cbc.js index 6835924..f509ba8 100644 --- a/lib/expmt/cbc.js +++ b/lib/expmt/cbc.js @@ -43,8 +43,20 @@ CBC.encrypt = function(messagebuf, ivbuf, blockcipher, cipherkeybuf) { var blocksize = ivbuf.length * 8; var blockbufs = CBC.buf2blockbufs(messagebuf, blocksize); var encbufs = CBC.encryptblocks(blockbufs, ivbuf, blockcipher, cipherkeybuf); - var enc = Buffer.concat(encbufs); - return enc; + var encbuf = Buffer.concat(encbufs); + return encbuf; +}; + +CBC.decrypt = function(encbuf, ivbuf, blockcipher, cipherkeybuf) { + var blocksize = ivbuf.length * 8; + var bytesize = ivbuf.length; + var encbufs = []; + for (var i = 0; i < encbuf.length / bytesize; i++) { + encbufs.push(encbuf.slice(i * bytesize, i * bytesize + bytesize)); + } + var blockbufs = CBC.decryptblocks(encbufs, ivbuf, blockcipher, cipherkeybuf); + var buf = CBC.blockbufs2buf(blockbufs, blocksize); + return buf; }; CBC.encryptblock = function(blockbuf, ivbuf, blockcipher, cipherkeybuf) { diff --git a/test/test.cbc.js b/test/test.cbc.js index c03de0c..03ec539 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -76,6 +76,73 @@ describe('CBC', function() { }); + describe('@encrypt', function() { + + it('should return this known value', function() { + var messagebuf1 = new Buffer(128 / 8); + messagebuf1.fill(0); + var messagebuf2 = new Buffer(128 / 8); + messagebuf2.fill(0x10); + var messagebuf = Buffer.concat([messagebuf1, messagebuf2]); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); + var blockcipher = {}; + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + blockcipher.decrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf); + var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf); + }); + + it('should return this shorter known value', function() { + var messagebuf1 = new Buffer(128 / 8); + messagebuf1.fill(0); + var messagebuf2 = new Buffer(120 / 8); + messagebuf2.fill(0x10); + var messagebuf = Buffer.concat([messagebuf1, messagebuf2]); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); + var blockcipher = {}; + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + blockcipher.decrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf); + var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf); + }); + + it('should return this shorter known value', function() { + var messagebuf1 = new Buffer(128 / 8); + messagebuf1.fill(0); + var messagebuf2 = new Buffer(136 / 8); + messagebuf2.fill(0x10); + var messagebuf = Buffer.concat([messagebuf1, messagebuf2]); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); + var blockcipher = {}; + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + blockcipher.decrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf); + var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf); + }); + + }); + describe('@encryptblock', function() { it('should return this known value', function() { From 429a2d099f8cf0e8355531ebe252fcd48bfb53e0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 27 Aug 2014 14:36:23 -0700 Subject: [PATCH 101/280] test decrypt --- test/test.cbc.js | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/test/test.cbc.js b/test/test.cbc.js index 03ec539..2a53751 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -54,28 +54,6 @@ describe('CBC', function() { }); - describe('@encrypt', function() { - - it('should return this known value', function() { - var messagebuf1 = new Buffer(128 / 8); - messagebuf1.fill(0); - var messagebuf2 = new Buffer(128 / 8); - messagebuf2.fill(0x10); - var messagebuf = Buffer.concat([messagebuf1, messagebuf2]); - var ivbuf = new Buffer(128 / 8); - ivbuf.fill(0x10); - var cipherkeybuf = new Buffer(128 / 8); - cipherkeybuf.fill(0); - var blockcipher = {}; - blockcipher.encrypt = function(messagebuf, cipherkeybuf) { - return messagebuf; - }; - var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf); - encbuf.toString('hex').should.equal('101010101010101010101010101010100000000000000000000000000000000010101010101010101010101010101010'); - }); - - }); - describe('@encrypt', function() { it('should return this known value', function() { @@ -143,6 +121,32 @@ describe('CBC', function() { }); + describe('decrypt', function() { + + it('', function() { + var messagebuf1 = new Buffer(128 / 8); + messagebuf1.fill(0); + var messagebuf2 = new Buffer(128 / 8); + messagebuf2.fill(0x10); + var messagebuf = Buffer.concat([messagebuf1, messagebuf2]); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); + var blockcipher = {}; + blockcipher.encrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + blockcipher.decrypt = function(messagebuf, cipherkeybuf) { + return messagebuf; + }; + var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf); + var messagebuf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf); + messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); + }); + + }); + describe('@encryptblock', function() { it('should return this known value', function() { From ce98b87d90d417d4a65f589678304d07e977ec98 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 27 Aug 2014 14:37:01 -0700 Subject: [PATCH 102/280] add it description --- test/test.cbc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.cbc.js b/test/test.cbc.js index 2a53751..2a949e6 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -123,7 +123,7 @@ describe('CBC', function() { describe('decrypt', function() { - it('', function() { + it('should properly decrypt an encrypted message', function() { var messagebuf1 = new Buffer(128 / 8); messagebuf1.fill(0); var messagebuf2 = new Buffer(128 / 8); From a8e3e0b99376fdce7cd2ea4725ba25461ecca708 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 27 Aug 2014 14:37:28 -0700 Subject: [PATCH 103/280] static methods start with @ in tests --- test/test.cbc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.cbc.js b/test/test.cbc.js index 2a949e6..0243359 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -121,7 +121,7 @@ describe('CBC', function() { }); - describe('decrypt', function() { + describe('@decrypt', function() { it('should properly decrypt an encrypted message', function() { var messagebuf1 = new Buffer(128 / 8); From ed826bf95c023f5e53316af0f0cbc78e2cfe5a50 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 27 Aug 2014 15:37:54 -0700 Subject: [PATCH 104/280] add test to make sure AES works with CBC --- test/test.cbc.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test.cbc.js b/test/test.cbc.js index 0243359..704a207 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -1,3 +1,4 @@ +var AES = require('../lib/expmt/aes'); var should = require('chai').should(); var CBC = require('../lib/expmt/cbc'); @@ -119,6 +120,21 @@ describe('CBC', function() { var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf); }); + it('should encrypt something with AES', function() { + var messagebuf1 = new Buffer(128 / 8); + messagebuf1.fill(0); + var messagebuf2 = new Buffer(128 / 8); + messagebuf2.fill(0x10); + var messagebuf = Buffer.concat([messagebuf1, messagebuf2]); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0x10); + var cipherkeybuf = new Buffer(128 / 8); + cipherkeybuf.fill(0); + var blockcipher = AES; + var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf); + var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf); + }); + }); describe('@decrypt', function() { From ae02a878ddee470e18c3c45945698c419b1b68b8 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 27 Aug 2014 15:57:46 -0700 Subject: [PATCH 105/280] woops ... actually check that output is correct --- test/test.cbc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test.cbc.js b/test/test.cbc.js index 704a207..1b163f5 100644 --- a/test/test.cbc.js +++ b/test/test.cbc.js @@ -133,6 +133,7 @@ describe('CBC', function() { var blockcipher = AES; var encbuf = CBC.encrypt(messagebuf, ivbuf, blockcipher, cipherkeybuf); var buf2 = CBC.decrypt(encbuf, ivbuf, blockcipher, cipherkeybuf); + buf2.toString('hex').should.equal(messagebuf.toString('hex')); }); }); From 1cb2f900af100a9f92ab8b695b0fe3c62ca8e715 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 27 Aug 2014 17:15:10 -0700 Subject: [PATCH 106/280] symmetric encryption convenience class --- lib/expmt/encryption.js | 33 +++++++++++++++++++ test/test.encryption.js | 73 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 lib/expmt/encryption.js create mode 100644 test/test.encryption.js diff --git a/lib/expmt/encryption.js b/lib/expmt/encryption.js new file mode 100644 index 0000000..dbb271c --- /dev/null +++ b/lib/expmt/encryption.js @@ -0,0 +1,33 @@ +var AES = require('./aes'); +var CBC = require('./cbc'); +var Random = require('../random'); +var Hash = require('../hash'); + +var Encryption = function Encryption() { +}; + +Encryption.encrypt = function(messagebuf, passwordstr) { + var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); + return Encryption.encryptCipherkey(messagebuf, cipherkeybuf); +}; + +Encryption.decrypt = function(encbuf, passwordstr) { + var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); + return Encryption.decryptCipherkey(encbuf, cipherkeybuf); +}; + +Encryption.encryptCipherkey = function(messagebuf, cipherkeybuf, ivbuf) { + ivbuf = ivbuf || Random.getRandomBuffer(128 / 8); + var ctbuf = CBC.encrypt(messagebuf, ivbuf, AES, cipherkeybuf); + var encbuf = Buffer.concat([ivbuf, ctbuf]); + return encbuf; +}; + +Encryption.decryptCipherkey = function(encbuf, cipherkeybuf) { + var ivbuf = encbuf.slice(0, 128 / 8); + var ctbuf = encbuf.slice(128 / 8); + var messagebuf = CBC.decrypt(ctbuf, ivbuf, AES, cipherkeybuf); + return messagebuf; +}; + +module.exports = Encryption; diff --git a/test/test.encryption.js b/test/test.encryption.js new file mode 100644 index 0000000..2c49fd1 --- /dev/null +++ b/test/test.encryption.js @@ -0,0 +1,73 @@ +var should = require('chai').should(); +var Encryption = require('../lib/expmt/encryption'); + +describe('Encryption', function() { + + describe('@encrypt', function() { + + it('should return encrypt one block', function() { + var password = "password"; + var messagebuf = new Buffer(128 / 8 - 1); + messagebuf.fill(0); + var encbuf = Encryption.encrypt(messagebuf, password); + encbuf.length.should.equal(128 / 8 + 128 / 8); + }); + + }); + + describe('@decrypt', function() { + + it('should decrypt that which was encrypted', function() { + var password = "password"; + var messagebuf = new Buffer(128 / 8 - 1); + messagebuf.fill(0); + var encbuf = Encryption.encrypt(messagebuf, password); + var messagebuf2 = Encryption.decrypt(encbuf, password); + messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); + }); + + }); + + describe('@encryptCipherkey', function() { + + it('should return encrypt one block', function() { + var cipherkeybuf = new Buffer(256 / 8); + cipherkeybuf.fill(0x10); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0); + var messagebuf = new Buffer(128 / 8 - 1); + messagebuf.fill(0); + var encbuf = Encryption.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); + encbuf.length.should.equal(128 / 8 + 128 / 8); + }); + + it('should return encrypt two blocks', function() { + var cipherkeybuf = new Buffer(256 / 8); + cipherkeybuf.fill(0x10); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0); + var messagebuf = new Buffer(128 / 8); + messagebuf.fill(0); + var encbuf = Encryption.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); + encbuf.length.should.equal(128 / 8 + 128 / 8 + 128 / 8); + }); + + }); + + describe('@decryptCipherkey', function() { + + it('should decrypt that which was encrypted', function() { + var cipherkeybuf = new Buffer(256 / 8); + cipherkeybuf.fill(0x10); + var ivbuf = new Buffer(128 / 8); + ivbuf.fill(0); + var messagebuf = new Buffer(128 / 8); + messagebuf.fill(0); + var encbuf = Encryption.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); + var messagebuf2 = Encryption.decryptCipherkey(encbuf, cipherkeybuf); + messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); + }); + + }); + +}); From 6f54d4cde691fb7c3a94de7665954550b16da6df Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 11:10:40 -0700 Subject: [PATCH 107/280] Encryption -> SymEnc This is more explanatory ("symmetric encryption") and also does not encourage its use for people who don't know what they're doing. (It should only be used in combination with some type of message authentication.) --- lib/expmt/{encryption.js => symenc.js} | 16 ++++++++-------- test/{test.encryption.js => test.symenc.js} | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) rename lib/expmt/{encryption.js => symenc.js} (57%) rename test/{test.encryption.js => test.symenc.js} (74%) diff --git a/lib/expmt/encryption.js b/lib/expmt/symenc.js similarity index 57% rename from lib/expmt/encryption.js rename to lib/expmt/symenc.js index dbb271c..23629b4 100644 --- a/lib/expmt/encryption.js +++ b/lib/expmt/symenc.js @@ -3,31 +3,31 @@ var CBC = require('./cbc'); var Random = require('../random'); var Hash = require('../hash'); -var Encryption = function Encryption() { +var SymEnc = function SymEnc() { }; -Encryption.encrypt = function(messagebuf, passwordstr) { +SymEnc.encrypt = function(messagebuf, passwordstr) { var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); - return Encryption.encryptCipherkey(messagebuf, cipherkeybuf); + return SymEnc.encryptCipherkey(messagebuf, cipherkeybuf); }; -Encryption.decrypt = function(encbuf, passwordstr) { +SymEnc.decrypt = function(encbuf, passwordstr) { var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); - return Encryption.decryptCipherkey(encbuf, cipherkeybuf); + return SymEnc.decryptCipherkey(encbuf, cipherkeybuf); }; -Encryption.encryptCipherkey = function(messagebuf, cipherkeybuf, ivbuf) { +SymEnc.encryptCipherkey = function(messagebuf, cipherkeybuf, ivbuf) { ivbuf = ivbuf || Random.getRandomBuffer(128 / 8); var ctbuf = CBC.encrypt(messagebuf, ivbuf, AES, cipherkeybuf); var encbuf = Buffer.concat([ivbuf, ctbuf]); return encbuf; }; -Encryption.decryptCipherkey = function(encbuf, cipherkeybuf) { +SymEnc.decryptCipherkey = function(encbuf, cipherkeybuf) { var ivbuf = encbuf.slice(0, 128 / 8); var ctbuf = encbuf.slice(128 / 8); var messagebuf = CBC.decrypt(ctbuf, ivbuf, AES, cipherkeybuf); return messagebuf; }; -module.exports = Encryption; +module.exports = SymEnc; diff --git a/test/test.encryption.js b/test/test.symenc.js similarity index 74% rename from test/test.encryption.js rename to test/test.symenc.js index 2c49fd1..dc56eec 100644 --- a/test/test.encryption.js +++ b/test/test.symenc.js @@ -1,7 +1,7 @@ var should = require('chai').should(); -var Encryption = require('../lib/expmt/encryption'); +var SymEnc = require('../lib/expmt/symenc'); -describe('Encryption', function() { +describe('SymEnc', function() { describe('@encrypt', function() { @@ -9,7 +9,7 @@ describe('Encryption', function() { var password = "password"; var messagebuf = new Buffer(128 / 8 - 1); messagebuf.fill(0); - var encbuf = Encryption.encrypt(messagebuf, password); + var encbuf = SymEnc.encrypt(messagebuf, password); encbuf.length.should.equal(128 / 8 + 128 / 8); }); @@ -21,8 +21,8 @@ describe('Encryption', function() { var password = "password"; var messagebuf = new Buffer(128 / 8 - 1); messagebuf.fill(0); - var encbuf = Encryption.encrypt(messagebuf, password); - var messagebuf2 = Encryption.decrypt(encbuf, password); + var encbuf = SymEnc.encrypt(messagebuf, password); + var messagebuf2 = SymEnc.decrypt(encbuf, password); messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); }); @@ -37,7 +37,7 @@ describe('Encryption', function() { ivbuf.fill(0); var messagebuf = new Buffer(128 / 8 - 1); messagebuf.fill(0); - var encbuf = Encryption.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); + var encbuf = SymEnc.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); encbuf.length.should.equal(128 / 8 + 128 / 8); }); @@ -48,7 +48,7 @@ describe('Encryption', function() { ivbuf.fill(0); var messagebuf = new Buffer(128 / 8); messagebuf.fill(0); - var encbuf = Encryption.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); + var encbuf = SymEnc.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); encbuf.length.should.equal(128 / 8 + 128 / 8 + 128 / 8); }); @@ -63,8 +63,8 @@ describe('Encryption', function() { ivbuf.fill(0); var messagebuf = new Buffer(128 / 8); messagebuf.fill(0); - var encbuf = Encryption.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); - var messagebuf2 = Encryption.decryptCipherkey(encbuf, cipherkeybuf); + var encbuf = SymEnc.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); + var messagebuf2 = SymEnc.decryptCipherkey(encbuf, cipherkeybuf); messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); }); From 5fdc778cf572f23db8ac3bc05d64c579d79482da Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 14:27:47 -0700 Subject: [PATCH 108/280] expose SymEnc --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index a18dc67..097bf4b 100644 --- a/index.js +++ b/index.js @@ -24,6 +24,7 @@ bitcore.Signature = require('./lib/signature'); bitcore.expmt = {}; bitcore.expmt.AES = require('./lib/expmt/aes'); bitcore.expmt.CBC = require('./lib/expmt/cbc'); +bitcore.expmt.SymEnc = require('./lib/expmt/symenc'); bitcore.expmt.Stealth = require('./lib/expmt/stealth'); //dependencies, subject to change From cc316e94555e6210d3582ac43fd71181ef78ddc9 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 14:43:21 -0700 Subject: [PATCH 109/280] ECIES --- index.js | 1 + lib/expmt/ecies.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++ test/test.ecies.js | 41 ++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 lib/expmt/ecies.js create mode 100644 test/test.ecies.js diff --git a/index.js b/index.js index 097bf4b..18db7a0 100644 --- a/index.js +++ b/index.js @@ -24,6 +24,7 @@ bitcore.Signature = require('./lib/signature'); bitcore.expmt = {}; bitcore.expmt.AES = require('./lib/expmt/aes'); bitcore.expmt.CBC = require('./lib/expmt/cbc'); +bitcore.expmt.ECIES = require('./lib/ecies'); bitcore.expmt.SymEnc = require('./lib/expmt/symenc'); bitcore.expmt.Stealth = require('./lib/expmt/stealth'); diff --git a/lib/expmt/ecies.js b/lib/expmt/ecies.js new file mode 100644 index 0000000..b96cf2a --- /dev/null +++ b/lib/expmt/ecies.js @@ -0,0 +1,52 @@ +var SymEnc = require('./symenc'); +var Key = require('../key'); +var Point = require('../point'); +var Hash = require('../hash'); +var Pubkey = require('../pubkey'); + +// http://en.wikipedia.org/wiki/Integrated_Encryption_Scheme +var ECIES = function ECIES() { + if (!(this instanceof ECIES)) + return new ECIES(); +}; + +ECIES.encrypt = function(messagebuf, tokey, fromkey, ivbuf) { + var r = fromkey.privkey.bn; + var R = fromkey.pubkey.point; + var Rpubkey = fromkey.pubkey; + var Rbuf = Rpubkey.toDER(true); + var KB = tokey.pubkey.point; + var P = KB.mul(r); + var S = P.getX(); + var Sbuf = S.toBuffer({size: 32}); + var kEkM = Hash.sha512(Sbuf); + var kE = kEkM.slice(0, 32); + var kM = kEkM.slice(32, 64); + var c = SymEnc.encryptCipherkey(messagebuf, kE, ivbuf); + var d = Hash.sha256hmac(c, kM); + var encbuf = Buffer.concat([Rbuf, c, d]); + return encbuf; +}; + +ECIES.decrypt = function(encbuf, tokey) { + var kB = tokey.privkey.bn; + var frompubkey = Pubkey().fromDER(encbuf.slice(0, 33)); + var R = frompubkey.point; + var P = R.mul(kB); + if (P.eq(new Point())) + throw new Error('P equals 0'); + var S = P.getX(); + var Sbuf = S.toBuffer({size: 32}); + var kEkM = Hash.sha512(Sbuf); + var kE = kEkM.slice(0, 32); + var kM = kEkM.slice(32, 64); + var c = encbuf.slice(33, encbuf.length - 32); + var d = encbuf.slice(encbuf.length - 32, encbuf.length); + var d2 = Hash.sha256hmac(c, kM); + if (d.toString('hex') !== d2.toString('hex')) + throw new Error('Invalid checksum'); + var messagebuf = SymEnc.decryptCipherkey(c, kE); + return messagebuf; +}; + +module.exports = ECIES; diff --git a/test/test.ecies.js b/test/test.ecies.js new file mode 100644 index 0000000..4ee661b --- /dev/null +++ b/test/test.ecies.js @@ -0,0 +1,41 @@ +var ECIES = require('../lib/expmt/ecies'); +var should = require('chai').should(); +var Key = require('../lib/key'); +var Hash = require('../lib/hash'); + +describe('#ECIES', function() { + + it('should make a new ECIES object', function() { + var ecies = new ECIES(); + should.exist(ecies); + }); + + it('should make a new ECIES object when called without "new"', function() { + var ecies = ECIES(); + should.exist(ecies); + }); + + var fromkey = Key().fromRandom(); + var tokey = Key().fromRandom(); + var messagebuf = Hash.sha256(new Buffer('my message is the hash of this string')); + + describe('@encrypt', function() { + + it('should return a buffer', function() { + var encbuf = ECIES.encrypt(messagebuf, tokey, fromkey); + Buffer.isBuffer(encbuf).should.equal(true); + }); + + }); + + describe('@decrypt', function() { + + it('should decrypt that which was encrypted', function() { + var encbuf = ECIES.encrypt(messagebuf, tokey, fromkey); + var messagebuf2 = ECIES.decrypt(encbuf, tokey); + messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); + }); + + }); + +}); From f52e679f9353ae9e8212b32bae6180da5651e3f3 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 15:18:48 -0700 Subject: [PATCH 110/280] refactor address - use "set" function ...intend for this to become standard throughout the lib --- lib/address.js | 52 ++++++++++++++++++++++++-------------------- test/test.address.js | 22 +++++++++---------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/lib/address.js b/lib/address.js index 608d0b8..4015676 100644 --- a/lib/address.js +++ b/lib/address.js @@ -3,18 +3,24 @@ var constants = require('./constants'); var Hash = require('./hash'); var Pubkey = require('./pubkey'); -function Address(hashbuf, network, type) { +function Address(obj) { if (!(this instanceof Address)) - return new Address(hashbuf, network, type); - this.hashbuf = hashbuf; - this.network = network; - this.type = type; + return new Address(obj); + if (obj) + this.set(obj); }; -Address.prototype.fromPubkey = function(pubkey, network) { +Address.prototype.set = function(obj) { + this.hashbuf = obj.hashbuf || this.hashbuf || null; + this.networkstr = obj.networkstr || this.networkstr || 'mainnet'; + this.typestr = obj.typestr || this.typestr || 'pubkeyhash'; + return this; +}; + +Address.prototype.fromPubkey = function(pubkey, networkstr) { this.hashbuf = Hash.sha256ripemd160(pubkey.toBuffer()); - this.network = network || 'mainnet'; - this.type = 'pubkeyhash'; + this.networkstr = networkstr || 'mainnet'; + this.typestr = 'pubkeyhash'; return this; }; @@ -24,20 +30,20 @@ Address.prototype.fromString = function(str) { throw new Error('Address buffers must be exactly 21 bytes'); var version = buf[0]; if (version === constants['mainnet']['pubkeyhash']) { - this.network = 'mainnet'; - this.type = 'pubkeyhash'; + this.networkstr = 'mainnet'; + this.typestr = 'pubkeyhash'; } else if (version === constants['mainnet']['p2sh']) { - this.network = 'mainnet'; - this.type = 'p2sh'; + this.networkstr = 'mainnet'; + this.typestr = 'p2sh'; } else if (version === constants['testnet']['pubkeyhash']) { - this.network = 'testnet'; - this.type = 'pubkeyhash'; + this.networkstr = 'testnet'; + this.typestr = 'pubkeyhash'; } else if (version === constants['testnet']['p2sh']) { - this.network = 'testnet'; - this.type = 'p2sh'; + this.networkstr = 'testnet'; + this.typestr = 'p2sh'; } else { - this.network = 'unknown'; - this.type = 'unknown'; + this.networkstr = 'unknown'; + this.typestr = 'unknown'; } this.hashbuf = buf.slice(1); @@ -55,7 +61,7 @@ Address.prototype.isValid = function() { }; Address.prototype.toBuffer = function() { - version = new Buffer([constants[this.network][this.type]]); + version = new Buffer([constants[this.networkstr][this.typestr]]); var buf = Buffer.concat([version, this.hashbuf]); return buf; }; @@ -67,10 +73,10 @@ Address.prototype.toString = function() { Address.prototype.validate = function() { if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 20) throw new Error('hash must be a buffer of 20 bytes'); - if (this.network !== 'mainnet' && this.network !== 'testnet') - throw new Error('network must be "mainnet" or "testnet"'); - if (this.type !== 'pubkeyhash' && this.type !== 'p2sh') - throw new Error('type must be "pubkeyhash" or "p2sh"'); + if (this.networkstr !== 'mainnet' && this.networkstr !== 'testnet') + throw new Error('networkstr must be "mainnet" or "testnet"'); + if (this.typestr !== 'pubkeyhash' && this.typestr !== 'p2sh') + throw new Error('typestr must be "pubkeyhash" or "p2sh"'); return this; }; diff --git a/test/test.address.js b/test/test.address.js index bdd8c93..45959a1 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -44,7 +44,7 @@ describe('Address', function() { it('should derive from this known address string testnet', function() { var address = new Address(); address.fromString(str); - address.network = 'testnet'; + address.networkstr = 'testnet'; address.fromString(address.toString()); address.toString().should.equal('mm1X5M2QWyHVjn7txrF7mmtZDpjCXzoa98'); }); @@ -52,8 +52,8 @@ describe('Address', function() { it('should derive from this known address string mainnet p2sh', function() { var address = new Address(); address.fromString(str); - address.network = 'mainnet'; - address.type = 'p2sh'; + address.networkstr = 'mainnet'; + address.typestr = 'p2sh'; address.fromString(address.toString()); address.toString().should.equal('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); }); @@ -61,8 +61,8 @@ describe('Address', function() { 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.networkstr = 'testnet'; + address.typestr = 'p2sh'; address.fromString(address.toString()); address.toString().should.equal('2MxjnmaMtsJfyFcyG3WZCzS2RihdNuWqeX4'); }); @@ -80,14 +80,14 @@ describe('Address', function() { it('should describe this address with unknown network as invalid', function() { var address = new Address(); address.fromString('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); - address.network = 'unknown'; + address.networkstr = '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.typestr = 'unknown'; address.isValid().should.equal(false); }); @@ -124,19 +124,19 @@ describe('Address', function() { it('should throw an error on this invalid network', function() { var address = new Address(); address.fromString(str); - address.network = 'unknown'; + address.networkstr = 'unknown'; (function() { address.validate(); - }).should.throw('network must be "mainnet" or "testnet"'); + }).should.throw('networkstr 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'; + address.typestr = 'unknown'; (function() { address.validate(); - }).should.throw('type must be "pubkeyhash" or "p2sh"'); + }).should.throw('typestr must be "pubkeyhash" or "p2sh"'); }); }); From 6b7592d67b30ead71e7c3895ba679548a066afca Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 15:22:03 -0700 Subject: [PATCH 111/280] add address validation convenience function --- lib/address.js | 6 ++++++ test/test.address.js | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/address.js b/lib/address.js index 4015676..fb50e6f 100644 --- a/lib/address.js +++ b/lib/address.js @@ -10,6 +10,12 @@ function Address(obj) { this.set(obj); }; +Address.validate = function(addrstr) { + var address = new Address().fromString(addrstr); + address.validate(); + return this; +}; + Address.prototype.set = function(obj) { this.hashbuf = obj.hashbuf || this.hashbuf || null; this.networkstr = obj.networkstr || this.networkstr || 'mainnet'; diff --git a/test/test.address.js b/test/test.address.js index 45959a1..9b9ff50 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -12,6 +12,14 @@ describe('Address', function() { should.exist(address); }); + describe('@validate', function() { + + it('should validate this valid address string', function() { + should.exist(Address.validate(str)) + }); + + }); + describe('#fromPubkey', function() { it('should make this address from a compressed pubkey', function() { From da8989b64943cc6323bd4c594263e104bdda7ff0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 15:27:58 -0700 Subject: [PATCH 112/280] add set function to Base58 --- lib/base58.js | 12 +++++++++--- test/test.base58.js | 12 ++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/base58.js b/lib/base58.js index 1bb24f8..da977ca 100644 --- a/lib/base58.js +++ b/lib/base58.js @@ -1,9 +1,15 @@ var bs58 = require('bs58'); -var Base58 = function Base58(buf) { +var Base58 = function Base58(obj) { if (!(this instanceof Base58)) - return new Base58(buf); - this.buf = buf; + return new Base58(obj); + if (obj) + this.set(obj); +}; + +Base58.prototype.set = function(obj) { + this.buf = obj.buf || this.buf || undefined; + return this; }; Base58.encode = function(buf) { diff --git a/test/test.base58.js b/test/test.base58.js index 236f5d0..fca7d9a 100644 --- a/test/test.base58.js +++ b/test/test.base58.js @@ -15,6 +15,14 @@ describe('Base58', function() { should.exist(b58); }); + describe('#set', function() { + + it('should set a blank buffer', function() { + Base58().set({buf: new Buffer([])}); + }); + + }); + describe('@encode', function() { it('should encode the buffer accurately', function() { @@ -67,7 +75,7 @@ describe('Base58', function() { describe('#toBuffer', function() { it('should return the buffer', function() { - var b58 = Base58(buf); + var b58 = Base58({buf: buf}); b58.buf.toString('hex').should.equal(buf.toString('hex')); }); @@ -76,7 +84,7 @@ describe('Base58', function() { describe('#toString', function() { it('should return the buffer', function() { - var b58 = Base58(buf); + var b58 = Base58({buf: buf}); b58.toString().should.equal(enc); }); From d50d766352a0ee0da13b7e6623958ff6d5b58e17 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 15:31:06 -0700 Subject: [PATCH 113/280] Base58Check.prototype.set --- lib/base58check.js | 12 +++++++++--- test/test.base58check.js | 12 ++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/base58check.js b/lib/base58check.js index d211c39..9bc78ee 100644 --- a/lib/base58check.js +++ b/lib/base58check.js @@ -1,10 +1,16 @@ var base58 = require('./base58'); var sha256sha256 = require('./hash').sha256sha256; -var Base58Check = function Base58Check(buf) { +var Base58Check = function Base58Check(obj) { if (!(this instanceof Base58Check)) - return new Base58Check(buf); - this.buf = buf; + return new Base58Check(obj); + if (obj) + this.set(obj); +}; + +Base58Check.prototype.set = function(obj) { + this.buf = obj.buf || this.buf || undefined; + return this; }; Base58Check.decode = function(s) { diff --git a/test/test.base58check.js b/test/test.base58check.js index 58e2bb6..635198c 100644 --- a/test/test.base58check.js +++ b/test/test.base58check.js @@ -16,6 +16,14 @@ describe('Base58Check', function() { should.exist(b58); }); + describe('#set', function() { + + it('should set a buf', function() { + should.exist(Base58Check().set({buf: buf}).buf); + }); + + }); + describe('@encode', function() { it('should encode the buffer accurately', function() { @@ -83,7 +91,7 @@ describe('Base58Check', function() { describe('#toBuffer', function() { it('should return the buffer', function() { - var b58 = Base58Check(buf); + var b58 = Base58Check({buf: buf}); b58.buf.toString('hex').should.equal(buf.toString('hex')); }); @@ -92,7 +100,7 @@ describe('Base58Check', function() { describe('#toString', function() { it('should return the buffer', function() { - var b58 = Base58Check(buf); + var b58 = Base58Check({buf: buf}); b58.toString().should.equal(enc); }); From 3919b246852adb5e25443026f60d47e4f743909f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 15:38:29 -0700 Subject: [PATCH 114/280] make bip32 mostly compatible with the rest of the lib the constructor shouldn't do much. just set some varibles. in this case, i have yet to write the code that sets the varibles. but better this than autogenerating a new random BIP32. for that, call fromRandom() --- lib/bip32.js | 12 +++---- test/test.bip32.js | 82 +++++++++++++++++++++++----------------------- 2 files changed, 45 insertions(+), 49 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 4e84fff..ffac16b 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -8,15 +8,9 @@ var Random = require('./random'); var bn = require('./bn'); var constants = require('./constants'); -var BIP32 = function BIP32(str) { +var BIP32 = function BIP32() { if (!(this instanceof BIP32)) - return new BIP32(str); - if (str === 'testnet' || str === 'mainnet') { - this.version = constants[str].bip32privkey; - this.fromRandom(); - } - else if (str) - this.fromString(str); + return new BIP32(); } BIP32.prototype.fromRandom = function(network) { @@ -48,6 +42,8 @@ BIP32.prototype.fromString = function(str) { if (bytes !== undefined && bytes !== null) this.initFromBytes(bytes); + + return this; }; BIP32.prototype.fromSeed = function(bytes, network) { diff --git a/test/test.bip32.js b/test/test.bip32.js index 971318f..82643b7 100644 --- a/test/test.bip32.js +++ b/test/test.bip32.js @@ -43,224 +43,224 @@ describe('BIP32', function() { }); it('should initialize test vector 1 from the extended public key', function() { - var bip32 = new BIP32(vector1_m_public); + var bip32 = new BIP32().fromString(vector1_m_public); should.exist(bip32); }); it('should initialize test vector 1 from the extended private key', function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); should.exist(bip32); }); it('should get the extended public key from the extended private key for test vector 1', function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); bip32.extendedPublicKeyString().should.equal(vector1_m_public); }); it("should get m/0' ext. private key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector1_m0h_private); }); it("should get m/0' ext. public key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'"); should.exist(child); child.extendedPublicKeyString().should.equal(vector1_m0h_public); }); it("should get m/0'/1 ext. private key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector1_m0h1_private); }); it("should get m/0'/1 ext. public key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1"); should.exist(child); child.extendedPublicKeyString().should.equal(vector1_m0h1_public); }); it("should get m/0'/1 ext. public key from m/0' public key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'"); - var child_pub = new BIP32(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); var child2 = child_pub.derive("m/1"); should.exist(child2); child2.extendedPublicKeyString().should.equal(vector1_m0h1_public); }); it("should get m/0'/1/2h ext. private key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector1_m0h12h_private); }); it("should get m/0'/1/2h ext. public key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'"); should.exist(child); child.extendedPublicKeyString().should.equal(vector1_m0h12h_public); }); it("should get m/0'/1/2h/2 ext. private key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector1_m0h12h2_private); }); it("should get m/0'/1/2'/2 ext. public key from m/0'/1/2' public key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'"); - var child_pub = new BIP32(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); var child2 = child_pub.derive("m/2"); should.exist(child2); child2.extendedPublicKeyString().should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2 ext. public key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2"); should.exist(child); child.extendedPublicKeyString().should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2/1000000000 ext. private key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2/1000000000"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector1_m0h12h21000000000_private); }); it("should get m/0'/1/2h/2/1000000000 ext. public key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2/1000000000"); should.exist(child); child.extendedPublicKeyString().should.equal(vector1_m0h12h21000000000_public); }); it("should get m/0'/1/2'/2/1000000000 ext. public key from m/0'/1/2'/2 public key from test vector 1", function() { - var bip32 = new BIP32(vector1_m_private); + var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2"); - var child_pub = new BIP32(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); var child2 = child_pub.derive("m/1000000000"); should.exist(child2); child2.extendedPublicKeyString().should.equal(vector1_m0h12h21000000000_public); }); it('should initialize test vector 2 from the extended public key', function() { - var bip32 = new BIP32(vector2_m_public); + var bip32 = new BIP32().fromString(vector2_m_public); should.exist(bip32); }); it('should initialize test vector 2 from the extended private key', function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); should.exist(bip32); }); it('should get the extended public key from the extended private key for test vector 2', function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); bip32.extendedPublicKeyString().should.equal(vector2_m_public); }); it("should get m/0 ext. private key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector2_m0_private); }); it("should get m/0 ext. public key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0"); should.exist(child); child.extendedPublicKeyString().should.equal(vector2_m0_public); }); it("should get m/0 ext. public key from m public key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m"); - var child_pub = new BIP32(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); var child2 = child_pub.derive("m/0"); should.exist(child2); child2.extendedPublicKeyString().should.equal(vector2_m0_public); }); it("should get m/0/2147483647h ext. private key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector2_m02147483647h_private); }); it("should get m/0/2147483647h ext. public key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'"); should.exist(child); child.extendedPublicKeyString().should.equal(vector2_m02147483647h_public); }); it("should get m/0/2147483647h/1 ext. private key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector2_m02147483647h1_private); }); it("should get m/0/2147483647h/1 ext. public key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1"); should.exist(child); child.extendedPublicKeyString().should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1 ext. public key from m/0/2147483647h public key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'"); - var child_pub = new BIP32(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); var child2 = child_pub.derive("m/1"); should.exist(child2); child2.extendedPublicKeyString().should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1/2147483646h ext. private key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector2_m02147483647h12147483646h_private); }); it("should get m/0/2147483647h/1/2147483646h ext. public key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'"); should.exist(child); child.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. private key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'/2"); should.exist(child); child.extendedPrivateKeyString().should.equal(vector2_m02147483647h12147483646h2_private); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'/2"); should.exist(child); child.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h2_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from m/0/2147483647h/2147483646h public key from test vector 2", function() { - var bip32 = new BIP32(vector2_m_private); + var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'"); - var child_pub = new BIP32(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); var child2 = child_pub.derive("m/2"); should.exist(child2); child2.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h2_public); @@ -289,7 +289,7 @@ describe('BIP32', function() { it('should initialize a new BIP32 correctly from a random BIP32', function() { var b1 = new BIP32(); b1.fromRandom('testnet'); - var b2 = new BIP32(b1.extendedPublicKeyString()); + var b2 = new BIP32().fromString(b1.extendedPublicKeyString()); b2.extendedPublicKeyString().should.equal(b1.extendedPublicKeyString()); }); @@ -311,7 +311,7 @@ describe('BIP32', function() { }); it('should return an xpub string', function() { - var bip32b = new BIP32(bip32.extendedPublicKeyString()); + var bip32b = new BIP32().fromString(bip32.extendedPublicKeyString()); bip32b.toString().slice(0, 4).should.equal('xpub'); }); @@ -320,7 +320,7 @@ describe('BIP32', function() { }); it('should return a tpub string', function() { - var tip32b = new BIP32(tip32.extendedPublicKeyString()); + var tip32b = new BIP32().fromString(tip32.extendedPublicKeyString()); tip32b.toString().slice(0, 4).should.equal('tpub'); }); From 3475ee973ca6352e8b83fd97d78357b853482f2b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 15:52:32 -0700 Subject: [PATCH 115/280] BufferReader.prototype.set --- lib/bufferreader.js | 16 ++++++++---- test/test.bufferreader.js | 52 ++++++++++++++++++++++----------------- test/test.bufferwriter.js | 2 +- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index 35a305c..cf6b891 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -1,10 +1,16 @@ var BN = require('./bn'); -var BufferReader = function BufferReader(buf, pos) { +var BufferReader = function BufferReader(obj) { if (!(this instanceof BufferReader)) - return new BufferReader(buf); - this.buf = buf; - this.pos = pos || 0; + return new BufferReader(obj); + if (obj) + this.set(obj); +}; + +BufferReader.prototype.set = function(obj) { + this.buf = obj.buf || this.buf || undefined; + this.pos = obj.pos || this.pos || 0; + return this; }; BufferReader.prototype.eof = function eof() { @@ -56,7 +62,7 @@ BufferReader.prototype.readUInt64BEBN = function() { BufferReader.prototype.readUInt64LEBN = function() { var buf = this.buf.slice(this.pos, this.pos + 8); - var reversebuf = BufferReader(buf).reverse().read(); + var reversebuf = BufferReader({buf: buf}).reverse().read(); var bn = BN().fromBuffer(reversebuf); this.pos = this.pos + 8; return bn; diff --git a/test/test.bufferreader.js b/test/test.bufferreader.js index cf0a25e..d4ffdb3 100644 --- a/test/test.bufferreader.js +++ b/test/test.bufferreader.js @@ -9,10 +9,18 @@ describe('BufferReader', function() { should.exist(br); }); + describe('#set', function() { + + it('should set pos', function() { + should.exist(BufferReader().set({pos: 1}).pos); + }); + + }); + describe('#eof', function() { it('should return true for a blank br', function() { - var br = new BufferReader(new Buffer([])); + var br = new BufferReader({buf: new Buffer([])}); br.eof().should.equal(true); }); @@ -22,7 +30,7 @@ describe('BufferReader', function() { it('should return the same buffer', function() { var buf = new Buffer([0]); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.read().toString('hex').should.equal(buf.toString('hex')); }); @@ -33,7 +41,7 @@ describe('BufferReader', function() { it('should return 1', function() { var buf = new Buffer(1); buf.writeUInt8(1, 0); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt8().should.equal(1); }); @@ -44,7 +52,7 @@ describe('BufferReader', function() { it('should return 1', function() { var buf = new Buffer(2); buf.writeUInt16BE(1, 0); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt16BE().should.equal(1); }); @@ -55,7 +63,7 @@ describe('BufferReader', function() { it('should return 1', function() { var buf = new Buffer(2); buf.writeUInt16LE(1, 0); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt16LE().should.equal(1); }); @@ -66,7 +74,7 @@ describe('BufferReader', function() { it('should return 1', function() { var buf = new Buffer(4); buf.writeUInt32BE(1, 0); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt32BE().should.equal(1); }); @@ -77,7 +85,7 @@ describe('BufferReader', function() { it('should return 1', function() { var buf = new Buffer(4); buf.writeUInt32LE(1, 0); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt32LE().should.equal(1); }); @@ -89,14 +97,14 @@ describe('BufferReader', function() { var buf = new Buffer(8); buf.fill(0); buf.writeUInt32BE(1, 4); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt64BEBN().toNumber().should.equal(1); }); it('should return 2^64', function() { var buf = new Buffer(8); buf.fill(0xff); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt64BEBN().toNumber().should.equal(Math.pow(2, 64)); }); @@ -108,7 +116,7 @@ describe('BufferReader', function() { var buf = new Buffer(8); buf.fill(0); buf.writeUInt32LE(1, 0); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt64LEBN().toNumber().should.equal(1); }); @@ -116,21 +124,21 @@ describe('BufferReader', function() { var buf = new Buffer(8); buf.fill(0); buf.writeUInt32LE(Math.pow(2, 30), 0); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt64LEBN().toNumber().should.equal(Math.pow(2, 30)); }); it('should return 0', function() { var buf = new Buffer(8); buf.fill(0); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt64LEBN().toNumber().should.equal(0); }); it('should return 2^64', function() { var buf = new Buffer(8); buf.fill(0xff); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readUInt64LEBN().toNumber().should.equal(Math.pow(2, 64)); }); @@ -140,26 +148,26 @@ describe('BufferReader', function() { it('should read a 1 byte varint', function() { var buf = new Buffer([50]); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readVarInt().should.equal(50); }); it('should read a 3 byte varint', function() { var buf = new Buffer([253, 253, 0]); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readVarInt().should.equal(253); }); it('should read a 5 byte varint', function() { var buf = new Buffer([254, 0, 0, 0, 0]); buf.writeUInt32LE(50000, 1); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readVarInt().should.equal(50000); }); it('should throw an error on a 9 byte varint', function() { var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); (function() { br.readVarInt(); }).should.throw('number too large to retain precision - use readVarIntBN'); @@ -171,26 +179,26 @@ describe('BufferReader', function() { it('should read a 1 byte varint', function() { var buf = new Buffer([50]); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readVarIntBN().toNumber().should.equal(50); }); it('should read a 3 byte varint', function() { var buf = new Buffer([253, 253, 0]); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readVarIntBN().toNumber().should.equal(253); }); it('should read a 5 byte varint', function() { var buf = new Buffer([254, 0, 0, 0, 0]); buf.writeUInt32LE(50000, 1); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readVarIntBN().toNumber().should.equal(50000); }); it('should read a 9 byte varint', function() { var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.readVarIntBN().toNumber().should.equal(Math.pow(2, 64)); }); @@ -200,7 +208,7 @@ describe('BufferReader', function() { it('should reverse this [0, 1]', function() { var buf = new Buffer([0, 1]); - var br = new BufferReader(buf); + var br = new BufferReader({buf: buf}); br.reverse().read().toString('hex').should.equal('0100'); }); diff --git a/test/test.bufferwriter.js b/test/test.bufferwriter.js index 02a7377..7ac33e5 100644 --- a/test/test.bufferwriter.js +++ b/test/test.bufferwriter.js @@ -126,7 +126,7 @@ describe('BufferWriter', function() { var n = Math.pow(2, 53); n.should.equal(n + 1); //javascript number precision limit bw.writeVarInt(n); - var br = new BufferReader(bw.concat()); + var br = new BufferReader({buf: bw.concat()}); br.readVarIntBN().toNumber().should.equal(n); }); From 0d9b54711e76196a4413bff8cc8f27aa899a235c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 16:02:24 -0700 Subject: [PATCH 116/280] BufferWriter.prototype.set --- lib/bufferwriter.js | 14 +++++++++++--- test/test.bufferwriter.js | 14 +++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/bufferwriter.js b/lib/bufferwriter.js index 85a18fe..5bf5800 100644 --- a/lib/bufferwriter.js +++ b/lib/bufferwriter.js @@ -1,9 +1,17 @@ var BN = require('./bn'); -var BufferWriter = function BufferWriter(bufs) { +var BufferWriter = function BufferWriter(obj) { if (!(this instanceof BufferWriter)) - return new BufferReader(buf); - this.bufs = bufs || []; + return new BufferReader(obj); + if (obj) + this.set(obj); + else + this.bufs = []; +}; + +BufferWriter.prototype.set = function(obj) { + this.bufs = obj.bufs || this.bufs || []; + return this; }; BufferWriter.prototype.concat = function() { diff --git a/test/test.bufferwriter.js b/test/test.bufferwriter.js index 7ac33e5..acbc8f1 100644 --- a/test/test.bufferwriter.js +++ b/test/test.bufferwriter.js @@ -10,12 +10,24 @@ describe('BufferWriter', function() { should.exist(bw); }); + describe('#set', function() { + + it('set bufs', function() { + var buf1 = new Buffer([0]); + var buf2 = new Buffer([1]); + var bufs = [buf1, buf2]; + var bw = new BufferWriter().set({bufs: [buf1, buf2]}); + bw.concat().toString('hex').should.equal('0001'); + }); + + }); + describe('#concat', function() { it('should concat these two bufs', function() { var buf1 = new Buffer([0]); var buf2 = new Buffer([1]); - var bw = new BufferWriter([buf1, buf2]); + var bw = new BufferWriter({bufs: [buf1, buf2]}); bw.concat().toString('hex').should.equal('0001'); }); From 75c1503a9270e8bb386a8745d8952a32714c6218 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 16:07:28 -0700 Subject: [PATCH 117/280] ECDSA.prototype.set --- lib/ecdsa.js | 20 +++++++++++++------- lib/message.js | 2 +- test/test.ecdsa.js | 8 ++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 6c90e16..c5fe908 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -6,14 +6,20 @@ var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var Random = require('./random'); -var ECDSA = function ECDSA(hashbuf, key, sig, k, verified) { +var ECDSA = function ECDSA(obj) { if (!(this instanceof ECDSA)) - return new ECDSA(hashbuf, key, sig, k, verified); - this.hashbuf = hashbuf; - this.key = key; - this.sig = sig; - this.k = k; - this.verified = verified; + return new ECDSA(obj); + if (obj) + this.set(obj); +}; + +ECDSA.prototype.set = function(obj) { + this.hashbuf = obj.hashbuf || this.hashbuf || undefined; + this.key = obj.key || this.key || undefined; + this.sig = obj.sig || this.sig || undefined; + this.k = obj.k || this.k || undefined; + this.verified = obj.verified || this.verified || undefined; + return this; }; ECDSA.prototype.calci = function() { diff --git a/lib/message.js b/lib/message.js index c345dfb..6e4bcb9 100644 --- a/lib/message.js +++ b/lib/message.js @@ -54,7 +54,7 @@ Message.verify = function(messagebuf, sigstr, address) { Message.prototype.sign = function() { var hashbuf = Message.magicHash(this.messagebuf); - var ecdsa = ECDSA(hashbuf, this.key); + var ecdsa = ECDSA({hashbuf: hashbuf, key: this.key}); ecdsa.signRandomK(); ecdsa.calci(); this.sig = ecdsa.sig; diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index 76886f4..f17a8a4 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -21,6 +21,14 @@ describe("ECDSA", function() { ecdsa.key.pubkey = new Pubkey(point(BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))); + describe('#set', function() { + + it('should set hashbuf', function() { + should.exist(ECDSA().set({hashbuf: ecdsa.hashbuf}).hashbuf); + }); + + }); + describe('#calci', function() { it('should calculate i', function() { From 28d3a40704e942c7c880748a69319f2b13bc7541 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 16:18:36 -0700 Subject: [PATCH 118/280] Key.prototype.set --- lib/expmt/stealth.js | 6 +++--- lib/kdf.js | 2 +- lib/key.js | 14 ++++++++++---- test/test.key.js | 10 +++++++++- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/lib/expmt/stealth.js b/lib/expmt/stealth.js index 20fe407..c5fdaea 100644 --- a/lib/expmt/stealth.js +++ b/lib/expmt/stealth.js @@ -22,9 +22,9 @@ Stealth.prototype.fromAddressBuffer = function(buf) { var sPubBuf = buf.slice(33, 66); var payloadPubkey = Pubkey().fromDER(pPubBuf); - this.payloadKey = Key(undefined, payloadPubkey); + this.payloadKey = Key({pubkey: payloadPubkey}); var scanPubkey = Pubkey().fromDER(sPubBuf); - this.scanKey = Key(undefined, scanPubkey); + this.scanKey = Key({pubkey: scanPubkey}); return this; }; @@ -78,7 +78,7 @@ Stealth.prototype.getReceivePubkeyAsSender = function(senderKey) { Stealth.prototype.getReceiveKey = function(senderPubkey) { var sharedKey = this.getSharedKeyAsReceiver(senderPubkey); var privkey = Privkey(this.payloadKey.privkey.bn.add(sharedKey.privkey.bn).mod(Point.getN())); - var key = Key(privkey); + var key = Key({privkey: privkey}); key.privkey2pubkey(); return key; diff --git a/lib/kdf.js b/lib/kdf.js index 91d0752..4cba312 100644 --- a/lib/kdf.js +++ b/lib/kdf.js @@ -14,7 +14,7 @@ KDF.buf2key = function(buf) { KDF.sha256hmac2key = function(buf) { var privkey = KDF.sha256hmac2privkey(buf); - var key = new Key(privkey); + var key = new Key({privkey: privkey}); key.privkey2pubkey(); return key; }; diff --git a/lib/key.js b/lib/key.js index a7a3aa5..0515072 100644 --- a/lib/key.js +++ b/lib/key.js @@ -5,11 +5,17 @@ var Random = require('./random'); var Bn = require('./bn'); var point = require('./point'); -var Key = function Key(privkey, pubkey) { +var Key = function Key(obj) { if (!(this instanceof Key)) - return new Key(privkey, pubkey); - this.privkey = privkey; - this.pubkey = pubkey; + return new Key(obj); + if (obj) + this.set(obj); +}; + +Key.prototype.set = function(obj) { + this.privkey = obj.privkey || this.privkey || undefined; + this.pubkey = obj.pubkey || this.pubkey || undefined; + return this; }; Key.prototype.fromRandom = function() { diff --git a/test/test.key.js b/test/test.key.js index b7e76c3..05ae214 100644 --- a/test/test.key.js +++ b/test/test.key.js @@ -16,12 +16,20 @@ describe('Key', function() { it('should make a key with a priv and pub', function() { var priv = new Privkey(); var pub = new Pubkey(); - var key = new Key(priv, pub); + var key = new Key({privkey: priv, pubkey: pub}); should.exist(key); should.exist(key.privkey); should.exist(key.pubkey); }); + describe("#set", function() { + + it('should make a new priv and pub', function() { + should.exist(Key().set({privkey: Privkey()}).privkey); + }); + + }); + describe("#fromRandom", function() { it('should make a new priv and pub', function() { From a5f79c7651fcb7511c2034d51fecd45d0d6b52c6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 16:38:21 -0700 Subject: [PATCH 119/280] Signature.prototype.set --- lib/ecdsa.js | 2 +- lib/signature.js | 17 +++++++++++------ test/test.ecdsa.js | 2 +- test/test.signature.js | 12 ++++++++++-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index c5fe908..ec57b23 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -166,7 +166,7 @@ ECDSA.prototype.sign = function() { var s = k.invm(N).mul(e.add(d.mul(r))).mod(N); } while (r.cmp(0) <= 0 || s.cmp(0) <= 0); - this.sig = new Signature(r, s, undefined, this.key.pubkey.compressed); + this.sig = new Signature({r: r, s: s, compressed: this.key.pubkey.compressed}); return this.sig; }; diff --git a/lib/signature.js b/lib/signature.js index 775d9b4..d6d3739 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -2,14 +2,19 @@ var BN = require('./bn'); var Point = require('./point'); var Pubkey = require('./pubkey'); -var Signature = function Signature(r, s, i, compressed) { +var Signature = function Signature(obj) { if (!(this instanceof Signature)) - return new Signature(r, s, i, compressed); + return new Signature(obj); + if (obj) + this.set(obj); +}; - this.r = r; - this.s = s; - this.i = i; //public key recovery parameter in range [0, 3] - this.compressed = compressed; +Signature.prototype.set = function(obj) { + this.r = obj.r || this.r || undefined; + this.s = obj.s || this.s || undefined; + this.i = typeof obj.i !== 'undefined' ? obj.i : this.i; //public key recovery parameter in range [0, 3] + this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; //whether the recovered pubkey is compressed + return this; }; Signature.prototype.fromCompact = function(buf) { diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index f17a8a4..b8ff3bc 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -48,7 +48,7 @@ describe("ECDSA", function() { ecdsa.key.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test'))); ecdsa.key.privkey2pubkey(); ecdsa.hashbuf = hashbuf; - ecdsa.sig = new Signature(r, s); + ecdsa.sig = new Signature({r: r, s: s}); ecdsa.calci(); ecdsa.sig.i.should.equal(1); diff --git a/test/test.signature.js b/test/test.signature.js index d52bcd5..1d6972b 100644 --- a/test/test.signature.js +++ b/test/test.signature.js @@ -9,6 +9,14 @@ describe('Signature', function() { should.exist(sig); }); + describe('#set', function() { + + it('should set compressed', function() { + should.exist(Signature().set({compressed: true})); + }); + + }); + describe('#fromCompact', function() { it('should create a signature from a compressed signature', function() { @@ -110,7 +118,7 @@ describe('Signature', function() { it('should convert these known r and s values into a known signature', function() { var r = bn('63173831029936981022572627018246571655303050627048489594159321588908385378810'); var s = bn('4331694221846364448463828256391194279133231453999942381442030409253074198130'); - var sig = new Signature(r, s); + var sig = new Signature({r: r, s: s}); var der = sig.toDER(r, s); der.toString('hex').should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); }); @@ -122,7 +130,7 @@ describe('Signature', function() { it('should convert this signature in to hex DER', function() { var r = bn('63173831029936981022572627018246571655303050627048489594159321588908385378810'); var s = bn('4331694221846364448463828256391194279133231453999942381442030409253074198130'); - var sig = new Signature(r, s); + var sig = new Signature({r: r, s: s}); var hex = sig.toString(); hex.should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); }); From 0ca390d45ba59565d0b14aaff442be38b7f1572c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 16:42:47 -0700 Subject: [PATCH 120/280] Message.prototype.set --- lib/message.js | 22 ++++++++++++++-------- test/test.message.js | 9 +++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/message.js b/lib/message.js index 6e4bcb9..64bb1f4 100644 --- a/lib/message.js +++ b/lib/message.js @@ -7,14 +7,20 @@ var Hash = require('./hash'); var Address = require('./address'); var Signature = require('./signature'); -var Message = function Message(messagebuf, key, sig, address, verified) { +var Message = function Message(obj) { if (!(this instanceof Message)) - return new Message(messagebuf, key, sig); - this.messagebuf = messagebuf; - this.key = key; - this.sig = sig; - this.address = address; - this.verified = verified; + return new Message(obj); + if (obj) + this.set(obj); +}; + +Message.prototype.set = function(obj) { + this.messagebuf = obj.messagebuf || this.messagebuf; + this.key = obj.key || this.key; + this.sig = obj.sig || this.sig; + this.address = obj.address || this.address; + this.verified = typeof obj.verified !== 'undefined' ? obj.verified : this.verified; + return this; }; Message.magicBytes = new Buffer('Bitcoin Signed Message:\n'); @@ -35,7 +41,7 @@ Message.magicHash = function(messagebuf) { }; Message.sign = function(messagebuf, key) { - var m = Message(messagebuf, key); + var m = Message({messagebuf: messagebuf, key: key}); m.sign(); var sigbuf = m.sig.toCompact(); var sigstr = sigbuf.toString('base64'); diff --git a/test/test.message.js b/test/test.message.js index 311dbb3..84e65e2 100644 --- a/test/test.message.js +++ b/test/test.message.js @@ -14,6 +14,15 @@ describe('Message', function() { var message = Message(); should.exist(message); }); + + describe('#set', function() { + + it('should set the messagebuf', function() { + var messagebuf = new Buffer('message'); + should.exist(Message().set({messagebuf: messagebuf}).messagebuf); + }); + + }); describe('@sign', function() { var messagebuf = new Buffer('this is my message'); From 6b4bc4c49c243a32bd6b360b41d025e94122b5bc Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 17:00:34 -0700 Subject: [PATCH 121/280] Stealth.prototype.set --- lib/expmt/stealth.js | 13 +++++++++---- test/test.stealth.js | 8 ++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/expmt/stealth.js b/lib/expmt/stealth.js index c5fdaea..9fb48e1 100644 --- a/lib/expmt/stealth.js +++ b/lib/expmt/stealth.js @@ -6,12 +6,17 @@ var Hash = require('../hash'); var KDF = require('../kdf'); var base58check = require('../base58check'); -var Stealth = function Stealth(payloadKey, scanKey) { +var Stealth = function Stealth(obj) { if (!(this instanceof Stealth)) - return new Stealth(payloadKey, scanKey); + return new Stealth(obj); + if (obj) + this.set(obj); +}; - this.payloadKey = payloadKey; - this.scanKey = scanKey; +Stealth.prototype.set = function(obj) { + this.payloadKey = obj.payloadKey || this.payloadKey; + this.scanKey = obj.scanKey || this.scanKey; + return this; }; Stealth.prototype.fromAddressBuffer = function(buf) { diff --git a/test/test.stealth.js b/test/test.stealth.js index 6bacacd..138b16b 100644 --- a/test/test.stealth.js +++ b/test/test.stealth.js @@ -36,6 +36,14 @@ describe('Stealth', function() { should.exist(stealth); }); + describe('#set', function() { + + it('should set payload key', function() { + should.exist(Stealth().set({payloadKey: stealth.payloadKey}).payloadKey); + }); + + }); + describe('#fromAddressBuffer', function() { it('should give a stealth address with the right pubkeys', function() { From 109f31cfdb32a662fcb6c298405bc9452afc2271 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 17:26:56 -0700 Subject: [PATCH 122/280] Privkey.prototype.set --- lib/bip32.js | 6 +++--- lib/ecdsa.js | 1 - lib/expmt/stealth.js | 2 +- lib/kdf.js | 2 +- lib/key.js | 2 +- lib/privkey.js | 16 +++++++++++----- test/test.ecdsa.js | 2 +- test/test.key.js | 8 ++++---- test/test.privkey.js | 14 +++++++++++--- 9 files changed, 33 insertions(+), 20 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index ffac16b..4025c93 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -64,7 +64,7 @@ BIP32.prototype.fromSeed = function(bytes, network) { this.chainCode = hash.slice(32, 64); this.version = constants[network].bip32privkey; this.key = new Key(); - this.key.privkey = new Privkey(bn.fromBuffer(hash.slice(0, 32))); + this.key.privkey = new Privkey({bn: bn.fromBuffer(hash.slice(0, 32))}); this.key.privkey2pubkey(); this.hasPrivateKey = true; this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); @@ -98,7 +98,7 @@ BIP32.prototype.initFromBytes = function(bytes) { if (isPrivate && keyBytes[0] == 0) { this.key = new Key(); - this.key.privkey = new Privkey(bn.fromBuffer(keyBytes.slice(1, 33))); + this.key.privkey = new Privkey({bn: bn.fromBuffer(keyBytes.slice(1, 33))}); this.key.privkey2pubkey(); this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); this.hasPrivateKey = true; @@ -273,7 +273,7 @@ BIP32.prototype.deriveChild = function(i) { ret.chainCode = ir; ret.key = new Key(); - ret.key.privkey = new Privkey(k); + ret.key.privkey = new Privkey({bn: k}); ret.key.privkey2pubkey(); ret.hasPrivateKey = true; diff --git a/lib/ecdsa.js b/lib/ecdsa.js index ec57b23..2fd8734 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -2,7 +2,6 @@ var BN = require('./bn'); var Point = require('./point'); var Signature = require('./signature'); var Key = require('./key'); -var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var Random = require('./random'); diff --git a/lib/expmt/stealth.js b/lib/expmt/stealth.js index 9fb48e1..66aebb6 100644 --- a/lib/expmt/stealth.js +++ b/lib/expmt/stealth.js @@ -82,7 +82,7 @@ Stealth.prototype.getReceivePubkeyAsSender = function(senderKey) { Stealth.prototype.getReceiveKey = function(senderPubkey) { var sharedKey = this.getSharedKeyAsReceiver(senderPubkey); - var privkey = Privkey(this.payloadKey.privkey.bn.add(sharedKey.privkey.bn).mod(Point.getN())); + var privkey = Privkey({bn: this.payloadKey.privkey.bn.add(sharedKey.privkey.bn).mod(Point.getN())}); var key = Key({privkey: privkey}); key.privkey2pubkey(); diff --git a/lib/kdf.js b/lib/kdf.js index 4cba312..f286671 100644 --- a/lib/kdf.js +++ b/lib/kdf.js @@ -27,7 +27,7 @@ KDF.sha256hmac2privkey = function(buf) { var bn = Bn.fromBuffer(hash); concat = Buffer.concat([concat, new Buffer(0)]); } while(!bn.lt(Point.getN())); - return new Privkey(bn); + return new Privkey({bn: bn}); }; module.exports = KDF; diff --git a/lib/key.js b/lib/key.js index 0515072..3ad9bed 100644 --- a/lib/key.js +++ b/lib/key.js @@ -21,7 +21,7 @@ Key.prototype.set = function(obj) { Key.prototype.fromRandom = function() { do { var privbuf = Random.getRandomBuffer(32); - this.privkey = new Privkey(Bn(privbuf)); + this.privkey = new Privkey({bn: Bn(privbuf)}); var condition = this.privkey.bn.lt(point.getN()); } while (!condition); this.privkey2pubkey(); diff --git a/lib/privkey.js b/lib/privkey.js index 4edaede..9fde9d8 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -3,12 +3,18 @@ var point = require('./point'); var constants = require('./constants'); var base58check = require('./base58check'); -var Privkey = function Privkey(bn, network, compressed) { +var Privkey = function Privkey(obj) { if (!(this instanceof Privkey)) - return new Privkey(bn, network, compressed); - this.bn = bn; - this.network = network; - this.compressed = compressed; + return new Privkey(obj); + if (obj) + this.set(obj); +}; + +Privkey.prototype.set = function(obj) { + this.bn = obj.bn || this.bn; + this.network = obj.network || this.network; + this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; + return this; }; Privkey.prototype.validate = function() { diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index b8ff3bc..219001d 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -17,7 +17,7 @@ describe("ECDSA", function() { var ecdsa = new ECDSA(); ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); ecdsa.key = new Key(); - ecdsa.key.privkey = new Privkey(BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))); + ecdsa.key.privkey = new Privkey({bn: BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))}); ecdsa.key.pubkey = new Pubkey(point(BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))); diff --git a/test/test.key.js b/test/test.key.js index 05ae214..e7495dd 100644 --- a/test/test.key.js +++ b/test/test.key.js @@ -89,7 +89,7 @@ describe('Key', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; var key = new Key(); - key.privkey = new Privkey(bn(new Buffer(privhex, 'hex'))); + key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); key.privkey2pubkey(); key.getAddress().toString().should.equal((new Address()).fromPubkey(key.pubkey).toString()); }); @@ -102,7 +102,7 @@ describe('Key', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; var key = new Key(); - key.privkey = new Privkey(bn(new Buffer(privhex, 'hex'))); + key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); key.privkey2pubkey(); key.pubkey.toString().should.equal(pubhex); }); @@ -110,7 +110,7 @@ describe('Key', function() { it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var key = new Key(); - key.privkey = new Privkey(bn(new Buffer(privhex, 'hex'))); + key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); key.privkey.compressed = true; key.privkey2pubkey(); key.pubkey.compressed.should.equal(true); @@ -119,7 +119,7 @@ describe('Key', function() { it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var key = new Key(); - key.privkey = new Privkey(bn(new Buffer(privhex, 'hex'))); + key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); key.privkey.compressed = false; key.privkey2pubkey(); key.pubkey.compressed.should.equal(false); diff --git a/test/test.privkey.js b/test/test.privkey.js index 8bb0a85..26872a3 100644 --- a/test/test.privkey.js +++ b/test/test.privkey.js @@ -17,20 +17,28 @@ describe('Privkey', function() { }); it('should create a mainnet private key', function() { - var privkey = new Privkey(Bn.fromBuffer(buf), 'mainnet', true); + var privkey = new Privkey({bn: Bn.fromBuffer(buf), network: 'mainnet', compressed: true}); privkey.toString().should.equal(encmainnet); }); it('should create an uncompressed testnet private key', function() { - var privkey = new Privkey(Bn.fromBuffer(buf), 'testnet', false); + var privkey = new Privkey({bn: Bn.fromBuffer(buf), network: 'testnet', compressed: false}); privkey.toString().should.equal(enctu); }); it('should create an uncompressed mainnet private key', function() { - var privkey = new Privkey(Bn.fromBuffer(buf), 'mainnet', false); + var privkey = new Privkey({bn: Bn.fromBuffer(buf), network: 'mainnet', compressed: false}); privkey.toString().should.equal(encmu); }); + describe('#set', function() { + + it('should set bn', function() { + should.exist(Privkey().set({bn: Bn.fromBuffer(buf)}).bn); + }); + + }); + describe('#fromWIF', function() { it('should parse this compressed testnet address correctly', function() { From fa85fa471531b37cac7e0b34ed70fda2989fd37d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 17:28:47 -0700 Subject: [PATCH 123/280] fix ecies exposure --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 18db7a0..7439860 100644 --- a/index.js +++ b/index.js @@ -24,7 +24,7 @@ bitcore.Signature = require('./lib/signature'); bitcore.expmt = {}; bitcore.expmt.AES = require('./lib/expmt/aes'); bitcore.expmt.CBC = require('./lib/expmt/cbc'); -bitcore.expmt.ECIES = require('./lib/ecies'); +bitcore.expmt.ECIES = require('./lib/expmt/ecies'); bitcore.expmt.SymEnc = require('./lib/expmt/symenc'); bitcore.expmt.Stealth = require('./lib/expmt/stealth'); From ac47796acd722703e8ca335da1ea8295d5d26c29 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 17:30:32 -0700 Subject: [PATCH 124/280] .isValid() is more convenient than .validate() --- lib/address.js | 5 ++--- test/test.address.js | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/address.js b/lib/address.js index fb50e6f..b248aed 100644 --- a/lib/address.js +++ b/lib/address.js @@ -10,10 +10,9 @@ function Address(obj) { this.set(obj); }; -Address.validate = function(addrstr) { +Address.isValid = function(addrstr) { var address = new Address().fromString(addrstr); - address.validate(); - return this; + return address.isValid(); }; Address.prototype.set = function(obj) { diff --git a/test/test.address.js b/test/test.address.js index 9b9ff50..0700e30 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -12,10 +12,10 @@ describe('Address', function() { should.exist(address); }); - describe('@validate', function() { + describe('@isValid', function() { it('should validate this valid address string', function() { - should.exist(Address.validate(str)) + Address.isValid(str).should.equal(true); }); }); From 356ddcfa4eb4e464390ac3ad3c6d0b9929969ef0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 17:32:08 -0700 Subject: [PATCH 125/280] test invalid address --- lib/address.js | 6 +++++- test/test.address.js | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/address.js b/lib/address.js index b248aed..b3b36f3 100644 --- a/lib/address.js +++ b/lib/address.js @@ -11,7 +11,11 @@ function Address(obj) { }; Address.isValid = function(addrstr) { - var address = new Address().fromString(addrstr); + try { + var address = new Address().fromString(addrstr); + } catch (e) { + return false; + } return address.isValid(); }; diff --git a/test/test.address.js b/test/test.address.js index 0700e30..e18cf49 100644 --- a/test/test.address.js +++ b/test/test.address.js @@ -18,6 +18,10 @@ describe('Address', function() { Address.isValid(str).should.equal(true); }); + it('should invalidate this valid address string', function() { + Address.isValid(str.substr(1)).should.equal(false); + }); + }); describe('#fromPubkey', function() { From e2824035bb86f2fe960000f3b4daa347676e0043 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 17:41:38 -0700 Subject: [PATCH 126/280] Pubkey.prototype.set --- lib/ecdsa.js | 2 +- lib/expmt/stealth.js | 8 ++++---- lib/key.js | 2 +- lib/pubkey.js | 16 +++++++++++----- test/test.ecdsa.js | 6 ++++-- test/test.pubkey.js | 10 +++++++++- 6 files changed, 30 insertions(+), 14 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 2fd8734..e2648aa 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -104,7 +104,7 @@ ECDSA.prototype.sig2pubkey = function() { //var Q = R.multiplyTwo(s, G, eNeg).mul(rInv); var Q = R.mul(s).add(G.mul(eNeg)).mul(rInv); - var pubkey = new Pubkey(Q); + var pubkey = new Pubkey({point: Q}); pubkey.compressed = this.sig.compressed; pubkey.validate(); diff --git a/lib/expmt/stealth.js b/lib/expmt/stealth.js index 66aebb6..877ff13 100644 --- a/lib/expmt/stealth.js +++ b/lib/expmt/stealth.js @@ -50,7 +50,7 @@ Stealth.prototype.fromRandom = function() { Stealth.prototype.getSharedKeyAsReceiver = function(senderPubkey) { var sharedSecretPoint = senderPubkey.point.mul(this.scanKey.privkey.bn); - var sharedSecretPubkey = Pubkey(sharedSecretPoint); + var sharedSecretPubkey = Pubkey({point: sharedSecretPoint}); var buf = sharedSecretPubkey.toDER(true); var sharedKey = KDF.sha256hmac2key(buf); @@ -59,7 +59,7 @@ Stealth.prototype.getSharedKeyAsReceiver = function(senderPubkey) { Stealth.prototype.getSharedKeyAsSender = function(senderKey) { var sharedSecretPoint = this.scanKey.pubkey.point.mul(senderKey.privkey.bn); - var sharedSecretPubkey = Pubkey(sharedSecretPoint); + var sharedSecretPubkey = Pubkey({point: sharedSecretPoint}); var buf = sharedSecretPubkey.toDER(true); var sharedKey = KDF.sha256hmac2key(buf); @@ -68,14 +68,14 @@ Stealth.prototype.getSharedKeyAsSender = function(senderKey) { Stealth.prototype.getReceivePubkeyAsReceiver = function(senderPubkey) { var sharedKey = this.getSharedKeyAsReceiver(senderPubkey); - var pubkey = Pubkey(this.payloadKey.pubkey.point.add(sharedKey.pubkey.point)); + var pubkey = Pubkey({point: this.payloadKey.pubkey.point.add(sharedKey.pubkey.point)}); return pubkey; }; Stealth.prototype.getReceivePubkeyAsSender = function(senderKey) { var sharedKey = this.getSharedKeyAsSender(senderKey); - var pubkey = Pubkey(this.payloadKey.pubkey.point.add(sharedKey.pubkey.point)); + var pubkey = Pubkey({point: this.payloadKey.pubkey.point.add(sharedKey.pubkey.point)}); return pubkey; }; diff --git a/lib/key.js b/lib/key.js index 3ad9bed..2616ba3 100644 --- a/lib/key.js +++ b/lib/key.js @@ -45,7 +45,7 @@ Key.prototype.getAddress = function(network) { }; Key.prototype.privkey2pubkey = function() { - this.pubkey = new Pubkey(point.getG().mul(this.privkey.bn), this.privkey.compressed); + this.pubkey = new Pubkey({point: point.getG().mul(this.privkey.bn), compressed: this.privkey.compressed}); }; Key.prototype.toString = function() { diff --git a/lib/pubkey.js b/lib/pubkey.js index 8a659a2..b4ffa5f 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -1,13 +1,19 @@ var Point = require('./point'); var bn = require('./bn'); -var Pubkey = function Pubkey(point, compressed) { +var Pubkey = function Pubkey(obj) { if (!(this instanceof Pubkey)) - return new Pubkey(point); - if (point && !point.getX() && !point.getY()) + return new Pubkey(obj); + if (obj) + this.set(obj); +}; + +Pubkey.prototype.set = function(obj) { + if (obj.point && !obj.point.getX() && !obj.point.getY()) throw new Error('Invalid point'); - this.point = point; - this.compressed = compressed; + this.point = obj.point || this.point; + this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; + return this; }; Pubkey.prototype.fromDER = function(buf) { diff --git a/test/test.ecdsa.js b/test/test.ecdsa.js index 219001d..3a8238a 100644 --- a/test/test.ecdsa.js +++ b/test/test.ecdsa.js @@ -18,8 +18,10 @@ describe("ECDSA", function() { ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); ecdsa.key = new Key(); ecdsa.key.privkey = new Privkey({bn: BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))}); - ecdsa.key.pubkey = new Pubkey(point(BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), - BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex')))); + ecdsa.key.pubkey = new Pubkey({ + point: point(BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), + BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex'))) + }); describe('#set', function() { diff --git a/test/test.pubkey.js b/test/test.pubkey.js index 06e9b7a..3510b6d 100644 --- a/test/test.pubkey.js +++ b/test/test.pubkey.js @@ -12,10 +12,18 @@ describe('Pubkey', function() { it('should create a public key with a point', function() { var p = Point(); - var pk = new Pubkey(p); + var pk = new Pubkey({point: p}); should.exist(pk.point); }); + describe('#set', function() { + + it('should make a public key from a point', function() { + should.exist(Pubkey().set({point: Point.getG()}).point); + }); + + }); + describe('#fromDER', function() { it('should parse this uncompressed public key', function() { From 280578d64120238b6fd3111e20fab6c7b747881f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 17:53:11 -0700 Subject: [PATCH 127/280] network -> networkstr ...for compatibility with address, and to make the types obvious --- lib/privkey.js | 22 +++++++++++----------- test/test.privkey.js | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/privkey.js b/lib/privkey.js index 9fde9d8..d012fa8 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -12,7 +12,7 @@ var Privkey = function Privkey(obj) { Privkey.prototype.set = function(obj) { this.bn = obj.bn || this.bn; - this.network = obj.network || this.network; + this.networkstr = obj.networkstr || this.networkstr; this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; return this; }; @@ -20,27 +20,27 @@ Privkey.prototype.set = function(obj) { Privkey.prototype.validate = function() { if (!this.bn.lt(point.getN())) throw new Error('Number must be less than N'); - if (typeof constants[this.network] === undefined) - throw new Error('Must specify the network ("mainnet" or "testnet")'); + if (typeof constants[this.networkstr] === undefined) + throw new Error('Must specify the networkstr ("mainnet" or "testnet")'); if (typeof this.compressed !== 'boolean') throw new Error('Must specify whether the corresponding public key is compressed or not (true or false)'); }; Privkey.prototype.toWIF = function() { - var network = this.network; + var networkstr = this.networkstr; var compressed = this.compressed; - if (typeof this.network === 'undefined') - network = 'mainnet'; + if (typeof this.networkstr === 'undefined') + networkstr = 'mainnet'; if (typeof this.compressed === 'undefined') compressed = true; var privbuf = this.bn.toBuffer({size: 32}); var buf; if (compressed) - buf = Buffer.concat([new Buffer([constants[network].privkey]), this.bn.toBuffer({size: 32}), new Buffer([0x01])]); + buf = Buffer.concat([new Buffer([constants[networkstr].privkey]), this.bn.toBuffer({size: 32}), new Buffer([0x01])]); else - buf = Buffer.concat([new Buffer([constants[network].privkey]), this.bn.toBuffer({size: 32})]); + buf = Buffer.concat([new Buffer([constants[networkstr].privkey]), this.bn.toBuffer({size: 32})]); return base58check.encode(buf); }; @@ -56,11 +56,11 @@ Privkey.prototype.fromWIF = function(str) { throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)'); if (buf[0] === constants.mainnet.privkey) - this.network = 'mainnet'; + this.networkstr = 'mainnet'; else if (buf[0] === constants.testnet.privkey) - this.network = 'testnet'; + this.networkstr = 'testnet'; else - throw new Error('Invalid network'); + throw new Error('Invalid networkstr'); this.bn = Bn.fromBuffer(buf.slice(1, 32 + 1)); }; diff --git a/test/test.privkey.js b/test/test.privkey.js index 26872a3..ed0a4ce 100644 --- a/test/test.privkey.js +++ b/test/test.privkey.js @@ -17,17 +17,17 @@ describe('Privkey', function() { }); it('should create a mainnet private key', function() { - var privkey = new Privkey({bn: Bn.fromBuffer(buf), network: 'mainnet', compressed: true}); + var privkey = new Privkey({bn: Bn.fromBuffer(buf), networkstr: 'mainnet', compressed: true}); privkey.toString().should.equal(encmainnet); }); it('should create an uncompressed testnet private key', function() { - var privkey = new Privkey({bn: Bn.fromBuffer(buf), network: 'testnet', compressed: false}); + var privkey = new Privkey({bn: Bn.fromBuffer(buf), networkstr: 'testnet', compressed: false}); privkey.toString().should.equal(enctu); }); it('should create an uncompressed mainnet private key', function() { - var privkey = new Privkey({bn: Bn.fromBuffer(buf), network: 'mainnet', compressed: false}); + var privkey = new Privkey({bn: Bn.fromBuffer(buf), networkstr: 'mainnet', compressed: false}); privkey.toString().should.equal(encmu); }); From 2ef5e2f003f85cf8aa17129964d635f271ca2aac Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 17:55:33 -0700 Subject: [PATCH 128/280] network -> networkstr --- lib/bip32.js | 16 ++++++++-------- lib/key.js | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 4025c93..a88b654 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -13,10 +13,10 @@ var BIP32 = function BIP32() { return new BIP32(); } -BIP32.prototype.fromRandom = function(network) { - if (!network) - network = 'mainnet'; - this.version = constants[network].bip32privkey; +BIP32.prototype.fromRandom = function(networkstr) { + if (!networkstr) + networkstr = 'mainnet'; + this.version = constants[networkstr].bip32privkey; this.depth = 0x00; this.parentFingerprint = new Buffer([0, 0, 0, 0]); this.childIndex = new Buffer([0, 0, 0, 0]); @@ -46,9 +46,9 @@ BIP32.prototype.fromString = function(str) { return this; }; -BIP32.prototype.fromSeed = function(bytes, network) { - if (!network) - network = 'mainnet'; +BIP32.prototype.fromSeed = function(bytes, networkstr) { + if (!networkstr) + networkstr = 'mainnet'; if (!Buffer.isBuffer(bytes)) throw new Error('gcbytes must be a buffer'); @@ -62,7 +62,7 @@ BIP32.prototype.fromSeed = function(bytes, network) { this.parentFingerprint = new Buffer([0, 0, 0, 0]); this.childIndex = new Buffer([0, 0, 0, 0]); this.chainCode = hash.slice(32, 64); - this.version = constants[network].bip32privkey; + this.version = constants[networkstr].bip32privkey; this.key = new Key(); this.key.privkey = new Privkey({bn: bn.fromBuffer(hash.slice(0, 32))}); this.key.privkey2pubkey(); diff --git a/lib/key.js b/lib/key.js index 2616ba3..bec7295 100644 --- a/lib/key.js +++ b/lib/key.js @@ -40,8 +40,8 @@ Key.prototype.fromString = function(str) { } }; -Key.prototype.getAddress = function(network) { - return Address().fromPubkey(this.pubkey, network); +Key.prototype.getAddress = function(networkstr) { + return Address().fromPubkey(this.pubkey, networkstr); }; Key.prototype.privkey2pubkey = function() { From c39acbcca307f40b6f0cc08ca031991ef4997739 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 19:27:22 -0700 Subject: [PATCH 129/280] Privkey().fromRandom() --- lib/key.js | 9 ++------- lib/privkey.js | 15 +++++++++++++-- test/test.privkey.js | 21 ++++++++++++++++----- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/lib/key.js b/lib/key.js index bec7295..a73bae2 100644 --- a/lib/key.js +++ b/lib/key.js @@ -1,8 +1,7 @@ var Address = require('../lib/address'); var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); -var Random = require('./random'); -var Bn = require('./bn'); +var BN = require('./bn'); var point = require('./point'); var Key = function Key(obj) { @@ -19,11 +18,7 @@ Key.prototype.set = function(obj) { }; Key.prototype.fromRandom = function() { - do { - var privbuf = Random.getRandomBuffer(32); - this.privkey = new Privkey({bn: Bn(privbuf)}); - var condition = this.privkey.bn.lt(point.getN()); - } while (!condition); + this.privkey = Privkey().fromRandom(); this.privkey2pubkey(); return this; }; diff --git a/lib/privkey.js b/lib/privkey.js index d012fa8..d04972a 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -1,7 +1,8 @@ -var Bn = require('./bn'); +var BN = require('./bn'); var point = require('./point'); var constants = require('./constants'); var base58check = require('./base58check'); +var Random = require('./random'); var Privkey = function Privkey(obj) { if (!(this instanceof Privkey)) @@ -17,6 +18,16 @@ Privkey.prototype.set = function(obj) { return this; }; +Privkey.prototype.fromRandom = function() { + do { + var privbuf = Random.getRandomBuffer(32); + var bn = BN().fromBuffer(privbuf); + var condition = bn.lt(point.getN()); + } while (!condition); + this.bn = bn; + return this; +}; + Privkey.prototype.validate = function() { if (!this.bn.lt(point.getN())) throw new Error('Number must be less than N'); @@ -62,7 +73,7 @@ Privkey.prototype.fromWIF = function(str) { else throw new Error('Invalid networkstr'); - this.bn = Bn.fromBuffer(buf.slice(1, 32 + 1)); + this.bn = BN.fromBuffer(buf.slice(1, 32 + 1)); }; Privkey.prototype.toString = function() { diff --git a/test/test.privkey.js b/test/test.privkey.js index ed0a4ce..6cc0afa 100644 --- a/test/test.privkey.js +++ b/test/test.privkey.js @@ -1,6 +1,7 @@ var Privkey = require('../lib/privkey'); var base58check = require('../lib/base58check'); -var Bn = require('../lib/bn'); +var BN = require('../lib/bn'); +var Point = require('../lib/point'); var should = require('chai').should(); describe('Privkey', function() { @@ -17,24 +18,34 @@ describe('Privkey', function() { }); it('should create a mainnet private key', function() { - var privkey = new Privkey({bn: Bn.fromBuffer(buf), networkstr: 'mainnet', compressed: true}); + var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'mainnet', compressed: true}); privkey.toString().should.equal(encmainnet); }); it('should create an uncompressed testnet private key', function() { - var privkey = new Privkey({bn: Bn.fromBuffer(buf), networkstr: 'testnet', compressed: false}); + var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'testnet', compressed: false}); privkey.toString().should.equal(enctu); }); it('should create an uncompressed mainnet private key', function() { - var privkey = new Privkey({bn: Bn.fromBuffer(buf), networkstr: 'mainnet', compressed: false}); + var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'mainnet', compressed: false}); privkey.toString().should.equal(encmu); }); describe('#set', function() { it('should set bn', function() { - should.exist(Privkey().set({bn: Bn.fromBuffer(buf)}).bn); + should.exist(Privkey().set({bn: BN.fromBuffer(buf)}).bn); + }); + + }); + + describe('#fromRandom', function() { + + it('should set bn gt 0 and lt n', function() { + var privkey = Privkey().fromRandom(); + privkey.bn.gt(BN(0)).should.equal(true); + privkey.bn.lt(Point.getN()).should.equal(true); }); }); From 531308577328f6ab7a659a7b5089336adff083df Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 20:19:30 -0700 Subject: [PATCH 130/280] Pubkey().fromPrivkey() --- lib/key.js | 2 +- lib/privkey.js | 6 +++--- lib/pubkey.js | 9 +++++++++ test/test.pubkey.js | 9 +++++++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/key.js b/lib/key.js index a73bae2..d7baf4a 100644 --- a/lib/key.js +++ b/lib/key.js @@ -40,7 +40,7 @@ Key.prototype.getAddress = function(networkstr) { }; Key.prototype.privkey2pubkey = function() { - this.pubkey = new Pubkey({point: point.getG().mul(this.privkey.bn), compressed: this.privkey.compressed}); + this.pubkey = Pubkey().fromPrivkey(this.privkey); }; Key.prototype.toString = function() { diff --git a/lib/privkey.js b/lib/privkey.js index d04972a..b1dd12c 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -1,5 +1,5 @@ var BN = require('./bn'); -var point = require('./point'); +var Point = require('./point'); var constants = require('./constants'); var base58check = require('./base58check'); var Random = require('./random'); @@ -22,14 +22,14 @@ Privkey.prototype.fromRandom = function() { do { var privbuf = Random.getRandomBuffer(32); var bn = BN().fromBuffer(privbuf); - var condition = bn.lt(point.getN()); + var condition = bn.lt(Point.getN()); } while (!condition); this.bn = bn; return this; }; Privkey.prototype.validate = function() { - if (!this.bn.lt(point.getN())) + if (!this.bn.lt(Point.getN())) throw new Error('Number must be less than N'); if (typeof constants[this.networkstr] === undefined) throw new Error('Must specify the networkstr ("mainnet" or "testnet")'); diff --git a/lib/pubkey.js b/lib/pubkey.js index b4ffa5f..4642aa8 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -1,5 +1,6 @@ var Point = require('./point'); var bn = require('./bn'); +var privkey = require('./privkey'); var Pubkey = function Pubkey(obj) { if (!(this instanceof Pubkey)) @@ -16,6 +17,14 @@ Pubkey.prototype.set = function(obj) { return this; }; +Pubkey.prototype.fromPrivkey = function(privkey) { + this.set({ + point: Point.getG().mul(privkey.bn), + compressed: privkey.compressed} + ); + return this; +}; + Pubkey.prototype.fromDER = function(buf) { if (buf[0] == 0x04) { var xbuf = buf.slice(1, 33); diff --git a/test/test.pubkey.js b/test/test.pubkey.js index 3510b6d..efee3b6 100644 --- a/test/test.pubkey.js +++ b/test/test.pubkey.js @@ -2,6 +2,7 @@ var should = require('chai').should(); var Pubkey = require('../lib/pubkey'); var Point = require('../lib/point'); var Bn = require('../lib/bn'); +var Privkey = require('../lib/privkey'); describe('Pubkey', function() { @@ -24,6 +25,14 @@ describe('Pubkey', function() { }); + describe('#fromPrivkey', function() { + + it('should make a public key from a privkey', function() { + should.exist(Pubkey().fromPrivkey(Privkey().fromRandom())); + }); + + }); + describe('#fromDER', function() { it('should parse this uncompressed public key', function() { From 80625fdbefcd094e11f5d85bc0a351c24654f1f3 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 20:27:45 -0700 Subject: [PATCH 131/280] move static isValid next to instance isValid ...to be easier for developers to parse and distinguish --- lib/address.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/address.js b/lib/address.js index b3b36f3..61399ad 100644 --- a/lib/address.js +++ b/lib/address.js @@ -10,15 +10,6 @@ function Address(obj) { this.set(obj); }; -Address.isValid = function(addrstr) { - try { - var address = new Address().fromString(addrstr); - } catch (e) { - return false; - } - return address.isValid(); -}; - Address.prototype.set = function(obj) { this.hashbuf = obj.hashbuf || this.hashbuf || null; this.networkstr = obj.networkstr || this.networkstr || 'mainnet'; @@ -60,6 +51,15 @@ Address.prototype.fromString = function(str) { return this; } +Address.isValid = function(addrstr) { + try { + var address = new Address().fromString(addrstr); + } catch (e) { + return false; + } + return address.isValid(); +}; + Address.prototype.isValid = function() { try { this.validate(); From b7bde14e062a24b4d9da476f1d0cdca0d662f85c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 28 Aug 2014 20:32:44 -0700 Subject: [PATCH 132/280] add bitcoinjs contributors ...since some of the source code comes from bitcoinjs --- package.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/package.json b/package.json index 5282640..981732f 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,10 @@ "test": "mocha" }, "contributors": [ + { + "name": "Daniel Cousens", + "email": "bitcoin@dcousens.com" + }, { "name": "Gordon Hall", "email": "gordon@bitpay.com" @@ -15,6 +19,10 @@ "name": "Jeff Garzik", "email": "jgarzik@bitpay.com" }, + { + "name": "Kyle Drake", + "email": "kyle@kyledrake.net" + }, { "name": "Manuel Araoz", "email": "manuelaraoz@gmail.com" @@ -34,6 +42,10 @@ { "name": "Stephen Pair", "email": "stephen@bitpay.com" + }, + { + "name": "Wei Lu", + "email": "luwei.here@gmail.com" } ], "keywords": [ From 40e8dfec06767f18f10f358feb2b5734f3a8acd6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 12:38:43 -0700 Subject: [PATCH 133/280] compressed by default with fromRandom --- lib/ecdsa.js | 1 + lib/privkey.js | 6 +++++- test/test.key.js | 5 ++++- test/test.privkey.js | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index e2648aa..e7b33cd 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -31,6 +31,7 @@ ECDSA.prototype.calci = function() { } if (Qprime.point.eq(this.key.pubkey.point)) { + this.sig.compressed = this.key.pubkey.compressed; return this; } } diff --git a/lib/privkey.js b/lib/privkey.js index b1dd12c..e102bf3 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -24,7 +24,11 @@ Privkey.prototype.fromRandom = function() { var bn = BN().fromBuffer(privbuf); var condition = bn.lt(Point.getN()); } while (!condition); - this.bn = bn; + this.set({ + bn: bn, + networkstr: 'mainnet', + compressed: true + }); return this; }; diff --git a/test/test.key.js b/test/test.key.js index e7495dd..488237f 100644 --- a/test/test.key.js +++ b/test/test.key.js @@ -32,7 +32,7 @@ describe('Key', function() { describe("#fromRandom", function() { - it('should make a new priv and pub', function() { + it('should make a new priv and pub, should be compressed, mainnet', function() { var key = new Key(); key.fromRandom(); should.exist(key.privkey); @@ -40,6 +40,9 @@ describe('Key', function() { key.privkey.bn.gt(bn(0)).should.equal(true); key.pubkey.point.getX().gt(bn(0)).should.equal(true); key.pubkey.point.getY().gt(bn(0)).should.equal(true); + key.privkey.compressed.should.equal(true); + key.privkey.networkstr.should.equal('mainnet'); + key.pubkey.compressed.should.equal(true); }); }); diff --git a/test/test.privkey.js b/test/test.privkey.js index 6cc0afa..24f9568 100644 --- a/test/test.privkey.js +++ b/test/test.privkey.js @@ -42,10 +42,11 @@ describe('Privkey', function() { describe('#fromRandom', function() { - it('should set bn gt 0 and lt n', function() { + it('should set bn gt 0 and lt n, and should be compressed', function() { var privkey = Privkey().fromRandom(); privkey.bn.gt(BN(0)).should.equal(true); privkey.bn.lt(Point.getN()).should.equal(true); + privkey.compressed.should.equal(true); }); }); From faa7a81cacea11a6eecca5fbc6d5c21ff10bb2e8 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 12:43:55 -0700 Subject: [PATCH 134/280] Key().fromPrivkey() --- lib/key.js | 6 ++++++ test/test.key.js | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/key.js b/lib/key.js index d7baf4a..bf4ad94 100644 --- a/lib/key.js +++ b/lib/key.js @@ -17,6 +17,12 @@ Key.prototype.set = function(obj) { return this; }; +Key.prototype.fromPrivkey = function(privkey) { + this.privkey = privkey; + this.privkey2pubkey(); + return this; +}; + Key.prototype.fromRandom = function() { this.privkey = Privkey().fromRandom(); this.privkey2pubkey(); diff --git a/test/test.key.js b/test/test.key.js index 488237f..a321c44 100644 --- a/test/test.key.js +++ b/test/test.key.js @@ -30,6 +30,14 @@ describe('Key', function() { }); + describe("#fromPrivkey", function() { + + it('should make a new key from a privkey', function() { + should.exist(Key().fromPrivkey(Privkey().fromRandom()).pubkey); + }); + + }); + describe("#fromRandom", function() { it('should make a new priv and pub, should be compressed, mainnet', function() { From bdb8daaf078dc16d466e8f1d2053083d5d35344f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 13:01:05 -0700 Subject: [PATCH 135/280] remove redundant "test." from test names --- browser/build | 2 +- test/{test.address.js => address.js} | 0 test/{test.aes.js => aes.js} | 0 test/{test.base58.js => base58.js} | 0 test/{test.base58check.js => base58check.js} | 0 test/{test.bip32.js => bip32.js} | 0 test/{test.bn.js => bn.js} | 0 test/{test.bufferreader.js => bufferreader.js} | 0 test/{test.bufferwriter.js => bufferwriter.js} | 0 test/{test.cbc.js => cbc.js} | 0 test/{test.ecdsa.js => ecdsa.js} | 0 test/{test.ecies.js => ecies.js} | 0 test/{test.hash.js => hash.js} | 0 test/{test.kdf.js => kdf.js} | 0 test/{test.key.js => key.js} | 0 test/{test.message.js => message.js} | 0 test/{test.point.js => point.js} | 0 test/{test.privkey.js => privkey.js} | 0 test/{test.pubkey.js => pubkey.js} | 0 test/{test.random.js => random.js} | 0 test/{test.signature.js => signature.js} | 0 test/{test.stealth.js => stealth.js} | 0 test/{test.symenc.js => symenc.js} | 0 23 files changed, 1 insertion(+), 1 deletion(-) rename test/{test.address.js => address.js} (100%) rename test/{test.aes.js => aes.js} (100%) rename test/{test.base58.js => base58.js} (100%) rename test/{test.base58check.js => base58check.js} (100%) rename test/{test.bip32.js => bip32.js} (100%) rename test/{test.bn.js => bn.js} (100%) rename test/{test.bufferreader.js => bufferreader.js} (100%) rename test/{test.bufferwriter.js => bufferwriter.js} (100%) rename test/{test.cbc.js => cbc.js} (100%) rename test/{test.ecdsa.js => ecdsa.js} (100%) rename test/{test.ecies.js => ecies.js} (100%) rename test/{test.hash.js => hash.js} (100%) rename test/{test.kdf.js => kdf.js} (100%) rename test/{test.key.js => key.js} (100%) rename test/{test.message.js => message.js} (100%) rename test/{test.point.js => point.js} (100%) rename test/{test.privkey.js => privkey.js} (100%) rename test/{test.pubkey.js => pubkey.js} (100%) rename test/{test.random.js => random.js} (100%) rename test/{test.signature.js => signature.js} (100%) rename test/{test.stealth.js => stealth.js} (100%) rename test/{test.symenc.js => symenc.js} (100%) diff --git a/browser/build b/browser/build index 136320a..56ace55 100755 --- a/browser/build +++ b/browser/build @@ -1,4 +1,4 @@ #!/bin/bash browserify index.js -o browser/privsec.js -ls test/test.* | xargs browserify -o browser/tests.js +ls test/*.js | xargs browserify -o browser/tests.js diff --git a/test/test.address.js b/test/address.js similarity index 100% rename from test/test.address.js rename to test/address.js diff --git a/test/test.aes.js b/test/aes.js similarity index 100% rename from test/test.aes.js rename to test/aes.js diff --git a/test/test.base58.js b/test/base58.js similarity index 100% rename from test/test.base58.js rename to test/base58.js diff --git a/test/test.base58check.js b/test/base58check.js similarity index 100% rename from test/test.base58check.js rename to test/base58check.js diff --git a/test/test.bip32.js b/test/bip32.js similarity index 100% rename from test/test.bip32.js rename to test/bip32.js diff --git a/test/test.bn.js b/test/bn.js similarity index 100% rename from test/test.bn.js rename to test/bn.js diff --git a/test/test.bufferreader.js b/test/bufferreader.js similarity index 100% rename from test/test.bufferreader.js rename to test/bufferreader.js diff --git a/test/test.bufferwriter.js b/test/bufferwriter.js similarity index 100% rename from test/test.bufferwriter.js rename to test/bufferwriter.js diff --git a/test/test.cbc.js b/test/cbc.js similarity index 100% rename from test/test.cbc.js rename to test/cbc.js diff --git a/test/test.ecdsa.js b/test/ecdsa.js similarity index 100% rename from test/test.ecdsa.js rename to test/ecdsa.js diff --git a/test/test.ecies.js b/test/ecies.js similarity index 100% rename from test/test.ecies.js rename to test/ecies.js diff --git a/test/test.hash.js b/test/hash.js similarity index 100% rename from test/test.hash.js rename to test/hash.js diff --git a/test/test.kdf.js b/test/kdf.js similarity index 100% rename from test/test.kdf.js rename to test/kdf.js diff --git a/test/test.key.js b/test/key.js similarity index 100% rename from test/test.key.js rename to test/key.js diff --git a/test/test.message.js b/test/message.js similarity index 100% rename from test/test.message.js rename to test/message.js diff --git a/test/test.point.js b/test/point.js similarity index 100% rename from test/test.point.js rename to test/point.js diff --git a/test/test.privkey.js b/test/privkey.js similarity index 100% rename from test/test.privkey.js rename to test/privkey.js diff --git a/test/test.pubkey.js b/test/pubkey.js similarity index 100% rename from test/test.pubkey.js rename to test/pubkey.js diff --git a/test/test.random.js b/test/random.js similarity index 100% rename from test/test.random.js rename to test/random.js diff --git a/test/test.signature.js b/test/signature.js similarity index 100% rename from test/test.signature.js rename to test/signature.js diff --git a/test/test.stealth.js b/test/stealth.js similarity index 100% rename from test/test.stealth.js rename to test/stealth.js diff --git a/test/test.symenc.js b/test/symenc.js similarity index 100% rename from test/test.symenc.js rename to test/symenc.js From 40a2007e18b20148e55a8234963681d77ee7a2d6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 13:07:49 -0700 Subject: [PATCH 136/280] confusingly named SymEnc -> accurately named AESCBC This will also further discourage use amongst anyone who doesn't understand what this is. --- index.js | 2 +- lib/expmt/{symenc.js => aescbc.js} | 17 +++++++++-------- lib/expmt/ecies.js | 6 +++--- test/{symenc.js => aescbc.js} | 18 +++++++++--------- 4 files changed, 22 insertions(+), 21 deletions(-) rename lib/expmt/{symenc.js => aescbc.js} (57%) rename test/{symenc.js => aescbc.js} (77%) diff --git a/index.js b/index.js index 7439860..4cc01f9 100644 --- a/index.js +++ b/index.js @@ -23,9 +23,9 @@ bitcore.Signature = require('./lib/signature'); //experimental, nonstandard, or unstable features bitcore.expmt = {}; bitcore.expmt.AES = require('./lib/expmt/aes'); +bitcore.expmt.AESCBC = require('./lib/expmt/aescbc'); bitcore.expmt.CBC = require('./lib/expmt/cbc'); bitcore.expmt.ECIES = require('./lib/expmt/ecies'); -bitcore.expmt.SymEnc = require('./lib/expmt/symenc'); bitcore.expmt.Stealth = require('./lib/expmt/stealth'); //dependencies, subject to change diff --git a/lib/expmt/symenc.js b/lib/expmt/aescbc.js similarity index 57% rename from lib/expmt/symenc.js rename to lib/expmt/aescbc.js index 23629b4..401cb78 100644 --- a/lib/expmt/symenc.js +++ b/lib/expmt/aescbc.js @@ -3,31 +3,32 @@ var CBC = require('./cbc'); var Random = require('../random'); var Hash = require('../hash'); -var SymEnc = function SymEnc() { +// Symmetric encryption with AES and CBC convenience class +var AESCBC = function AESCBC() { }; -SymEnc.encrypt = function(messagebuf, passwordstr) { +AESCBC.encrypt = function(messagebuf, passwordstr) { var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); - return SymEnc.encryptCipherkey(messagebuf, cipherkeybuf); + return AESCBC.encryptCipherkey(messagebuf, cipherkeybuf); }; -SymEnc.decrypt = function(encbuf, passwordstr) { +AESCBC.decrypt = function(encbuf, passwordstr) { var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); - return SymEnc.decryptCipherkey(encbuf, cipherkeybuf); + return AESCBC.decryptCipherkey(encbuf, cipherkeybuf); }; -SymEnc.encryptCipherkey = function(messagebuf, cipherkeybuf, ivbuf) { +AESCBC.encryptCipherkey = function(messagebuf, cipherkeybuf, ivbuf) { ivbuf = ivbuf || Random.getRandomBuffer(128 / 8); var ctbuf = CBC.encrypt(messagebuf, ivbuf, AES, cipherkeybuf); var encbuf = Buffer.concat([ivbuf, ctbuf]); return encbuf; }; -SymEnc.decryptCipherkey = function(encbuf, cipherkeybuf) { +AESCBC.decryptCipherkey = function(encbuf, cipherkeybuf) { var ivbuf = encbuf.slice(0, 128 / 8); var ctbuf = encbuf.slice(128 / 8); var messagebuf = CBC.decrypt(ctbuf, ivbuf, AES, cipherkeybuf); return messagebuf; }; -module.exports = SymEnc; +module.exports = AESCBC; diff --git a/lib/expmt/ecies.js b/lib/expmt/ecies.js index b96cf2a..1bfb057 100644 --- a/lib/expmt/ecies.js +++ b/lib/expmt/ecies.js @@ -1,4 +1,4 @@ -var SymEnc = require('./symenc'); +var AESCBC = require('./aescbc'); var Key = require('../key'); var Point = require('../point'); var Hash = require('../hash'); @@ -22,7 +22,7 @@ ECIES.encrypt = function(messagebuf, tokey, fromkey, ivbuf) { var kEkM = Hash.sha512(Sbuf); var kE = kEkM.slice(0, 32); var kM = kEkM.slice(32, 64); - var c = SymEnc.encryptCipherkey(messagebuf, kE, ivbuf); + var c = AESCBC.encryptCipherkey(messagebuf, kE, ivbuf); var d = Hash.sha256hmac(c, kM); var encbuf = Buffer.concat([Rbuf, c, d]); return encbuf; @@ -45,7 +45,7 @@ ECIES.decrypt = function(encbuf, tokey) { var d2 = Hash.sha256hmac(c, kM); if (d.toString('hex') !== d2.toString('hex')) throw new Error('Invalid checksum'); - var messagebuf = SymEnc.decryptCipherkey(c, kE); + var messagebuf = AESCBC.decryptCipherkey(c, kE); return messagebuf; }; diff --git a/test/symenc.js b/test/aescbc.js similarity index 77% rename from test/symenc.js rename to test/aescbc.js index dc56eec..a1dca59 100644 --- a/test/symenc.js +++ b/test/aescbc.js @@ -1,7 +1,7 @@ var should = require('chai').should(); -var SymEnc = require('../lib/expmt/symenc'); +var AESCBC = require('../lib/expmt/aescbc'); -describe('SymEnc', function() { +describe('AESCBC', function() { describe('@encrypt', function() { @@ -9,7 +9,7 @@ describe('SymEnc', function() { var password = "password"; var messagebuf = new Buffer(128 / 8 - 1); messagebuf.fill(0); - var encbuf = SymEnc.encrypt(messagebuf, password); + var encbuf = AESCBC.encrypt(messagebuf, password); encbuf.length.should.equal(128 / 8 + 128 / 8); }); @@ -21,8 +21,8 @@ describe('SymEnc', function() { var password = "password"; var messagebuf = new Buffer(128 / 8 - 1); messagebuf.fill(0); - var encbuf = SymEnc.encrypt(messagebuf, password); - var messagebuf2 = SymEnc.decrypt(encbuf, password); + var encbuf = AESCBC.encrypt(messagebuf, password); + var messagebuf2 = AESCBC.decrypt(encbuf, password); messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); }); @@ -37,7 +37,7 @@ describe('SymEnc', function() { ivbuf.fill(0); var messagebuf = new Buffer(128 / 8 - 1); messagebuf.fill(0); - var encbuf = SymEnc.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); + var encbuf = AESCBC.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); encbuf.length.should.equal(128 / 8 + 128 / 8); }); @@ -48,7 +48,7 @@ describe('SymEnc', function() { ivbuf.fill(0); var messagebuf = new Buffer(128 / 8); messagebuf.fill(0); - var encbuf = SymEnc.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); + var encbuf = AESCBC.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); encbuf.length.should.equal(128 / 8 + 128 / 8 + 128 / 8); }); @@ -63,8 +63,8 @@ describe('SymEnc', function() { ivbuf.fill(0); var messagebuf = new Buffer(128 / 8); messagebuf.fill(0); - var encbuf = SymEnc.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); - var messagebuf2 = SymEnc.decryptCipherkey(encbuf, cipherkeybuf); + var encbuf = AESCBC.encryptCipherkey(messagebuf, cipherkeybuf, ivbuf); + var messagebuf2 = AESCBC.decryptCipherkey(encbuf, cipherkeybuf); messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); }); From 32fabd0c0fa9a51691611d16317eca4e0d4bae68 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 14:18:56 -0700 Subject: [PATCH 137/280] Key -> Keypair "Keypair" is a more explanatory name, and also should be less confused with other kinds of keys (particularly "cipher keys", which are the keys used in symmetric block ciphers, especially AES). --- index.js | 2 +- lib/bip32.js | 14 +++--- lib/ecdsa.js | 4 +- lib/expmt/ecies.js | 16 +++--- lib/expmt/stealth.js | 52 ++++++++++---------- lib/kdf.js | 13 +++-- lib/{key.js => keypair.js} | 0 lib/message.js | 4 +- test/ecdsa.js | 8 +-- test/ecies.js | 6 +-- test/kdf.js | 16 +++--- test/{key.js => keypair.js} | 30 ++++++------ test/message.js | 14 +++--- test/stealth.js | 98 ++++++++++++++++++------------------- 14 files changed, 138 insertions(+), 139 deletions(-) rename lib/{key.js => keypair.js} (100%) rename test/{key.js => keypair.js} (87%) diff --git a/index.js b/index.js index 4cc01f9..0f9e7e5 100644 --- a/index.js +++ b/index.js @@ -12,7 +12,7 @@ bitcore.Constants = require('./lib/constants'); bitcore.ECDSA = require('./lib/ecdsa'); bitcore.Hash = require('./lib/hash'); bitcore.KDF = require('./lib/kdf'); -bitcore.Key = require('./lib/key'); +bitcore.Keypair = require('./lib/keypair'); bitcore.Message = require('./lib/message'); bitcore.Point = require('./lib/point'); bitcore.Privkey = require('./lib/privkey'); diff --git a/lib/bip32.js b/lib/bip32.js index a88b654..0592a1a 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -1,6 +1,6 @@ var base58 = require('./base58'); var Hash = require('./hash'); -var Key = require('./key'); +var Keypair = require('./keypair'); var Pubkey = require('./pubkey'); var Privkey = require('./privkey'); var Point = require('./point'); @@ -21,7 +21,7 @@ BIP32.prototype.fromRandom = function(networkstr) { this.parentFingerprint = new Buffer([0, 0, 0, 0]); this.childIndex = new Buffer([0, 0, 0, 0]); this.chainCode = Random.getRandomBuffer(32); - this.key = (new Key()).fromRandom(); + this.key = (new Keypair()).fromRandom(); this.hasPrivateKey = true; this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); this.buildExtendedPublicKey(); @@ -63,7 +63,7 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) { this.childIndex = new Buffer([0, 0, 0, 0]); this.chainCode = hash.slice(32, 64); this.version = constants[networkstr].bip32privkey; - this.key = new Key(); + this.key = new Keypair(); this.key.privkey = new Privkey({bn: bn.fromBuffer(hash.slice(0, 32))}); this.key.privkey2pubkey(); this.hasPrivateKey = true; @@ -97,13 +97,13 @@ BIP32.prototype.initFromBytes = function(bytes) { this.version == constants.testnet.bip32pubkey); if (isPrivate && keyBytes[0] == 0) { - this.key = new Key(); + this.key = new Keypair(); this.key.privkey = new Privkey({bn: bn.fromBuffer(keyBytes.slice(1, 33))}); this.key.privkey2pubkey(); this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); this.hasPrivateKey = true; } else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) { - this.key = new Key(); + this.key = new Keypair(); this.key.pubkey = (new Pubkey()).fromDER(keyBytes); this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); this.hasPrivateKey = false; @@ -272,7 +272,7 @@ BIP32.prototype.deriveChild = function(i) { ret = new BIP32(); ret.chainCode = ir; - ret.key = new Key(); + ret.key = new Keypair(); ret.key.privkey = new Privkey({bn: k}); ret.key.privkey2pubkey(); ret.hasPrivateKey = true; @@ -293,7 +293,7 @@ BIP32.prototype.deriveChild = function(i) { ret = new BIP32(); ret.chainCode = ir; - var key = new Key(); + var key = new Keypair(); key.pubkey = newpub; ret.key = key; ret.hasPrivateKey = false; diff --git a/lib/ecdsa.js b/lib/ecdsa.js index e7b33cd..d4b6429 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -1,7 +1,7 @@ var BN = require('./bn'); var Point = require('./point'); var Signature = require('./signature'); -var Key = require('./key'); +var Keypair = require('./keypair'); var Pubkey = require('./pubkey'); var Random = require('./random'); @@ -45,7 +45,7 @@ ECDSA.prototype.fromString = function(str) { if (obj.hashbuf) this.hashbuf = new Buffer(obj.hashbuf, 'hex'); if (obj.key) - this.key = Key().fromString(obj.key); + this.key = Keypair().fromString(obj.key); if (obj.sig) this.sig = Signature().fromString(obj.sig); if (obj.k) diff --git a/lib/expmt/ecies.js b/lib/expmt/ecies.js index 1bfb057..2ddb852 100644 --- a/lib/expmt/ecies.js +++ b/lib/expmt/ecies.js @@ -1,5 +1,5 @@ var AESCBC = require('./aescbc'); -var Key = require('../key'); +var Keypair = require('../keypair'); var Point = require('../point'); var Hash = require('../hash'); var Pubkey = require('../pubkey'); @@ -10,12 +10,12 @@ var ECIES = function ECIES() { return new ECIES(); }; -ECIES.encrypt = function(messagebuf, tokey, fromkey, ivbuf) { - var r = fromkey.privkey.bn; - var R = fromkey.pubkey.point; - var Rpubkey = fromkey.pubkey; +ECIES.encrypt = function(messagebuf, tokeypair, fromkeypair, ivbuf) { + var r = fromkeypair.privkey.bn; + var R = fromkeypair.pubkey.point; + var Rpubkey = fromkeypair.pubkey; var Rbuf = Rpubkey.toDER(true); - var KB = tokey.pubkey.point; + var KB = tokeypair.pubkey.point; var P = KB.mul(r); var S = P.getX(); var Sbuf = S.toBuffer({size: 32}); @@ -28,8 +28,8 @@ ECIES.encrypt = function(messagebuf, tokey, fromkey, ivbuf) { return encbuf; }; -ECIES.decrypt = function(encbuf, tokey) { - var kB = tokey.privkey.bn; +ECIES.decrypt = function(encbuf, tokeypair) { + var kB = tokeypair.privkey.bn; var frompubkey = Pubkey().fromDER(encbuf.slice(0, 33)); var R = frompubkey.point; var P = R.mul(kB); diff --git a/lib/expmt/stealth.js b/lib/expmt/stealth.js index 877ff13..879e8ee 100644 --- a/lib/expmt/stealth.js +++ b/lib/expmt/stealth.js @@ -1,4 +1,4 @@ -var Key = require('../key'); +var Keypair = require('../keypair'); var Privkey = require('../privkey'); var Pubkey = require('../pubkey'); var Point = require('../point'); @@ -14,8 +14,8 @@ var Stealth = function Stealth(obj) { }; Stealth.prototype.set = function(obj) { - this.payloadKey = obj.payloadKey || this.payloadKey; - this.scanKey = obj.scanKey || this.scanKey; + this.payloadKeypair = obj.payloadKeypair || this.payloadKeypair; + this.scanKeypair = obj.scanKeypair || this.scanKeypair; return this; }; @@ -27,9 +27,9 @@ Stealth.prototype.fromAddressBuffer = function(buf) { var sPubBuf = buf.slice(33, 66); var payloadPubkey = Pubkey().fromDER(pPubBuf); - this.payloadKey = Key({pubkey: payloadPubkey}); + this.payloadKeypair = Keypair({pubkey: payloadPubkey}); var scanPubkey = Pubkey().fromDER(sPubBuf); - this.scanKey = Key({pubkey: scanPubkey}); + this.scanKeypair = Keypair({pubkey: scanPubkey}); return this; }; @@ -42,48 +42,48 @@ Stealth.prototype.fromAddressString = function(str) { }; Stealth.prototype.fromRandom = function() { - this.payloadKey = Key().fromRandom(); - this.scanKey = Key().fromRandom(); + this.payloadKeypair = Keypair().fromRandom(); + this.scanKeypair = Keypair().fromRandom(); return this; }; -Stealth.prototype.getSharedKeyAsReceiver = function(senderPubkey) { - var sharedSecretPoint = senderPubkey.point.mul(this.scanKey.privkey.bn); +Stealth.prototype.getSharedKeypairAsReceiver = function(senderPubkey) { + var sharedSecretPoint = senderPubkey.point.mul(this.scanKeypair.privkey.bn); var sharedSecretPubkey = Pubkey({point: sharedSecretPoint}); var buf = sharedSecretPubkey.toDER(true); - var sharedKey = KDF.sha256hmac2key(buf); + var sharedKeypair = KDF.sha256hmac2keypair(buf); - return sharedKey; + return sharedKeypair; }; -Stealth.prototype.getSharedKeyAsSender = function(senderKey) { - var sharedSecretPoint = this.scanKey.pubkey.point.mul(senderKey.privkey.bn); +Stealth.prototype.getSharedKeypairAsSender = function(senderKeypair) { + var sharedSecretPoint = this.scanKeypair.pubkey.point.mul(senderKeypair.privkey.bn); var sharedSecretPubkey = Pubkey({point: sharedSecretPoint}); var buf = sharedSecretPubkey.toDER(true); - var sharedKey = KDF.sha256hmac2key(buf); + var sharedKeypair = KDF.sha256hmac2keypair(buf); - return sharedKey; + return sharedKeypair; }; Stealth.prototype.getReceivePubkeyAsReceiver = function(senderPubkey) { - var sharedKey = this.getSharedKeyAsReceiver(senderPubkey); - var pubkey = Pubkey({point: this.payloadKey.pubkey.point.add(sharedKey.pubkey.point)}); + var sharedKeypair = this.getSharedKeypairAsReceiver(senderPubkey); + var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); return pubkey; }; -Stealth.prototype.getReceivePubkeyAsSender = function(senderKey) { - var sharedKey = this.getSharedKeyAsSender(senderKey); - var pubkey = Pubkey({point: this.payloadKey.pubkey.point.add(sharedKey.pubkey.point)}); +Stealth.prototype.getReceivePubkeyAsSender = function(senderKeypair) { + var sharedKeypair = this.getSharedKeypairAsSender(senderKeypair); + var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); return pubkey; }; -Stealth.prototype.getReceiveKey = function(senderPubkey) { - var sharedKey = this.getSharedKeyAsReceiver(senderPubkey); - var privkey = Privkey({bn: this.payloadKey.privkey.bn.add(sharedKey.privkey.bn).mod(Point.getN())}); - var key = Key({privkey: privkey}); +Stealth.prototype.getReceiveKeypair = function(senderPubkey) { + var sharedKeypair = this.getSharedKeypairAsReceiver(senderPubkey); + var privkey = Privkey({bn: this.payloadKeypair.privkey.bn.add(sharedKeypair.privkey.bn).mod(Point.getN())}); + var key = Keypair({privkey: privkey}); key.privkey2pubkey(); return key; @@ -101,8 +101,8 @@ Stealth.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) { }; Stealth.prototype.toAddressBuffer = function() { - var pBuf = this.payloadKey.pubkey.toDER(true); - var sBuf = this.scanKey.pubkey.toDER(true); + var pBuf = this.payloadKeypair.pubkey.toDER(true); + var sBuf = this.scanKeypair.pubkey.toDER(true); return Buffer.concat([pBuf, sBuf]); }; diff --git a/lib/kdf.js b/lib/kdf.js index f286671..17d738c 100644 --- a/lib/kdf.js +++ b/lib/kdf.js @@ -2,21 +2,20 @@ var Bn = require('./bn'); var Privkey = require('./privkey'); var Point = require('./point'); var Pubkey = require('./pubkey'); -var Key = require('./key'); +var Keypair = require('./keypair'); var Hash = require('./hash'); function KDF() { }; -KDF.buf2key = function(buf) { - return KDF.sha256hmac2key(buf); +KDF.buf2keypair = function(buf) { + return KDF.sha256hmac2keypair(buf); }; -KDF.sha256hmac2key = function(buf) { +KDF.sha256hmac2keypair = function(buf) { var privkey = KDF.sha256hmac2privkey(buf); - var key = new Key({privkey: privkey}); - key.privkey2pubkey(); - return key; + var keypair = Keypair().fromPrivkey(privkey); + return keypair; }; KDF.sha256hmac2privkey = function(buf) { diff --git a/lib/key.js b/lib/keypair.js similarity index 100% rename from lib/key.js rename to lib/keypair.js diff --git a/lib/message.js b/lib/message.js index 64bb1f4..1cfbe3c 100644 --- a/lib/message.js +++ b/lib/message.js @@ -1,5 +1,5 @@ var ECDSA = require('./ecdsa'); -var Key = require('./key'); +var Keypair = require('./keypair'); var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var BufferWriter = require('./bufferwriter'); @@ -73,7 +73,7 @@ Message.prototype.verify = function() { var ecdsa = new ECDSA(); ecdsa.hashbuf = hashbuf; ecdsa.sig = this.sig; - ecdsa.key = new Key(); + ecdsa.key = new Keypair(); ecdsa.key.pubkey = ecdsa.sig2pubkey(); if (!ecdsa.verify()) { diff --git a/test/ecdsa.js b/test/ecdsa.js index 3a8238a..0a9bff9 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -1,6 +1,6 @@ var ECDSA = require('../lib/ecdsa'); var Hash = require('../lib/hash'); -var Key = require('../lib/key'); +var Keypair = require('../lib/keypair'); var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); var Signature = require('../lib/signature'); @@ -16,7 +16,7 @@ describe("ECDSA", function() { var ecdsa = new ECDSA(); ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); - ecdsa.key = new Key(); + ecdsa.key = new Keypair(); ecdsa.key.privkey = new Privkey({bn: BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))}); ecdsa.key.pubkey = new Pubkey({ point: point(BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), @@ -45,7 +45,7 @@ describe("ECDSA", function() { var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10); var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10); var ecdsa = new ECDSA(); - ecdsa.key = new Key(); + ecdsa.key = new Keypair(); ecdsa.key.privkey = Privkey(); ecdsa.key.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test'))); ecdsa.key.privkey2pubkey(); @@ -119,7 +119,7 @@ describe("ECDSA", function() { ecdsa.hashbuf = Hash.sha256(new Buffer('test')); var pk = new Pubkey(); pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); - ecdsa.key = new Key(); + ecdsa.key = new Keypair(); ecdsa.key.pubkey = pk; ecdsa.sig = new Signature(); ecdsa.sig.r = BN(0); diff --git a/test/ecies.js b/test/ecies.js index 4ee661b..213d21b 100644 --- a/test/ecies.js +++ b/test/ecies.js @@ -1,6 +1,6 @@ var ECIES = require('../lib/expmt/ecies'); var should = require('chai').should(); -var Key = require('../lib/key'); +var Keypair = require('../lib/keypair'); var Hash = require('../lib/hash'); describe('#ECIES', function() { @@ -15,8 +15,8 @@ describe('#ECIES', function() { should.exist(ecies); }); - var fromkey = Key().fromRandom(); - var tokey = Key().fromRandom(); + var fromkey = Keypair().fromRandom(); + var tokey = Keypair().fromRandom(); var messagebuf = Hash.sha256(new Buffer('my message is the hash of this string')); describe('@encrypt', function() { diff --git a/test/kdf.js b/test/kdf.js index faeeda2..c334dd2 100644 --- a/test/kdf.js +++ b/test/kdf.js @@ -4,24 +4,24 @@ var Hash = require('../lib/hash'); describe('KDF', function() { - describe('#buf2key', function() { + describe('#buf2keypair', function() { it('should compute these known values', function() { var buf = Hash.sha256(new Buffer('test')); - var key = KDF.buf2key(buf); - key.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); - key.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1'); + var keypair = KDF.buf2keypair(buf); + keypair.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); + keypair.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1'); }); }); - describe('#sha256hmac2key', function() { + describe('#sha256hmac2keypair', function() { it('should compute these known values', function() { var buf = Hash.sha256(new Buffer('test')); - var key = KDF.sha256hmac2key(buf); - key.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); - key.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1'); + var keypair = KDF.sha256hmac2keypair(buf); + keypair.privkey.toString().should.equal('KxxVszVMFLGzmxpxR7sMSaWDmqMKLVhKebX5vZbGHyuR8spreQ7V'); + keypair.pubkey.toString().should.equal('03774f761ae89a0d2fda0d532bad62286ae8fcda9bc38c060036296085592a97c1'); }); }); diff --git a/test/key.js b/test/keypair.js similarity index 87% rename from test/key.js rename to test/keypair.js index a321c44..2ff85b7 100644 --- a/test/key.js +++ b/test/keypair.js @@ -4,19 +4,19 @@ var point = require('../lib/point'); var Address = require('../lib/address'); var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); -var Key = require('../lib/key'); +var Keypair = require('../lib/keypair'); -describe('Key', function() { +describe('Keypair', function() { it('should make a blank key', function() { - var key = new Key(); + var key = new Keypair(); should.exist(key); }); it('should make a key with a priv and pub', function() { var priv = new Privkey(); var pub = new Pubkey(); - var key = new Key({privkey: priv, pubkey: pub}); + var key = new Keypair({privkey: priv, pubkey: pub}); should.exist(key); should.exist(key.privkey); should.exist(key.pubkey); @@ -25,7 +25,7 @@ describe('Key', function() { describe("#set", function() { it('should make a new priv and pub', function() { - should.exist(Key().set({privkey: Privkey()}).privkey); + should.exist(Keypair().set({privkey: Privkey()}).privkey); }); }); @@ -33,7 +33,7 @@ describe('Key', function() { describe("#fromPrivkey", function() { it('should make a new key from a privkey', function() { - should.exist(Key().fromPrivkey(Privkey().fromRandom()).pubkey); + should.exist(Keypair().fromPrivkey(Privkey().fromRandom()).pubkey); }); }); @@ -41,7 +41,7 @@ describe('Key', function() { describe("#fromRandom", function() { it('should make a new priv and pub, should be compressed, mainnet', function() { - var key = new Key(); + var key = new Keypair(); key.fromRandom(); should.exist(key.privkey); should.exist(key.pubkey); @@ -58,7 +58,7 @@ describe('Key', function() { describe("#fromString()", function() { it('should recover a key creating with toString', function() { - var key = new Key(); + var key = new Keypair(); key.fromRandom(); var priv = key.privkey; var pub = key.pubkey; @@ -71,7 +71,7 @@ describe('Key', function() { }); it('should work with only Privkey set', function() { - var key = new Key(); + var key = new Keypair(); key.fromRandom(); key.pubkey = undefined; var priv = key.privkey; @@ -82,7 +82,7 @@ describe('Key', function() { }); it('should work with only Pubkey set', function() { - var key = new Key(); + var key = new Keypair(); key.fromRandom(); key.privkey = undefined; var pub = key.pubkey; @@ -99,7 +99,7 @@ describe('Key', function() { it('should return an address', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; - var key = new Key(); + var key = new Keypair(); key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); key.privkey2pubkey(); key.getAddress().toString().should.equal((new Address()).fromPubkey(key.pubkey).toString()); @@ -112,7 +112,7 @@ describe('Key', function() { it('should convert this known Privkey to known Pubkey', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; - var key = new Key(); + var key = new Keypair(); key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); key.privkey2pubkey(); key.pubkey.toString().should.equal(pubhex); @@ -120,7 +120,7 @@ describe('Key', function() { it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; - var key = new Key(); + var key = new Keypair(); key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); key.privkey.compressed = true; key.privkey2pubkey(); @@ -129,7 +129,7 @@ describe('Key', function() { it('should convert this known Privkey to known Pubkey and preserve compressed=true', function() { var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; - var key = new Key(); + var key = new Keypair(); key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); key.privkey.compressed = false; key.privkey2pubkey(); @@ -141,7 +141,7 @@ describe('Key', function() { describe("#toString()", function() { it('should exist', function() { - var key = new Key(); + var key = new Keypair(); key.fromRandom(); should.exist(key.toString()); }); diff --git a/test/message.js b/test/message.js index 84e65e2..f90e5f6 100644 --- a/test/message.js +++ b/test/message.js @@ -1,6 +1,6 @@ var Address = require('../lib/address'); var Message = require('../lib/message'); -var Key = require('../lib/key'); +var Keypair = require('../lib/keypair'); var should = require('chai').should(); describe('Message', function() { @@ -26,7 +26,7 @@ describe('Message', function() { describe('@sign', function() { var messagebuf = new Buffer('this is my message'); - var key = Key().fromRandom(); + var key = Keypair().fromRandom(); it('should return a base64 string', function() { var sigstr = Message.sign(messagebuf, key); @@ -35,7 +35,7 @@ describe('Message', function() { }); it('should sign with a compressed pubkey', function() { - var key = Key().fromRandom(); + var key = Keypair().fromRandom(); key.pubkey.compressed = true; var sigstr = Message.sign(messagebuf, key); var sigbuf = new Buffer(sigstr, 'base64'); @@ -44,7 +44,7 @@ describe('Message', function() { }); it('should sign with an uncompressed pubkey', function() { - var key = Key().fromRandom(); + var key = Keypair().fromRandom(); key.pubkey.compressed = false; var sigstr = Message.sign(messagebuf, key); var sigbuf = new Buffer(sigstr, 'base64'); @@ -56,7 +56,7 @@ describe('Message', function() { describe('@verify', function() { var messagebuf = new Buffer('this is my message'); - var key = Key().fromRandom(); + var key = Keypair().fromRandom(); it('should verify a signed message', function() { var sigstr = Message.sign(messagebuf, key); @@ -75,7 +75,7 @@ describe('Message', function() { describe('#sign', function() { var messagebuf = new Buffer('this is my message'); - var key = Key().fromRandom(); + var key = Keypair().fromRandom(); it('should sign a message', function() { var message = new Message(); @@ -90,7 +90,7 @@ describe('Message', function() { describe('#verify', function() { var messagebuf = new Buffer('this is my message'); - var key = Key().fromRandom(); + var key = Keypair().fromRandom(); it('should verify a message that was just signed', function() { var message = new Message(); diff --git a/test/stealth.js b/test/stealth.js index 138b16b..cab2700 100644 --- a/test/stealth.js +++ b/test/stealth.js @@ -1,6 +1,6 @@ var should = require('chai').should(); var Stealth = require('../lib/expmt/stealth'); -var Key = require('../lib/key'); +var Keypair = require('../lib/keypair'); var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); var BN = require('../lib/bn'); @@ -10,19 +10,19 @@ var base58check = require('../lib/base58check'); describe('Stealth', function() { var stealth = Stealth(); - stealth.payloadKey = Key(); - stealth.payloadKey.privkey = Privkey(); - stealth.payloadKey.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1'))); - stealth.payloadKey.privkey2pubkey(); - stealth.scanKey = Key(); - stealth.scanKey.privkey = Privkey(); - stealth.scanKey.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2'))); - stealth.scanKey.privkey2pubkey(); + stealth.payloadKeypair = Keypair(); + stealth.payloadKeypair.privkey = Privkey(); + stealth.payloadKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1'))); + stealth.payloadKeypair.privkey2pubkey(); + stealth.scanKeypair = Keypair(); + stealth.scanKeypair.privkey = Privkey(); + stealth.scanKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2'))); + stealth.scanKeypair.privkey2pubkey(); - var senderKey = Key(); - senderKey.privkey = Privkey(); - senderKey.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); - senderKey.privkey2pubkey(); + var senderKeypair = Keypair(); + senderKeypair.privkey = Privkey(); + senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); + senderKeypair.privkey2pubkey(); var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW'; @@ -39,7 +39,7 @@ describe('Stealth', function() { describe('#set', function() { it('should set payload key', function() { - should.exist(Stealth().set({payloadKey: stealth.payloadKey}).payloadKey); + should.exist(Stealth().set({payloadKeypair: stealth.payloadKeypair}).payloadKeypair); }); }); @@ -50,8 +50,8 @@ describe('Stealth', function() { var stealth2 = new Stealth(); var buf = base58check.decode(addressString); stealth2.fromAddressBuffer(buf); - stealth2.payloadKey.pubkey.toString().should.equal(stealth.payloadKey.pubkey.toString()); - stealth2.scanKey.pubkey.toString().should.equal(stealth.scanKey.pubkey.toString()); + stealth2.payloadKeypair.pubkey.toString().should.equal(stealth.payloadKeypair.pubkey.toString()); + stealth2.scanKeypair.pubkey.toString().should.equal(stealth.scanKeypair.pubkey.toString()); }); }); @@ -61,8 +61,8 @@ describe('Stealth', function() { it('should give a stealth address with the right pubkeys', function() { var stealth2 = new Stealth(); stealth2.fromAddressString(addressString); - stealth2.payloadKey.pubkey.toString().should.equal(stealth.payloadKey.pubkey.toString()); - stealth2.scanKey.pubkey.toString().should.equal(stealth.scanKey.pubkey.toString()); + stealth2.payloadKeypair.pubkey.toString().should.equal(stealth.payloadKeypair.pubkey.toString()); + stealth2.scanKeypair.pubkey.toString().should.equal(stealth.scanKeypair.pubkey.toString()); }); }); @@ -71,42 +71,42 @@ describe('Stealth', function() { it('should create a new stealth from random', function() { var stealth = Stealth().fromRandom(); - should.exist(stealth.payloadKey.privkey.bn.gt(0)); - should.exist(stealth.scanKey.privkey.bn.gt(0)); + should.exist(stealth.payloadKeypair.privkey.bn.gt(0)); + should.exist(stealth.scanKeypair.privkey.bn.gt(0)); }); }); - describe('#getSharedKeyAsReceiver', function() { + describe('#getSharedKeypairAsReceiver', function() { it('should return a key', function() { - var key = stealth.getSharedKeyAsReceiver(senderKey.pubkey); - (key instanceof Key).should.equal(true); + var key = stealth.getSharedKeypairAsReceiver(senderKeypair.pubkey); + (key instanceof Keypair).should.equal(true); }); }); - describe('#getSharedKeyAsSender', function() { + describe('#getSharedKeypairAsSender', function() { it('should return a key', function() { var stealth2 = new Stealth(); - stealth2.payloadKey = new Key(); - stealth2.payloadKey.pubkey = stealth.payloadKey.pubkey; - stealth2.scanKey = new Key(); - stealth2.scanKey.pubkey = stealth.scanKey.pubkey; - var key = stealth2.getSharedKeyAsSender(senderKey); - (key instanceof Key).should.equal(true); + stealth2.payloadKeypair = new Keypair(); + stealth2.payloadKeypair.pubkey = stealth.payloadKeypair.pubkey; + stealth2.scanKeypair = new Keypair(); + stealth2.scanKeypair.pubkey = stealth.scanKeypair.pubkey; + var key = stealth2.getSharedKeypairAsSender(senderKeypair); + (key instanceof Keypair).should.equal(true); }); - it('should return the same key as getSharedKeyAsReceiver', function() { + it('should return the same key as getSharedKeypairAsReceiver', function() { var stealth2 = new Stealth(); - stealth2.payloadKey = new Key(); - stealth2.payloadKey.pubkey = stealth.payloadKey.pubkey; - stealth2.scanKey = new Key(); - stealth2.scanKey.pubkey = stealth.scanKey.pubkey; - var key = stealth2.getSharedKeyAsSender(senderKey); + stealth2.payloadKeypair = new Keypair(); + stealth2.payloadKeypair.pubkey = stealth.payloadKeypair.pubkey; + stealth2.scanKeypair = new Keypair(); + stealth2.scanKeypair.pubkey = stealth.scanKeypair.pubkey; + var key = stealth2.getSharedKeypairAsSender(senderKeypair); - var key2 = stealth.getSharedKeyAsReceiver(senderKey.pubkey); + var key2 = stealth.getSharedKeypairAsReceiver(senderKeypair.pubkey); key.toString().should.equal(key2.toString()); }); @@ -115,7 +115,7 @@ describe('Stealth', function() { describe('#getReceivePubkeyAsReceiver', function() { it('should return a pubkey', function() { - var pubkey = stealth.getReceivePubkeyAsReceiver(senderKey.pubkey); + var pubkey = stealth.getReceivePubkeyAsReceiver(senderKeypair.pubkey); (pubkey instanceof Pubkey).should.equal(true); }); @@ -124,33 +124,33 @@ describe('Stealth', function() { describe('#getReceivePubkeyAsSender', function() { it('should return a pubkey', function() { - var pubkey = stealth.getReceivePubkeyAsSender(senderKey); + var pubkey = stealth.getReceivePubkeyAsSender(senderKeypair); (pubkey instanceof Pubkey).should.equal(true); }); it('should return the same pubkey as getReceivePubkeyAsReceiver', function() { - var pubkey = stealth.getReceivePubkeyAsSender(senderKey); - var pubkey2 = stealth.getReceivePubkeyAsReceiver(senderKey.pubkey); + var pubkey = stealth.getReceivePubkeyAsSender(senderKeypair); + var pubkey2 = stealth.getReceivePubkeyAsReceiver(senderKeypair.pubkey); pubkey2.toString().should.equal(pubkey.toString()); }); }); - describe('#getReceiveKey', function() { + describe('#getReceiveKeypair', function() { it('should return a key', function() { - var key = stealth.getReceiveKey(senderKey.pubkey); - (key instanceof Key).should.equal(true); + var key = stealth.getReceiveKeypair(senderKeypair.pubkey); + (key instanceof Keypair).should.equal(true); }); it('should return a key with the same pubkey as getReceivePubkeyAsReceiver', function() { - var key = stealth.getReceiveKey(senderKey.pubkey); - var pubkey = stealth.getReceivePubkeyAsReceiver(senderKey.pubkey); + var key = stealth.getReceiveKeypair(senderKeypair.pubkey); + var pubkey = stealth.getReceivePubkeyAsReceiver(senderKeypair.pubkey); key.pubkey.toString().should.equal(pubkey.toString()); }); it('should return private key with length 32 or less', function() { - var key = stealth.getReceiveKey(senderKey.pubkey); + var key = stealth.getReceiveKeypair(senderKeypair.pubkey); key.privkey.bn.toBuffer().length.should.be.below(33); }); @@ -160,12 +160,12 @@ describe('Stealth', function() { it('should return true if it (the transaction or message) is for me', function() { var pubkeyhash = new Buffer('3cb64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); - stealth.isForMe(senderKey.pubkey, pubkeyhash).should.equal(true); + stealth.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(true); }); it('should return false if it (the transaction or message) is not for me', function() { var pubkeyhash = new Buffer('00b64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); - stealth.isForMe(senderKey.pubkey, pubkeyhash).should.equal(false); + stealth.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(false); }); }); From f028b6b91399243776aeef008977965a58179aa7 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 14:34:01 -0700 Subject: [PATCH 138/280] Stealth -> Stealthkey More explanatory. Will break up into separate Stealthkey, StealthAddress, and StealthMessage classes. --- lib/expmt/{stealth.js => stealthkey.js} | 34 ++--- test/stealth.js | 190 ------------------------ test/stealthkey.js | 190 ++++++++++++++++++++++++ 3 files changed, 207 insertions(+), 207 deletions(-) rename lib/expmt/{stealth.js => stealthkey.js} (73%) delete mode 100644 test/stealth.js create mode 100644 test/stealthkey.js diff --git a/lib/expmt/stealth.js b/lib/expmt/stealthkey.js similarity index 73% rename from lib/expmt/stealth.js rename to lib/expmt/stealthkey.js index 879e8ee..9d25830 100644 --- a/lib/expmt/stealth.js +++ b/lib/expmt/stealthkey.js @@ -6,22 +6,22 @@ var Hash = require('../hash'); var KDF = require('../kdf'); var base58check = require('../base58check'); -var Stealth = function Stealth(obj) { - if (!(this instanceof Stealth)) - return new Stealth(obj); +var Stealthkey = function Stealthkey(obj) { + if (!(this instanceof Stealthkey)) + return new Stealthkey(obj); if (obj) this.set(obj); }; -Stealth.prototype.set = function(obj) { +Stealthkey.prototype.set = function(obj) { this.payloadKeypair = obj.payloadKeypair || this.payloadKeypair; this.scanKeypair = obj.scanKeypair || this.scanKeypair; return this; }; -Stealth.prototype.fromAddressBuffer = function(buf) { +Stealthkey.prototype.fromAddressBuffer = function(buf) { if (!Buffer.isBuffer(buf) || buf.length !== 66) - throw new Error('stealth: A stealth address must have length 66'); + throw new Error('stealthkey: A stealthkey address must have length 66'); var pPubBuf = buf.slice(0, 33); var sPubBuf = buf.slice(33, 66); @@ -34,21 +34,21 @@ Stealth.prototype.fromAddressBuffer = function(buf) { return this; }; -Stealth.prototype.fromAddressString = function(str) { +Stealthkey.prototype.fromAddressString = function(str) { var buf = base58check.decode(str); this.fromAddressBuffer(buf); return this; }; -Stealth.prototype.fromRandom = function() { +Stealthkey.prototype.fromRandom = function() { this.payloadKeypair = Keypair().fromRandom(); this.scanKeypair = Keypair().fromRandom(); return this; }; -Stealth.prototype.getSharedKeypairAsReceiver = function(senderPubkey) { +Stealthkey.prototype.getSharedKeypairAsReceiver = function(senderPubkey) { var sharedSecretPoint = senderPubkey.point.mul(this.scanKeypair.privkey.bn); var sharedSecretPubkey = Pubkey({point: sharedSecretPoint}); var buf = sharedSecretPubkey.toDER(true); @@ -57,7 +57,7 @@ Stealth.prototype.getSharedKeypairAsReceiver = function(senderPubkey) { return sharedKeypair; }; -Stealth.prototype.getSharedKeypairAsSender = function(senderKeypair) { +Stealthkey.prototype.getSharedKeypairAsSender = function(senderKeypair) { var sharedSecretPoint = this.scanKeypair.pubkey.point.mul(senderKeypair.privkey.bn); var sharedSecretPubkey = Pubkey({point: sharedSecretPoint}); var buf = sharedSecretPubkey.toDER(true); @@ -66,21 +66,21 @@ Stealth.prototype.getSharedKeypairAsSender = function(senderKeypair) { return sharedKeypair; }; -Stealth.prototype.getReceivePubkeyAsReceiver = function(senderPubkey) { +Stealthkey.prototype.getReceivePubkeyAsReceiver = function(senderPubkey) { var sharedKeypair = this.getSharedKeypairAsReceiver(senderPubkey); var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); return pubkey; }; -Stealth.prototype.getReceivePubkeyAsSender = function(senderKeypair) { +Stealthkey.prototype.getReceivePubkeyAsSender = function(senderKeypair) { var sharedKeypair = this.getSharedKeypairAsSender(senderKeypair); var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); return pubkey; }; -Stealth.prototype.getReceiveKeypair = function(senderPubkey) { +Stealthkey.prototype.getReceiveKeypair = function(senderPubkey) { var sharedKeypair = this.getSharedKeypairAsReceiver(senderPubkey); var privkey = Privkey({bn: this.payloadKeypair.privkey.bn.add(sharedKeypair.privkey.bn).mod(Point.getN())}); var key = Keypair({privkey: privkey}); @@ -89,7 +89,7 @@ Stealth.prototype.getReceiveKeypair = function(senderPubkey) { return key; }; -Stealth.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) { +Stealthkey.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) { var pubkey = this.getReceivePubkeyAsReceiver(senderPubkey); var pubkeybuf = pubkey.toDER(true); var pubkeyhash = Hash.sha256ripemd160(pubkeybuf); @@ -100,18 +100,18 @@ Stealth.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) { return false; }; -Stealth.prototype.toAddressBuffer = function() { +Stealthkey.prototype.toAddressBuffer = function() { var pBuf = this.payloadKeypair.pubkey.toDER(true); var sBuf = this.scanKeypair.pubkey.toDER(true); return Buffer.concat([pBuf, sBuf]); }; -Stealth.prototype.toAddressString = function() { +Stealthkey.prototype.toAddressString = function() { var buf = this.toAddressBuffer(); var b58 = base58check.encode(buf); return b58; }; -module.exports = Stealth; +module.exports = Stealthkey; diff --git a/test/stealth.js b/test/stealth.js deleted file mode 100644 index cab2700..0000000 --- a/test/stealth.js +++ /dev/null @@ -1,190 +0,0 @@ -var should = require('chai').should(); -var Stealth = require('../lib/expmt/stealth'); -var Keypair = require('../lib/keypair'); -var Privkey = require('../lib/privkey'); -var Pubkey = require('../lib/pubkey'); -var BN = require('../lib/bn'); -var Hash = require('../lib/hash'); -var base58check = require('../lib/base58check'); - -describe('Stealth', function() { - - var stealth = Stealth(); - stealth.payloadKeypair = Keypair(); - stealth.payloadKeypair.privkey = Privkey(); - stealth.payloadKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1'))); - stealth.payloadKeypair.privkey2pubkey(); - stealth.scanKeypair = Keypair(); - stealth.scanKeypair.privkey = Privkey(); - stealth.scanKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2'))); - stealth.scanKeypair.privkey2pubkey(); - - var senderKeypair = Keypair(); - senderKeypair.privkey = Privkey(); - senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); - senderKeypair.privkey2pubkey(); - - var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW'; - - it('should create a new stealth', function() { - var stealth = new Stealth(); - should.exist(stealth); - }); - - it('should create a new stealth without using "new"', function() { - var stealth = Stealth(); - should.exist(stealth); - }); - - describe('#set', function() { - - it('should set payload key', function() { - should.exist(Stealth().set({payloadKeypair: stealth.payloadKeypair}).payloadKeypair); - }); - - }); - - describe('#fromAddressBuffer', function() { - - it('should give a stealth address with the right pubkeys', function() { - var stealth2 = new Stealth(); - var buf = base58check.decode(addressString); - stealth2.fromAddressBuffer(buf); - stealth2.payloadKeypair.pubkey.toString().should.equal(stealth.payloadKeypair.pubkey.toString()); - stealth2.scanKeypair.pubkey.toString().should.equal(stealth.scanKeypair.pubkey.toString()); - }); - - }); - - describe('#fromAddressString', function() { - - it('should give a stealth address with the right pubkeys', function() { - var stealth2 = new Stealth(); - stealth2.fromAddressString(addressString); - stealth2.payloadKeypair.pubkey.toString().should.equal(stealth.payloadKeypair.pubkey.toString()); - stealth2.scanKeypair.pubkey.toString().should.equal(stealth.scanKeypair.pubkey.toString()); - }); - - }); - - describe('#fromRandom', function() { - - it('should create a new stealth from random', function() { - var stealth = Stealth().fromRandom(); - should.exist(stealth.payloadKeypair.privkey.bn.gt(0)); - should.exist(stealth.scanKeypair.privkey.bn.gt(0)); - }); - - }); - - describe('#getSharedKeypairAsReceiver', function() { - - it('should return a key', function() { - var key = stealth.getSharedKeypairAsReceiver(senderKeypair.pubkey); - (key instanceof Keypair).should.equal(true); - }); - - }); - - describe('#getSharedKeypairAsSender', function() { - - it('should return a key', function() { - var stealth2 = new Stealth(); - stealth2.payloadKeypair = new Keypair(); - stealth2.payloadKeypair.pubkey = stealth.payloadKeypair.pubkey; - stealth2.scanKeypair = new Keypair(); - stealth2.scanKeypair.pubkey = stealth.scanKeypair.pubkey; - var key = stealth2.getSharedKeypairAsSender(senderKeypair); - (key instanceof Keypair).should.equal(true); - }); - - it('should return the same key as getSharedKeypairAsReceiver', function() { - var stealth2 = new Stealth(); - stealth2.payloadKeypair = new Keypair(); - stealth2.payloadKeypair.pubkey = stealth.payloadKeypair.pubkey; - stealth2.scanKeypair = new Keypair(); - stealth2.scanKeypair.pubkey = stealth.scanKeypair.pubkey; - var key = stealth2.getSharedKeypairAsSender(senderKeypair); - - var key2 = stealth.getSharedKeypairAsReceiver(senderKeypair.pubkey); - key.toString().should.equal(key2.toString()); - }); - - }); - - describe('#getReceivePubkeyAsReceiver', function() { - - it('should return a pubkey', function() { - var pubkey = stealth.getReceivePubkeyAsReceiver(senderKeypair.pubkey); - (pubkey instanceof Pubkey).should.equal(true); - }); - - }); - - describe('#getReceivePubkeyAsSender', function() { - - it('should return a pubkey', function() { - var pubkey = stealth.getReceivePubkeyAsSender(senderKeypair); - (pubkey instanceof Pubkey).should.equal(true); - }); - - it('should return the same pubkey as getReceivePubkeyAsReceiver', function() { - var pubkey = stealth.getReceivePubkeyAsSender(senderKeypair); - var pubkey2 = stealth.getReceivePubkeyAsReceiver(senderKeypair.pubkey); - pubkey2.toString().should.equal(pubkey.toString()); - }); - - }); - - describe('#getReceiveKeypair', function() { - - it('should return a key', function() { - var key = stealth.getReceiveKeypair(senderKeypair.pubkey); - (key instanceof Keypair).should.equal(true); - }); - - it('should return a key with the same pubkey as getReceivePubkeyAsReceiver', function() { - var key = stealth.getReceiveKeypair(senderKeypair.pubkey); - var pubkey = stealth.getReceivePubkeyAsReceiver(senderKeypair.pubkey); - key.pubkey.toString().should.equal(pubkey.toString()); - }); - - it('should return private key with length 32 or less', function() { - var key = stealth.getReceiveKeypair(senderKeypair.pubkey); - key.privkey.bn.toBuffer().length.should.be.below(33); - }); - - }); - - describe('#isForMe', function() { - - it('should return true if it (the transaction or message) is for me', function() { - var pubkeyhash = new Buffer('3cb64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); - stealth.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(true); - }); - - it('should return false if it (the transaction or message) is not for me', function() { - var pubkeyhash = new Buffer('00b64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); - stealth.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(false); - }); - - }); - - describe('#toAddressBuffer', function() { - - it('should return this known address buffer', function() { - var buf = stealth.toAddressBuffer(); - base58check.encode(buf).should.equal(addressString); - }); - - }); - - describe('#toAddressString', function() { - - it('should return this known address string', function() { - stealth.toAddressString().should.equal(addressString); - }); - - }); - -}); diff --git a/test/stealthkey.js b/test/stealthkey.js new file mode 100644 index 0000000..7b2a871 --- /dev/null +++ b/test/stealthkey.js @@ -0,0 +1,190 @@ +var should = require('chai').should(); +var Stealthkey = require('../lib/expmt/stealthkey'); +var Keypair = require('../lib/keypair'); +var Privkey = require('../lib/privkey'); +var Pubkey = require('../lib/pubkey'); +var BN = require('../lib/bn'); +var Hash = require('../lib/hash'); +var base58check = require('../lib/base58check'); + +describe('Stealthkey', function() { + + var stealthkey = Stealthkey(); + stealthkey.payloadKeypair = Keypair(); + stealthkey.payloadKeypair.privkey = Privkey(); + stealthkey.payloadKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1'))); + stealthkey.payloadKeypair.privkey2pubkey(); + stealthkey.scanKeypair = Keypair(); + stealthkey.scanKeypair.privkey = Privkey(); + stealthkey.scanKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2'))); + stealthkey.scanKeypair.privkey2pubkey(); + + var senderKeypair = Keypair(); + senderKeypair.privkey = Privkey(); + senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); + senderKeypair.privkey2pubkey(); + + var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW'; + + it('should create a new stealthkey', function() { + var stealthkey = new Stealthkey(); + should.exist(stealthkey); + }); + + it('should create a new stealthkey without using "new"', function() { + var stealthkey = Stealthkey(); + should.exist(stealthkey); + }); + + describe('#set', function() { + + it('should set payload key', function() { + should.exist(Stealthkey().set({payloadKeypair: stealthkey.payloadKeypair}).payloadKeypair); + }); + + }); + + describe('#fromAddressBuffer', function() { + + it('should give a stealthkey address with the right pubkeys', function() { + var stealthkey2 = new Stealthkey(); + var buf = base58check.decode(addressString); + stealthkey2.fromAddressBuffer(buf); + stealthkey2.payloadKeypair.pubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); + stealthkey2.scanKeypair.pubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); + }); + + }); + + describe('#fromAddressString', function() { + + it('should give a stealthkey address with the right pubkeys', function() { + var stealthkey2 = new Stealthkey(); + stealthkey2.fromAddressString(addressString); + stealthkey2.payloadKeypair.pubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); + stealthkey2.scanKeypair.pubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); + }); + + }); + + describe('#fromRandom', function() { + + it('should create a new stealthkey from random', function() { + var stealthkey = Stealthkey().fromRandom(); + should.exist(stealthkey.payloadKeypair.privkey.bn.gt(0)); + should.exist(stealthkey.scanKeypair.privkey.bn.gt(0)); + }); + + }); + + describe('#getSharedKeypairAsReceiver', function() { + + it('should return a key', function() { + var key = stealthkey.getSharedKeypairAsReceiver(senderKeypair.pubkey); + (key instanceof Keypair).should.equal(true); + }); + + }); + + describe('#getSharedKeypairAsSender', function() { + + it('should return a key', function() { + var stealthkey2 = new Stealthkey(); + stealthkey2.payloadKeypair = new Keypair(); + stealthkey2.payloadKeypair.pubkey = stealthkey.payloadKeypair.pubkey; + stealthkey2.scanKeypair = new Keypair(); + stealthkey2.scanKeypair.pubkey = stealthkey.scanKeypair.pubkey; + var key = stealthkey2.getSharedKeypairAsSender(senderKeypair); + (key instanceof Keypair).should.equal(true); + }); + + it('should return the same key as getSharedKeypairAsReceiver', function() { + var stealthkey2 = new Stealthkey(); + stealthkey2.payloadKeypair = new Keypair(); + stealthkey2.payloadKeypair.pubkey = stealthkey.payloadKeypair.pubkey; + stealthkey2.scanKeypair = new Keypair(); + stealthkey2.scanKeypair.pubkey = stealthkey.scanKeypair.pubkey; + var key = stealthkey2.getSharedKeypairAsSender(senderKeypair); + + var key2 = stealthkey.getSharedKeypairAsReceiver(senderKeypair.pubkey); + key.toString().should.equal(key2.toString()); + }); + + }); + + describe('#getReceivePubkeyAsReceiver', function() { + + it('should return a pubkey', function() { + var pubkey = stealthkey.getReceivePubkeyAsReceiver(senderKeypair.pubkey); + (pubkey instanceof Pubkey).should.equal(true); + }); + + }); + + describe('#getReceivePubkeyAsSender', function() { + + it('should return a pubkey', function() { + var pubkey = stealthkey.getReceivePubkeyAsSender(senderKeypair); + (pubkey instanceof Pubkey).should.equal(true); + }); + + it('should return the same pubkey as getReceivePubkeyAsReceiver', function() { + var pubkey = stealthkey.getReceivePubkeyAsSender(senderKeypair); + var pubkey2 = stealthkey.getReceivePubkeyAsReceiver(senderKeypair.pubkey); + pubkey2.toString().should.equal(pubkey.toString()); + }); + + }); + + describe('#getReceiveKeypair', function() { + + it('should return a key', function() { + var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey); + (key instanceof Keypair).should.equal(true); + }); + + it('should return a key with the same pubkey as getReceivePubkeyAsReceiver', function() { + var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey); + var pubkey = stealthkey.getReceivePubkeyAsReceiver(senderKeypair.pubkey); + key.pubkey.toString().should.equal(pubkey.toString()); + }); + + it('should return private key with length 32 or less', function() { + var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey); + key.privkey.bn.toBuffer().length.should.be.below(33); + }); + + }); + + describe('#isForMe', function() { + + it('should return true if it (the transaction or message) is for me', function() { + var pubkeyhash = new Buffer('3cb64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); + stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(true); + }); + + it('should return false if it (the transaction or message) is not for me', function() { + var pubkeyhash = new Buffer('00b64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); + stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(false); + }); + + }); + + describe('#toAddressBuffer', function() { + + it('should return this known address buffer', function() { + var buf = stealthkey.toAddressBuffer(); + base58check.encode(buf).should.equal(addressString); + }); + + }); + + describe('#toAddressString', function() { + + it('should return this known address string', function() { + stealthkey.toAddressString().should.equal(addressString); + }); + + }); + +}); From 47b48bd5c78d09ef632700d85eba0dec53045adf Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 14:37:26 -0700 Subject: [PATCH 139/280] Fix Stealthkey exposure --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 0f9e7e5..ed56b6d 100644 --- a/index.js +++ b/index.js @@ -26,7 +26,7 @@ bitcore.expmt.AES = require('./lib/expmt/aes'); bitcore.expmt.AESCBC = require('./lib/expmt/aescbc'); bitcore.expmt.CBC = require('./lib/expmt/cbc'); bitcore.expmt.ECIES = require('./lib/expmt/ecies'); -bitcore.expmt.Stealth = require('./lib/expmt/stealth'); +bitcore.expmt.Stealthkey = require('./lib/expmt/stealthkey'); //dependencies, subject to change bitcore.deps = {}; From c59013eb46d49115e4efcdb8d385b617fff2bdf7 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 15:42:30 -0700 Subject: [PATCH 140/280] remove useless Address dependency from Keypair to get an address from a key, just do Address().fromPubkey(key.pubkey) --- lib/keypair.js | 5 ----- test/keypair.js | 14 -------------- 2 files changed, 19 deletions(-) diff --git a/lib/keypair.js b/lib/keypair.js index bf4ad94..e47e540 100644 --- a/lib/keypair.js +++ b/lib/keypair.js @@ -1,4 +1,3 @@ -var Address = require('../lib/address'); var Privkey = require('./privkey'); var Pubkey = require('./pubkey'); var BN = require('./bn'); @@ -41,10 +40,6 @@ Key.prototype.fromString = function(str) { } }; -Key.prototype.getAddress = function(networkstr) { - return Address().fromPubkey(this.pubkey, networkstr); -}; - Key.prototype.privkey2pubkey = function() { this.pubkey = Pubkey().fromPrivkey(this.privkey); }; diff --git a/test/keypair.js b/test/keypair.js index 2ff85b7..5e9dd01 100644 --- a/test/keypair.js +++ b/test/keypair.js @@ -1,7 +1,6 @@ var should = require('chai').should(); var bn = require('../lib/bn'); var point = require('../lib/point'); -var Address = require('../lib/address'); var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); var Keypair = require('../lib/keypair'); @@ -94,19 +93,6 @@ describe('Keypair', function() { }); - describe('#getAddress', function() { - - it('should return an address', function() { - var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff'; - var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'; - var key = new Keypair(); - key.privkey = new Privkey({bn: bn(new Buffer(privhex, 'hex'))}); - key.privkey2pubkey(); - key.getAddress().toString().should.equal((new Address()).fromPubkey(key.pubkey).toString()); - }); - - }); - describe("#privkey2pubkey", function() { it('should convert this known Privkey to known Pubkey', function() { From 03578e2ba54aa271a49557b3e980115d2d9220bd Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 19:24:51 -0700 Subject: [PATCH 141/280] Opcode ...code from bitcore --- lib/opcode.js | 168 +++++++++++++++++++++++++++++++++++++++++++++++++ test/opcode.js | 37 +++++++++++ 2 files changed, 205 insertions(+) create mode 100644 lib/opcode.js create mode 100644 test/opcode.js diff --git a/lib/opcode.js b/lib/opcode.js new file mode 100644 index 0000000..6091c7b --- /dev/null +++ b/lib/opcode.js @@ -0,0 +1,168 @@ +function Opcode(num) { + if (!(this instanceof Opcode)) + return new Opcode(num); + if (typeof num === 'number') { + this.num = num; + } else if (num) { + var obj = num; + this.set(obj); + } +} + +Opcode.prototype.set = function(obj) { + this.num = typeof obj.num !== 'undefined' ? obj.num : this.num; + return this; +}; + +Opcode.prototype.toString = function() { + return Opcode.reverseMap[this.num]; +}; + +Opcode.map = { + // push value + OP_FALSE: 0, + OP_0: 0, + OP_PUSHDATA1: 76, + OP_PUSHDATA2: 77, + OP_PUSHDATA4: 78, + OP_1NEGATE: 79, + OP_RESERVED: 80, + OP_TRUE: 81, + OP_1: 81, + OP_2: 82, + OP_3: 83, + OP_4: 84, + OP_5: 85, + OP_6: 86, + OP_7: 87, + OP_8: 88, + OP_9: 89, + OP_10: 90, + OP_11: 91, + OP_12: 92, + OP_13: 93, + OP_14: 94, + OP_15: 95, + OP_16: 96, + + // control + OP_NOP: 97, + OP_VER: 98, + OP_IF: 99, + OP_NOTIF: 100, + OP_VERIF: 101, + OP_VERNOTIF: 102, + OP_ELSE: 103, + OP_ENDIF: 104, + OP_VERIFY: 105, + OP_RETURN: 106, + + // stack ops + OP_TOALTSTACK: 107, + OP_FROMALTSTACK: 108, + OP_2DROP: 109, + OP_2DUP: 110, + OP_3DUP: 111, + OP_2OVER: 112, + OP_2ROT: 113, + OP_2SWAP: 114, + OP_IFDUP: 115, + OP_DEPTH: 116, + OP_DROP: 117, + OP_DUP: 118, + OP_NIP: 119, + OP_OVER: 120, + OP_PICK: 121, + OP_ROLL: 122, + OP_ROT: 123, + OP_SWAP: 124, + OP_TUCK: 125, + + // splice ops + OP_CAT: 126, + OP_SUBSTR: 127, + OP_LEFT: 128, + OP_RIGHT: 129, + OP_SIZE: 130, + + // bit logic + OP_INVERT: 131, + OP_AND: 132, + OP_OR: 133, + OP_XOR: 134, + OP_EQUAL: 135, + OP_EQUALVERIFY: 136, + OP_RESERVED1: 137, + OP_RESERVED2: 138, + + // numeric + OP_1ADD: 139, + OP_1SUB: 140, + OP_2MUL: 141, + OP_2DIV: 142, + OP_NEGATE: 143, + OP_ABS: 144, + OP_NOT: 145, + OP_0NOTEQUAL: 146, + + OP_ADD: 147, + OP_SUB: 148, + OP_MUL: 149, + OP_DIV: 150, + OP_MOD: 151, + OP_LSHIFT: 152, + OP_RSHIFT: 153, + + OP_BOOLAND: 154, + OP_BOOLOR: 155, + OP_NUMEQUAL: 156, + OP_NUMEQUALVERIFY: 157, + OP_NUMNOTEQUAL: 158, + OP_LESSTHAN: 159, + OP_GREATERTHAN: 160, + OP_LESSTHANOREQUAL: 161, + OP_GREATERTHANOREQUAL: 162, + OP_MIN: 163, + OP_MAX: 164, + + OP_WITHIN: 165, + + // crypto + OP_RIPEMD160: 166, + OP_SHA1: 167, + OP_SHA256: 168, + OP_HASH160: 169, + OP_HASH256: 170, + OP_CODESEPARATOR: 171, + OP_CHECKSIG: 172, + OP_CHECKSIGVERIFY: 173, + OP_CHECKMULTISIG: 174, + OP_CHECKMULTISIGVERIFY: 175, + + // expansion + OP_NOP1: 176, + OP_NOP2: 177, + OP_NOP3: 178, + OP_NOP4: 179, + OP_NOP5: 180, + OP_NOP6: 181, + OP_NOP7: 182, + OP_NOP8: 183, + OP_NOP9: 184, + OP_NOP10: 185, + + // template matching params + OP_PUBKEYHASH: 253, + OP_PUBKEY: 254, + OP_INVALIDOPCODE: 255 +}; + +Opcode.reverseMap = []; + +for (var k in Opcode.map) { + if (Opcode.map.hasOwnProperty(k)) { + Opcode.reverseMap[Opcode.map[k]] = k; + } +} + +module.exports = Opcode; diff --git a/test/opcode.js b/test/opcode.js new file mode 100644 index 0000000..843b6f4 --- /dev/null +++ b/test/opcode.js @@ -0,0 +1,37 @@ +var should = require('chai').should(); +var Opcode = require('../lib/opcode'); + +describe('Opcode', function() { + + it('should create a new Opcode', function() { + var opcode = new Opcode(5); + }); + + it('should convert to a string with this handy syntax', function() { + Opcode(0).toString().should.equal('OP_0'); + Opcode(97).toString().should.equal('OP_NOP'); + Opcode(96).toString().should.equal('OP_16'); + }); + + describe('@map', function() { + + it('should have a map containing 116 elements', function() { + var i = 0; + for (var key in Opcode.map) { + i++; + } + i.should.equal(116); + }); + + }); + + describe('@reverseMap', function() { + + it('should exist and have op 185', function() { + should.exist(Opcode.reverseMap); + Opcode.reverseMap[185].should.equal('OP_NOP10'); + }); + + }); + +}); From d8e1f0d9b3b70733235781ce03b18eea485c349e Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 29 Aug 2014 21:02:56 -0700 Subject: [PATCH 142/280] expose Opcode --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index ed56b6d..ea948d1 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,7 @@ bitcore.Hash = require('./lib/hash'); bitcore.KDF = require('./lib/kdf'); bitcore.Keypair = require('./lib/keypair'); bitcore.Message = require('./lib/message'); +bitcore.Opcode = require('./lib/opcode'); bitcore.Point = require('./lib/point'); bitcore.Privkey = require('./lib/privkey'); bitcore.Pubkey = require('./lib/pubkey'); From 004f8c29c8861e21b179a62edb5c417d2490e3de Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sat, 30 Aug 2014 11:58:33 -0700 Subject: [PATCH 143/280] npm shrinkwrap This will ensure that the versions of the dependencies of the dependencies remain the same on npm install, that way we can ensure bitcore works as intended for the end-user. Note that this does not ensure byte-for-byte compatibility. We may address that issue in the future. See: https://www.npmjs.org/doc/cli/npm-shrinkwrap.html --- npm-shrinkwrap.json | 94 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 npm-shrinkwrap.json diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json new file mode 100644 index 0000000..6bb724d --- /dev/null +++ b/npm-shrinkwrap.json @@ -0,0 +1,94 @@ +{ + "name": "bitcore2", + "version": "0.0.0", + "dependencies": { + "aes": { + "version": "0.1.0", + "from": "aes@=0.1.0", + "resolved": "https://registry.npmjs.org/aes/-/aes-0.1.0.tgz" + }, + "bn.js": { + "version": "0.13.3", + "from": "bn.js@=0.13.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.13.3.tgz" + }, + "bs58": { + "version": "1.2.1", + "from": "bs58@=1.2.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-1.2.1.tgz" + }, + "elliptic": { + "version": "0.15.7", + "from": "elliptic@=0.15.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-0.15.7.tgz", + "dependencies": { + "bn.js": { + "version": "0.11.7", + "from": "bn.js@^0.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.11.7.tgz" + }, + "hash.js": { + "version": "0.2.1", + "from": "hash.js@^0.2.0", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-0.2.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@^2.0.1" + }, + "uglify-js": { + "version": "2.4.15", + "from": "uglify-js@^2.4.13", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.15.tgz", + "dependencies": { + "async": { + "version": "0.2.10", + "from": "async@~0.2.6" + }, + "source-map": { + "version": "0.1.34", + "from": "source-map@0.1.34", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", + "dependencies": { + "amdefine": { + "version": "0.1.0", + "from": "amdefine@>=0.0.4" + } + } + }, + "optimist": { + "version": "0.3.7", + "from": "optimist@~0.3.5", + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "from": "wordwrap@~0.0.2" + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "from": "uglify-to-browserify@~1.0.0" + } + } + } + } + }, + "hash.js": { + "version": "0.3.1", + "from": "hash.js@=0.3.1", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-0.3.1.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@^2.0.1" + } + } + }, + "sha512": { + "version": "0.0.1", + "from": "sha512@=0.0.1", + "resolved": "https://registry.npmjs.org/sha512/-/sha512-0.0.1.tgz" + } + } +} From 6ffb6574ed88459355e3cba86710ffd9cf9ba3be Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 31 Aug 2014 20:38:19 -0700 Subject: [PATCH 144/280] comment about npm shrinkwrap --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b71586c..5711a72 100644 --- a/README.md +++ b/README.md @@ -45,3 +45,4 @@ Features over bitcore: * A proper point class * Better test coverage * Proper message signing and verification +* npm-shrinkwrap.json ensures npm install works as intended From a0150f82ef11659ae07f12bbcbef7ecb9e97509b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 31 Aug 2014 20:38:39 -0700 Subject: [PATCH 145/280] fromNumber, toNumber, fromString, toString ...like the rest of the library. --- lib/opcode.js | 18 ++++++++++++++++++ test/opcode.js | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/lib/opcode.js b/lib/opcode.js index 6091c7b..417213d 100644 --- a/lib/opcode.js +++ b/lib/opcode.js @@ -1,8 +1,12 @@ function Opcode(num) { if (!(this instanceof Opcode)) return new Opcode(num); + if (typeof num === 'number') { this.num = num; + } else if (typeof num === 'string') { + var str = num; + this.num = Opcode.map[str]; } else if (num) { var obj = num; this.set(obj); @@ -14,6 +18,20 @@ Opcode.prototype.set = function(obj) { return this; }; +Opcode.prototype.fromNumber = function(num) { + this.num = num; + return this; +}; + +Opcode.prototype.toNumber = function() { + return this.num; +}; + +Opcode.prototype.fromString = function(str) { + this.num = Opcode.map[str]; + return this; +}; + Opcode.prototype.toString = function() { return Opcode.reverseMap[this.num]; }; diff --git a/test/opcode.js b/test/opcode.js index 843b6f4..e531c9a 100644 --- a/test/opcode.js +++ b/test/opcode.js @@ -9,8 +9,46 @@ describe('Opcode', function() { it('should convert to a string with this handy syntax', function() { Opcode(0).toString().should.equal('OP_0'); - Opcode(97).toString().should.equal('OP_NOP'); Opcode(96).toString().should.equal('OP_16'); + Opcode(97).toString().should.equal('OP_NOP'); + }); + + it('should convert to a number with this handy syntax', function() { + Opcode('OP_0').toNumber().should.equal(0); + Opcode('OP_16').toNumber().should.equal(96); + Opcode('OP_NOP').toNumber().should.equal(97); + }); + + describe('#fromNumber', function() { + + it('should work for 0', function() { + Opcode().fromNumber(0).num.should.equal(0); + }); + + }); + + describe('#toNumber', function() { + + it('should work for 0', function() { + Opcode().fromNumber(0).toNumber().should.equal(0); + }); + + }); + + describe('#fromString', function() { + + it('should work for OP_0', function() { + Opcode().fromString('OP_0').num.should.equal(0); + }); + + }); + + describe('#toString', function() { + + it('should work for OP_0', function() { + Opcode().fromString('OP_0').toString().should.equal('OP_0'); + }); + }); describe('@map', function() { From 6a26813955c8d687581388b8eef325bd249610c2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 12:06:18 -0700 Subject: [PATCH 146/280] p2sh -> scripthash more appealing and memorable name --- lib/address.js | 12 ++++++------ lib/constants.js | 4 ++-- test/address.js | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/address.js b/lib/address.js index 61399ad..43912c0 100644 --- a/lib/address.js +++ b/lib/address.js @@ -32,15 +32,15 @@ Address.prototype.fromString = function(str) { if (version === constants['mainnet']['pubkeyhash']) { this.networkstr = 'mainnet'; this.typestr = 'pubkeyhash'; - } else if (version === constants['mainnet']['p2sh']) { + } else if (version === constants['mainnet']['scripthash']) { this.networkstr = 'mainnet'; - this.typestr = 'p2sh'; + this.typestr = 'scripthash'; } else if (version === constants['testnet']['pubkeyhash']) { this.networkstr = 'testnet'; this.typestr = 'pubkeyhash'; - } else if (version === constants['testnet']['p2sh']) { + } else if (version === constants['testnet']['scripthash']) { this.networkstr = 'testnet'; - this.typestr = 'p2sh'; + this.typestr = 'scripthash'; } else { this.networkstr = 'unknown'; this.typestr = 'unknown'; @@ -84,8 +84,8 @@ Address.prototype.validate = function() { throw new Error('hash must be a buffer of 20 bytes'); if (this.networkstr !== 'mainnet' && this.networkstr !== 'testnet') throw new Error('networkstr must be "mainnet" or "testnet"'); - if (this.typestr !== 'pubkeyhash' && this.typestr !== 'p2sh') - throw new Error('typestr must be "pubkeyhash" or "p2sh"'); + if (this.typestr !== 'pubkeyhash' && this.typestr !== 'scripthash') + throw new Error('typestr must be "pubkeyhash" or "scripthash"'); return this; }; diff --git a/lib/constants.js b/lib/constants.js index 6191987..c64494d 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,7 +1,7 @@ exports.mainnet = { pubkeyhash: 0x00, privkey: 0x80, - p2sh: 0x05, + scripthash: 0x05, bip32pubkey: 0x0488b21e, bip32privkey: 0x0488ade4, }; @@ -9,7 +9,7 @@ exports.mainnet = { exports.testnet = { pubkeyhash: 0x6f, privkey: 0xef, - p2sh: 0xc4, + scripthash: 0xc4, bip32pubkey: 0x043587cf, bip32privkey: 0x04358394, }; diff --git a/test/address.js b/test/address.js index e18cf49..44dde17 100644 --- a/test/address.js +++ b/test/address.js @@ -61,20 +61,20 @@ describe('Address', function() { address.toString().should.equal('mm1X5M2QWyHVjn7txrF7mmtZDpjCXzoa98'); }); - it('should derive from this known address string mainnet p2sh', function() { + it('should derive from this known address string mainnet scripthash', function() { var address = new Address(); address.fromString(str); address.networkstr = 'mainnet'; - address.typestr = 'p2sh'; + address.typestr = 'scripthash'; address.fromString(address.toString()); address.toString().should.equal('37BahqRsFrAd3qLiNNwLNV3AWMRD7itxTo'); }); - it('should derive from this known address string testnet p2sh', function() { + it('should derive from this known address string testnet scripthash', function() { var address = new Address(); address.fromString(str); address.networkstr = 'testnet'; - address.typestr = 'p2sh'; + address.typestr = 'scripthash'; address.fromString(address.toString()); address.toString().should.equal('2MxjnmaMtsJfyFcyG3WZCzS2RihdNuWqeX4'); }); @@ -148,7 +148,7 @@ describe('Address', function() { address.typestr = 'unknown'; (function() { address.validate(); - }).should.throw('typestr must be "pubkeyhash" or "p2sh"'); + }).should.throw('typestr must be "pubkeyhash" or "scripthash"'); }); }); From a481a0225e91b59033e51cb213c9f1bbf061e9db Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 14:59:42 -0700 Subject: [PATCH 147/280] rename bundle from privsec to bitcore ...since this library is indeed likely to become the new bitcore --- .gitignore | 2 +- browser/build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index d3c99a0..77a88c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ *.swp coverage node_modules -browser/privsec.js +browser/bitcore.js browser/tests.js diff --git a/browser/build b/browser/build index 56ace55..fca4b11 100755 --- a/browser/build +++ b/browser/build @@ -1,4 +1,4 @@ #!/bin/bash -browserify index.js -o browser/privsec.js +browserify index.js -o browser/bitcore.js ls test/*.js | xargs browserify -o browser/tests.js From 15801773e387756de3e044a8ac8f6eb35dcba183 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 15:45:03 -0700 Subject: [PATCH 148/280] the beginnings of script --- index.js | 2 +- lib/script.js | 24 ++++++++++++++++++++++++ test/test.script.js | 11 +++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 lib/script.js create mode 100644 test/test.script.js diff --git a/index.js b/index.js index ea948d1..e47ef15 100644 --- a/index.js +++ b/index.js @@ -19,6 +19,7 @@ bitcore.Point = require('./lib/point'); bitcore.Privkey = require('./lib/privkey'); bitcore.Pubkey = require('./lib/pubkey'); bitcore.Random = require('./lib/random'); +bitcore.Script = require('./lib/script'); bitcore.Signature = require('./lib/signature'); //experimental, nonstandard, or unstable features @@ -39,7 +40,6 @@ bitcore.deps.elliptic = require('elliptic'); bitcore.deps.hashjs = require('hash.js'); bitcore.deps.sha512 = require('sha512'); -//bitcore.script = require('lib/script'); //bitcore.scriptexec = require('lib/scriptexec'); //bitcore.tx = require('lib/tx'); //bitcore.txpartial = require('lib/txpartial'); diff --git a/lib/script.js b/lib/script.js new file mode 100644 index 0000000..0b19ffc --- /dev/null +++ b/lib/script.js @@ -0,0 +1,24 @@ +var BufferReader = require('./bufferreader'); +var Opcode = require('./opcode'); + +var Script = function Script(buf) { + if (!(this instanceof Script)) + return new Script(buf); + + this.chunks = []; + + if (Buffer.isBuffer(buf)) { + this.parse(buf); + } + else if (typeof buf !== 'undefined') { + var obj = buf; + this.set(obj); + } +}; + +Script.prototype.set = function(obj) { + this.chunks = obj.chunks || this.chunks; + return this; +}; + +module.exports = Script; diff --git a/test/test.script.js b/test/test.script.js new file mode 100644 index 0000000..32a0ec9 --- /dev/null +++ b/test/test.script.js @@ -0,0 +1,11 @@ +var Script = require('../lib/script'); +var should = require('chai').should(); +var Opcode = require('../lib/opcode'); + +describe('Script', function() { + + it('should make a new script', function() { + var script = new Script(); + }); + +}); From 572582938f0488333f914bb4a5798cff5c5de6c5 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 15:53:26 -0700 Subject: [PATCH 149/280] more convenient bufferreader --- lib/bufferreader.js | 11 ++++++++--- test/bufferreader.js | 9 +++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index cf6b891..b8e2e72 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -1,10 +1,15 @@ var BN = require('./bn'); -var BufferReader = function BufferReader(obj) { +var BufferReader = function BufferReader(buf) { if (!(this instanceof BufferReader)) - return new BufferReader(obj); - if (obj) + return new BufferReader(buf); + if (Buffer.isBuffer(buf)) { + this.set({buf: buf}); + } + else if (buf) { + var obj = buf; this.set(obj); + } }; BufferReader.prototype.set = function(obj) { diff --git a/test/bufferreader.js b/test/bufferreader.js index d4ffdb3..735d960 100644 --- a/test/bufferreader.js +++ b/test/bufferreader.js @@ -7,6 +7,15 @@ describe('BufferReader', function() { it('should make a new BufferReader', function() { var br = new BufferReader(); should.exist(br); + br = BufferReader(); + should.exist(br); + }); + + it('should create a new bufferreader with a buffer', function() { + var buf = new Buffer(0); + var br = new BufferReader(buf); + should.exist(br); + Buffer.isBuffer(br.buf).should.equal(true); }); describe('#set', function() { From 8b3ad7ac85cb7beac682d47f11f4af33fbec54ba Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 16:40:31 -0700 Subject: [PATCH 150/280] read a buffer, like slicing while iterating pos --- lib/bufferreader.js | 6 ++++++ test/bufferreader.js | 14 ++++++++++++++ test/{test.script.js => script.js} | 0 3 files changed, 20 insertions(+) rename test/{test.script.js => script.js} (100%) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index b8e2e72..f33ec90 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -22,6 +22,12 @@ BufferReader.prototype.eof = function eof() { return this.pos >= this.buf.length; }; +BufferReader.prototype.buffer = function(len) { + var buf = this.buf.slice(this.pos, this.pos + len); + this.pos = this.pos + len; + return buf; +}; + BufferReader.prototype.read = function() { var buf = this.buf.slice(this.pos); this.pos = this.buf.length; diff --git a/test/bufferreader.js b/test/bufferreader.js index 735d960..3bbce17 100644 --- a/test/bufferreader.js +++ b/test/bufferreader.js @@ -35,6 +35,20 @@ describe('BufferReader', function() { }); + describe('#buffer', function() { + + it('should return a buffer of this length', function() { + var buf = new Buffer(10); + buf.fill(0); + var br = new BufferReader(buf); + var buf2 = br.buffer(2); + buf2.length.should.equal(2); + br.eof().should.equal(false); + br.pos.should.equal(2); + }); + + }); + describe('read', function() { it('should return the same buffer', function() { diff --git a/test/test.script.js b/test/script.js similarity index 100% rename from test/test.script.js rename to test/script.js From 697b3e50094bcecb7cba5fb8e21cf8654d37a579 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 16:41:07 -0700 Subject: [PATCH 151/280] Script().fromBuffer(buf) ...code heavily inspired/copied from bitcore --- lib/script.js | 37 ++++++++++++++++++++++++++++++++- test/script.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/lib/script.js b/lib/script.js index 0b19ffc..b8b2ce1 100644 --- a/lib/script.js +++ b/lib/script.js @@ -8,7 +8,11 @@ var Script = function Script(buf) { this.chunks = []; if (Buffer.isBuffer(buf)) { - this.parse(buf); + this.fromBuffer(buf); + } + else if (typeof buf === 'string') { + var str = buf; + this.fromString(str); } else if (typeof buf !== 'undefined') { var obj = buf; @@ -21,4 +25,35 @@ Script.prototype.set = function(obj) { return this; }; +Script.prototype.fromBuffer = function(buf) { + this.chunks = []; + + var br = new BufferReader(buf); + while (!br.eof()) { + var opcode = br.readUInt8(); + + var len, chunk; + if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) { + // Read some bytes of data, opcode value is the length of data + this.chunks.push(br.buffer(opcode)); + } else if (opcode === Opcode.map.OP_PUSHDATA1) { + len = br.readUInt8(); + chunk = br.buffer(len); + this.chunks.push(chunk); + } else if (opcode === Opcode.map.OP_PUSHDATA2) { + len = br.readUInt16LE(); + chunk = br.buffer(len); + this.chunks.push(chunk); + } else if (opcode === Opcode.map.OP_PUSHDATA4) { + len = br.readUInt32LE(); + chunk = br.buffer(len); + this.chunks.push(chunk); + } else { + this.chunks.push(opcode); + } + } + + return this; +}; + module.exports = Script; diff --git a/test/script.js b/test/script.js index 32a0ec9..eef22aa 100644 --- a/test/script.js +++ b/test/script.js @@ -1,6 +1,8 @@ var Script = require('../lib/script'); var should = require('chai').should(); var Opcode = require('../lib/opcode'); +var BufferReader = require('../lib/bufferreader'); +var BufferWriter = require('../lib/bufferwriter'); describe('Script', function() { @@ -8,4 +10,58 @@ describe('Script', function() { var script = new Script(); }); + describe('#fromBuffer', function() { + + it('should parse this buffer containing an OP code', function() { + var buf = new Buffer(1); + buf[0] = Opcode('OP_0').toNumber(); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].should.equal(buf[0]); + }); + + it('should parse this buffer containing another OP code', function() { + var buf = new Buffer(1); + buf[0] = Opcode('OP_CHECKMULTISIG').toNumber(); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].should.equal(buf[0]); + }); + + it('should parse this buffer containing three bytes of data', function() { + var buf = new Buffer([3, 1, 2, 3]); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].toString('hex').should.equal('010203'); + }); + + it('should parse this buffer containing OP_PUSHDATA1 and three bytes of data', function() { + var buf = new Buffer([0, 0, 1, 2, 3]); + buf[0] = Opcode('OP_PUSHDATA1').toNumber(); + buf.writeUInt8(3, 1); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].toString('hex').should.equal('010203'); + }); + + it('should parse this buffer containing OP_PUSHDATA2 and three bytes of data', function() { + var buf = new Buffer([0, 0, 0, 1, 2, 3]); + buf[0] = Opcode('OP_PUSHDATA2').toNumber(); + buf.writeUInt16LE(3, 1); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].toString('hex').should.equal('010203'); + }); + + it('should parse this buffer containing OP_PUSHDATA4 and three bytes of data', function() { + var buf = new Buffer([0, 0, 0, 0, 0, 1, 2, 3]); + buf[0] = Opcode('OP_PUSHDATA4').toNumber(); + buf.writeUInt16LE(3, 1); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].toString('hex').should.equal('010203'); + }); + + }); + }); From e3aa93614e81dd5ef4b915210e6062b8003b6bff Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 16:44:27 -0700 Subject: [PATCH 152/280] one more test to make sure things are working --- test/script.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/script.js b/test/script.js index eef22aa..ceb418b 100644 --- a/test/script.js +++ b/test/script.js @@ -62,6 +62,19 @@ describe('Script', function() { script.chunks[0].toString('hex').should.equal('010203'); }); + it('should parse this buffer an OP code, data, and another OP code', function() { + var buf = new Buffer([0, 0, 0, 0, 0, 0, 1, 2, 3, 0]); + buf[0] = Opcode('OP_0').toNumber(); + buf[1] = Opcode('OP_PUSHDATA4').toNumber(); + buf.writeUInt16LE(3, 2); + buf[buf.length - 1] = Opcode('OP_0').toNumber(); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(3); + script.chunks[0].should.equal(buf[0]); + script.chunks[1].toString('hex').should.equal('010203'); + script.chunks[2].should.equal(buf[buf.length - 1]); + }); + }); }); From 378dc923ef29fa50d0344bfcdbb5bb77fd5d9055 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 17:27:39 -0700 Subject: [PATCH 153/280] preserve claimed length and op code When parsing OP_PUSHDATAX commands, the the length of data might not require the size integer of OP_PUSHDATAX. For instance, you might write 1 byte, and yet use OP_PUSHDATA4. We need to record which OP_PUSHDATAX was used so that when we write the buffer back out, we can write the same one. Also, the claimed length may be different. For instance, we may OP_PUSHDATA of length 100 to the stack, but there may only be 50 bytes left in the script. In that case, buf.length and chunk.len will be different. I'm not sure if that would be considered a valid script, but in any case, for script analysis, we need both values. --- lib/script.js | 59 +++++++++++++++++++++++++++++++++++++------------- test/script.js | 10 ++++----- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/lib/script.js b/lib/script.js index b8b2ce1..81a4cba 100644 --- a/lib/script.js +++ b/lib/script.js @@ -30,30 +30,59 @@ Script.prototype.fromBuffer = function(buf) { var br = new BufferReader(buf); while (!br.eof()) { - var opcode = br.readUInt8(); + var opcodenum = br.readUInt8(); - var len, chunk; - if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) { - // Read some bytes of data, opcode value is the length of data - this.chunks.push(br.buffer(opcode)); - } else if (opcode === Opcode.map.OP_PUSHDATA1) { + var len, buf; + if (opcodenum > 0 && opcodenum < Opcode.map.OP_PUSHDATA1) { + len = opcodenum; + this.chunks.push({ + buf: br.buffer(len), + len: len, + opcodenum: opcodenum + }); + } else if (opcodenum === Opcode.map.OP_PUSHDATA1) { len = br.readUInt8(); - chunk = br.buffer(len); - this.chunks.push(chunk); - } else if (opcode === Opcode.map.OP_PUSHDATA2) { + var buf = br.buffer(len); + this.chunks.push({ + buf: buf, + len: len, + opcodenum: opcodenum + }); + } else if (opcodenum === Opcode.map.OP_PUSHDATA2) { len = br.readUInt16LE(); - chunk = br.buffer(len); - this.chunks.push(chunk); - } else if (opcode === Opcode.map.OP_PUSHDATA4) { + buf = br.buffer(len); + this.chunks.push({ + buf: buf, + len: len, + opcodenum: opcodenum + }); + } else if (opcodenum === Opcode.map.OP_PUSHDATA4) { len = br.readUInt32LE(); - chunk = br.buffer(len); - this.chunks.push(chunk); + buf = br.buffer(len); + this.chunks.push({ + buf: buf, + len: len, + opcodenum: opcodenum + }); } else { - this.chunks.push(opcode); + this.chunks.push(opcodenum); } } return this; }; +Script.prototype.toBuffer = function() { + var bw = new BufferWriter(); + + for (var key in this.chunks) { + if (this.chunks.hasOwnProperty(key)) { + var chunk = this.chunks[key]; + if (typeof chunk === 'number') { + + } + } + } +}; + module.exports = Script; diff --git a/test/script.js b/test/script.js index ceb418b..a034675 100644 --- a/test/script.js +++ b/test/script.js @@ -32,7 +32,7 @@ describe('Script', function() { var buf = new Buffer([3, 1, 2, 3]); var script = Script().fromBuffer(buf); script.chunks.length.should.equal(1); - script.chunks[0].toString('hex').should.equal('010203'); + script.chunks[0].buf.toString('hex').should.equal('010203'); }); it('should parse this buffer containing OP_PUSHDATA1 and three bytes of data', function() { @@ -41,7 +41,7 @@ describe('Script', function() { buf.writeUInt8(3, 1); var script = Script().fromBuffer(buf); script.chunks.length.should.equal(1); - script.chunks[0].toString('hex').should.equal('010203'); + script.chunks[0].buf.toString('hex').should.equal('010203'); }); it('should parse this buffer containing OP_PUSHDATA2 and three bytes of data', function() { @@ -50,7 +50,7 @@ describe('Script', function() { buf.writeUInt16LE(3, 1); var script = Script().fromBuffer(buf); script.chunks.length.should.equal(1); - script.chunks[0].toString('hex').should.equal('010203'); + script.chunks[0].buf.toString('hex').should.equal('010203'); }); it('should parse this buffer containing OP_PUSHDATA4 and three bytes of data', function() { @@ -59,7 +59,7 @@ describe('Script', function() { buf.writeUInt16LE(3, 1); var script = Script().fromBuffer(buf); script.chunks.length.should.equal(1); - script.chunks[0].toString('hex').should.equal('010203'); + script.chunks[0].buf.toString('hex').should.equal('010203'); }); it('should parse this buffer an OP code, data, and another OP code', function() { @@ -71,7 +71,7 @@ describe('Script', function() { var script = Script().fromBuffer(buf); script.chunks.length.should.equal(3); script.chunks[0].should.equal(buf[0]); - script.chunks[1].toString('hex').should.equal('010203'); + script.chunks[1].buf.toString('hex').should.equal('010203'); script.chunks[2].should.equal(buf[buf.length - 1]); }); From e6af57f4faf0c98ddf37418e49b1caea5d762a96 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 18:01:17 -0700 Subject: [PATCH 154/280] script.toBuffer(); --- README.md | 1 + lib/script.js | 30 ++++++++++++++++---- test/script.js | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5711a72..2e0686e 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,4 @@ Features over bitcore: * Better test coverage * Proper message signing and verification * npm-shrinkwrap.json ensures npm install works as intended +* byte-for-byte reading/writing scripts diff --git a/lib/script.js b/lib/script.js index 81a4cba..4b56b37 100644 --- a/lib/script.js +++ b/lib/script.js @@ -1,4 +1,5 @@ var BufferReader = require('./bufferreader'); +var BufferWriter = require('./bufferwriter'); var Opcode = require('./opcode'); var Script = function Script(buf) { @@ -75,14 +76,33 @@ Script.prototype.fromBuffer = function(buf) { Script.prototype.toBuffer = function() { var bw = new BufferWriter(); - for (var key in this.chunks) { - if (this.chunks.hasOwnProperty(key)) { - var chunk = this.chunks[key]; - if (typeof chunk === 'number') { - + for (var i = 0; i < this.chunks.length; i++) { + var chunk = this.chunks[i]; + if (typeof chunk === 'number') { + var opcodenum = chunk; + bw.writeUInt8(opcodenum); + } else { + var opcodenum = chunk.opcodenum; + bw.writeUInt8(chunk.opcodenum); + if (opcodenum < Opcode.map.OP_PUSHDATA1) { + bw.write(chunk.buf); + } + else if (opcodenum === Opcode.map.OP_PUSHDATA1) { + bw.writeUInt8(chunk.len); + bw.write(chunk.buf); + } + else if (opcodenum === Opcode.map.OP_PUSHDATA2) { + bw.writeUInt16LE(chunk.len); + bw.write(chunk.buf); + } + else if (opcodenum === Opcode.map.OP_PUSHDATA4) { + bw.writeUInt32LE(chunk.len); + bw.write(chunk.buf); } } } + + return bw.concat(); }; module.exports = Script; diff --git a/test/script.js b/test/script.js index a034675..6f19ba7 100644 --- a/test/script.js +++ b/test/script.js @@ -77,4 +77,78 @@ describe('Script', function() { }); + describe('#toBuffer', function() { + + it('should output this buffer containing an OP code', function() { + var buf = new Buffer(1); + buf[0] = Opcode('OP_0').toNumber(); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].should.equal(buf[0]); + script.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + it('should output this buffer containing another OP code', function() { + var buf = new Buffer(1); + buf[0] = Opcode('OP_CHECKMULTISIG').toNumber(); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].should.equal(buf[0]); + script.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + it('should output this buffer containing three bytes of data', function() { + var buf = new Buffer([3, 1, 2, 3]); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].buf.toString('hex').should.equal('010203'); + script.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + it('should output this buffer containing OP_PUSHDATA1 and three bytes of data', function() { + var buf = new Buffer([0, 0, 1, 2, 3]); + buf[0] = Opcode('OP_PUSHDATA1').toNumber(); + buf.writeUInt8(3, 1); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].buf.toString('hex').should.equal('010203'); + script.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + it('should output this buffer containing OP_PUSHDATA2 and three bytes of data', function() { + var buf = new Buffer([0, 0, 0, 1, 2, 3]); + buf[0] = Opcode('OP_PUSHDATA2').toNumber(); + buf.writeUInt16LE(3, 1); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].buf.toString('hex').should.equal('010203'); + script.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + it('should output this buffer containing OP_PUSHDATA4 and three bytes of data', function() { + var buf = new Buffer([0, 0, 0, 0, 0, 1, 2, 3]); + buf[0] = Opcode('OP_PUSHDATA4').toNumber(); + buf.writeUInt16LE(3, 1); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(1); + script.chunks[0].buf.toString('hex').should.equal('010203'); + script.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + it('should output this buffer an OP code, data, and another OP code', function() { + var buf = new Buffer([0, 0, 0, 0, 0, 0, 1, 2, 3, 0]); + buf[0] = Opcode('OP_0').toNumber(); + buf[1] = Opcode('OP_PUSHDATA4').toNumber(); + buf.writeUInt16LE(3, 2); + buf[buf.length - 1] = Opcode('OP_0').toNumber(); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(3); + script.chunks[0].should.equal(buf[0]); + script.chunks[1].buf.toString('hex').should.equal('010203'); + script.chunks[2].should.equal(buf[buf.length - 1]); + script.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + }); + }); From 6375941ef818977570b7a777bc99e8366380cd09 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 18:31:02 -0700 Subject: [PATCH 155/280] script.toString() --- lib/script.js | 19 +++++++++++++++++++ test/script.js | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/lib/script.js b/lib/script.js index 4b56b37..16f366a 100644 --- a/lib/script.js +++ b/lib/script.js @@ -105,4 +105,23 @@ Script.prototype.toBuffer = function() { return bw.concat(); }; +Script.prototype.toString = function() { + var str = ""; + + for (var i = 0; i < this.chunks.length; i++) { + var chunk = this.chunks[i]; + if (typeof chunk === 'number') { + var opcodenum = chunk; + str = str + Opcode(opcodenum).toString() + " "; + } else { + var opcodenum = chunk.opcodenum; + str = str + Opcode(opcodenum).toString() + " " ; + str = str + chunk.len + " " ; + str = str + "0x" + chunk.buf.toString('hex') + " "; + } + } + + return str.substr(0, str.length - 1); +}; + module.exports = Script; diff --git a/test/script.js b/test/script.js index 6f19ba7..50110c8 100644 --- a/test/script.js +++ b/test/script.js @@ -151,4 +151,22 @@ describe('Script', function() { }); + describe('#toString', function() { + + it('should output this buffer an OP code, data, and another OP code', function() { + var buf = new Buffer([0, 0, 0, 0, 0, 0, 1, 2, 3, 0]); + buf[0] = Opcode('OP_0').toNumber(); + buf[1] = Opcode('OP_PUSHDATA4').toNumber(); + buf.writeUInt16LE(3, 2); + buf[buf.length - 1] = Opcode('OP_0').toNumber(); + var script = Script().fromBuffer(buf); + script.chunks.length.should.equal(3); + script.chunks[0].should.equal(buf[0]); + script.chunks[1].buf.toString('hex').should.equal('010203'); + script.chunks[2].should.equal(buf[buf.length - 1]); + script.toString().toString('hex').should.equal('OP_0 OP_PUSHDATA4 3 0x010203 OP_0'); + }); + + }); + }); From 3b2b725070ee8beca1356116f73f59b2592b4414 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 19:42:20 -0700 Subject: [PATCH 156/280] Script().fromString(str) ...the format of fromString and toString are deliberately not compatible with bitcoind. The format here is supposed to be both human-readable, and byte-for-byte isomorphic to the binary representation. In the future we will need to add support for bitcoind-like strings, both for the test data (e.g., script_invalid.json) or for the bitcoind console style. --- lib/script.js | 41 ++++++++++++++++++++++++++++++++++++++++- test/script.js | 11 +++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/script.js b/lib/script.js index 16f366a..4b7eb29 100644 --- a/lib/script.js +++ b/lib/script.js @@ -105,6 +105,44 @@ Script.prototype.toBuffer = function() { return bw.concat(); }; +Script.prototype.fromString = function(str) { + this.chunks = []; + + var tokens = str.split(' '); + var i = 0; + while (i < tokens.length) { + var token = tokens[i]; + var opcode = Opcode(token); + var opcodenum = opcode.toNumber(); + + if (typeof opcodenum === 'undefined') { + opcodenum = parseInt(token); + if (opcodenum > 0 && opcodenum < Opcode.map.OP_PUSHDATA1) { + this.chunks.push({ + buf: new Buffer(tokens[i + 1].slice(2), 'hex'), + len: opcodenum, + opcodenum: opcodenum + }); + i = i + 2; + } + else { + throw new Error('Invalid script'); + } + } else if (opcodenum === Opcode.map.OP_PUSHDATA1 || opcodenum === Opcode.map.OP_PUSHDATA2 || opcodenum === Opcode.map.OP_PUSHDATA4) { + this.chunks.push({ + buf: new Buffer(tokens[i + 2].slice(2), 'hex'), + len: parseInt(tokens[i + 1]), + opcodenum: opcodenum + }); + i = i + 3; + } else { + this.chunks.push(opcodenum); + i = i + 1; + } + } + return this; +}; + Script.prototype.toString = function() { var str = ""; @@ -115,7 +153,8 @@ Script.prototype.toString = function() { str = str + Opcode(opcodenum).toString() + " "; } else { var opcodenum = chunk.opcodenum; - str = str + Opcode(opcodenum).toString() + " " ; + if (opcodenum === Opcode.map.OP_PUSHDATA1 || opcodenum === Opcode.map.OP_PUSHDATA2 || opcodenum === Opcode.map.OP_PUSHDATA4) + str = str + Opcode(opcodenum).toString() + " " ; str = str + chunk.len + " " ; str = str + "0x" + chunk.buf.toString('hex') + " "; } diff --git a/test/script.js b/test/script.js index 50110c8..a8af6ca 100644 --- a/test/script.js +++ b/test/script.js @@ -151,6 +151,17 @@ describe('Script', function() { }); + describe('#fromString', function() { + + it('should parse these known scripts', function() { + Script().fromString('OP_0 OP_PUSHDATA4 3 0x010203 OP_0').toString().should.equal('OP_0 OP_PUSHDATA4 3 0x010203 OP_0'); + Script().fromString('OP_0 OP_PUSHDATA2 3 0x010203 OP_0').toString().should.equal('OP_0 OP_PUSHDATA2 3 0x010203 OP_0'); + Script().fromString('OP_0 OP_PUSHDATA1 3 0x010203 OP_0').toString().should.equal('OP_0 OP_PUSHDATA1 3 0x010203 OP_0'); + Script().fromString('OP_0 3 0x010203 OP_0').toString().should.equal('OP_0 3 0x010203 OP_0'); + }); + + }); + describe('#toString', function() { it('should output this buffer an OP code, data, and another OP code', function() { From eaaf8aade343ff321ba382a315b41bf1cf3a54d7 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 20:00:54 -0700 Subject: [PATCH 157/280] throw error if there is no string representation --- lib/opcode.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/opcode.js b/lib/opcode.js index 417213d..04c3ccf 100644 --- a/lib/opcode.js +++ b/lib/opcode.js @@ -33,7 +33,10 @@ Opcode.prototype.fromString = function(str) { }; Opcode.prototype.toString = function() { - return Opcode.reverseMap[this.num]; + var str = Opcode.reverseMap[this.num]; + if (typeof str === 'undefined') + throw new Error('Opcode does not have a string representation'); + return str; }; Opcode.map = { From 8a3d71b5969889797c15ce16dbb5a210928a2264 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 20:03:22 -0700 Subject: [PATCH 158/280] throw error for invalid strings --- lib/opcode.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/opcode.js b/lib/opcode.js index 04c3ccf..c53bdc8 100644 --- a/lib/opcode.js +++ b/lib/opcode.js @@ -28,7 +28,10 @@ Opcode.prototype.toNumber = function() { }; Opcode.prototype.fromString = function(str) { - this.num = Opcode.map[str]; + var num = Opcode.map[str]; + if (typeof num === 'undefined') + throw new Error('Invalid opcodestr'); + this.num = num; return this; }; From ef3a89f254f1d00b5e43006e44049cc3b9adae8f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 21:08:16 -0700 Subject: [PATCH 159/280] add Signature(r, s) convenience --- lib/signature.js | 12 ++++++++++-- test/signature.js | 19 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/signature.js b/lib/signature.js index d6d3739..31ef75a 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -2,11 +2,19 @@ var BN = require('./bn'); var Point = require('./point'); var Pubkey = require('./pubkey'); -var Signature = function Signature(obj) { +var Signature = function Signature(r, s) { if (!(this instanceof Signature)) return new Signature(obj); - if (obj) + if (r instanceof BN) { + this.set({ + r: r, + s: s + }); + } + else if (r) { + var obj = r; this.set(obj); + } }; Signature.prototype.set = function(obj) { diff --git a/test/signature.js b/test/signature.js index 1d6972b..2e70165 100644 --- a/test/signature.js +++ b/test/signature.js @@ -1,4 +1,4 @@ -var bn = require('../lib/bn'); +var BN = require('../lib/bn'); var should = require('chai').should(); var Signature = require('../lib/signature'); @@ -9,6 +9,15 @@ describe('Signature', function() { should.exist(sig); }); + it('should work with conveniently setting r, s', function() { + var r = BN(); + var s = BN(); + var sig = new Signature(r, s); + should.exist(sig); + sig.r.toString().should.equal(r.toString()); + sig.s.toString().should.equal(s.toString()); + }); + describe('#set', function() { it('should set compressed', function() { @@ -116,8 +125,8 @@ describe('Signature', function() { describe('#toDER', function() { it('should convert these known r and s values into a known signature', function() { - var r = bn('63173831029936981022572627018246571655303050627048489594159321588908385378810'); - var s = bn('4331694221846364448463828256391194279133231453999942381442030409253074198130'); + var r = BN('63173831029936981022572627018246571655303050627048489594159321588908385378810'); + var s = BN('4331694221846364448463828256391194279133231453999942381442030409253074198130'); var sig = new Signature({r: r, s: s}); var der = sig.toDER(r, s); der.toString('hex').should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); @@ -128,8 +137,8 @@ describe('Signature', function() { describe('#toString', function() { it('should convert this signature in to hex DER', function() { - var r = bn('63173831029936981022572627018246571655303050627048489594159321588908385378810'); - var s = bn('4331694221846364448463828256391194279133231453999942381442030409253074198130'); + var r = BN('63173831029936981022572627018246571655303050627048489594159321588908385378810'); + var s = BN('4331694221846364448463828256391194279133231453999942381442030409253074198130'); var sig = new Signature({r: r, s: s}); var hex = sig.toString(); hex.should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'); From 6f56c8d1fc93d0ef5a704f38987cbf4f26393db8 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 21:13:44 -0700 Subject: [PATCH 160/280] convenience: new Privkey(bn) --- lib/privkey.js | 8 ++++++-- test/privkey.js | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/privkey.js b/lib/privkey.js index e102bf3..c0fc0d6 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -4,11 +4,15 @@ var constants = require('./constants'); var base58check = require('./base58check'); var Random = require('./random'); -var Privkey = function Privkey(obj) { +var Privkey = function Privkey(bn) { if (!(this instanceof Privkey)) return new Privkey(obj); - if (obj) + if (bn instanceof BN) + this.bn = bn; + else if (bn) { + var obj = bn; this.set(obj); + } }; Privkey.prototype.set = function(obj) { diff --git a/test/privkey.js b/test/privkey.js index 24f9568..f9cb48e 100644 --- a/test/privkey.js +++ b/test/privkey.js @@ -17,6 +17,12 @@ describe('Privkey', function() { should.exist(privkey); }); + it('should create a 0 private key with this convenience method', function() { + var bn = BN(0); + var privkey = new Privkey(bn); + privkey.bn.toString().should.equal(bn.toString()); + }); + it('should create a mainnet private key', function() { var privkey = new Privkey({bn: BN.fromBuffer(buf), networkstr: 'mainnet', compressed: true}); privkey.toString().should.equal(encmainnet); From a768755764e40c72d0b880cbfe62cb32cfd37fbe Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 1 Sep 2014 21:16:10 -0700 Subject: [PATCH 161/280] convenience: new Pubkey(point) --- lib/pubkey.js | 8 ++++++-- test/pubkey.js | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/pubkey.js b/lib/pubkey.js index 4642aa8..522b758 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -2,11 +2,15 @@ var Point = require('./point'); var bn = require('./bn'); var privkey = require('./privkey'); -var Pubkey = function Pubkey(obj) { +var Pubkey = function Pubkey(point) { if (!(this instanceof Pubkey)) return new Pubkey(obj); - if (obj) + if (point instanceof Point) + this.point = point; + else if (point) { + var obj = point; this.set(obj); + } }; Pubkey.prototype.set = function(obj) { diff --git a/test/pubkey.js b/test/pubkey.js index efee3b6..03e7534 100644 --- a/test/pubkey.js +++ b/test/pubkey.js @@ -17,6 +17,13 @@ describe('Pubkey', function() { should.exist(pk.point); }); + it('should create a public key with a point with this convenient method', function() { + var p = Point(); + var pk = new Pubkey(p); + should.exist(pk.point); + pk.point.toString().should.equal(p.toString()); + }); + describe('#set', function() { it('should make a public key from a point', function() { From caf6c87419d32e6c7cd14689406b7f06b9d2cbc9 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 11:59:42 -0700 Subject: [PATCH 162/280] Signature(r, s) should work --- lib/signature.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/signature.js b/lib/signature.js index 31ef75a..c409d04 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -4,7 +4,7 @@ var Pubkey = require('./pubkey'); var Signature = function Signature(r, s) { if (!(this instanceof Signature)) - return new Signature(obj); + return new Signature(r, s); if (r instanceof BN) { this.set({ r: r, From 073ee0a0e43b32f2ceef34bb4e2db51279857558 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 12:07:18 -0700 Subject: [PATCH 163/280] fix: Pubkey(point) and Privkey(bn) --- lib/privkey.js | 2 +- lib/pubkey.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/privkey.js b/lib/privkey.js index c0fc0d6..aa107a1 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -6,7 +6,7 @@ var Random = require('./random'); var Privkey = function Privkey(bn) { if (!(this instanceof Privkey)) - return new Privkey(obj); + return new Privkey(bn); if (bn instanceof BN) this.bn = bn; else if (bn) { diff --git a/lib/pubkey.js b/lib/pubkey.js index 522b758..a0e0c99 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -4,7 +4,7 @@ var privkey = require('./privkey'); var Pubkey = function Pubkey(point) { if (!(this instanceof Pubkey)) - return new Pubkey(obj); + return new Pubkey(point); if (point instanceof Point) this.point = point; else if (point) { From 258dab9d6d06a0de013e4dc89bb9b2807cbe98a2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 12:08:33 -0700 Subject: [PATCH 164/280] convenience: Stealth(payloadKeypair, scanKeypair) --- lib/expmt/stealthkey.js | 15 ++++++++++++--- test/stealthkey.js | 8 ++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index 9d25830..f249d48 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -6,11 +6,20 @@ var Hash = require('../hash'); var KDF = require('../kdf'); var base58check = require('../base58check'); -var Stealthkey = function Stealthkey(obj) { +var Stealthkey = function Stealthkey(payloadKeypair, scanKeypair) { if (!(this instanceof Stealthkey)) - return new Stealthkey(obj); - if (obj) + return new Stealthkey(payloadKeypair, scanKeypair); + + if (payloadKeypair instanceof Keypair) { + this.set({ + payloadKeypair: payloadKeypair, + scanKeypair: scanKeypair + }); + } + else if (payloadKeypair) { + var obj = payloadKeypair; this.set(obj); + } }; Stealthkey.prototype.set = function(obj) { diff --git a/test/stealthkey.js b/test/stealthkey.js index 7b2a871..5a44fa6 100644 --- a/test/stealthkey.js +++ b/test/stealthkey.js @@ -36,6 +36,14 @@ describe('Stealthkey', function() { should.exist(stealthkey); }); + it('should create a new stealthkey with both keypairs in the constructor', function() { + var keypair1 = Keypair(); + var keypair2 = Keypair(); + var stealthkey = Stealthkey(keypair1, keypair2); + should.exist(stealthkey.payloadKeypair); + should.exist(stealthkey.scanKeypair); + }); + describe('#set', function() { it('should set payload key', function() { From 139fe023557d35a8515bf4361e7689608be50b84 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 12:48:36 -0700 Subject: [PATCH 165/280] StealthAddress --- lib/expmt/stealthaddress.js | 61 +++++++++++++++++++++++++++++ test/stealthaddress.js | 77 +++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 lib/expmt/stealthaddress.js create mode 100644 test/stealthaddress.js diff --git a/lib/expmt/stealthaddress.js b/lib/expmt/stealthaddress.js new file mode 100644 index 0000000..b7d58a6 --- /dev/null +++ b/lib/expmt/stealthaddress.js @@ -0,0 +1,61 @@ +var Stealthkey = require('./stealthkey'); +var Base58check = require('../base58check'); +var Pubkey = require('../pubkey'); + +var StealthAddress = function StealthAddress(obj) { + if (!(this instanceof StealthAddress)) + return new StealthAddress(obj); + + if (obj) + this.set(obj); +}; + +StealthAddress.prototype.set = function(obj) { + this.payloadPubkey = obj.payloadPubkey || this.payloadPubkey; + this.scanPubkey = obj.scanPubkey || this.scanPubkey; + return this; +}; + +StealthAddress.prototype.fromStealthkey = function(stealthkey) { + this.set({ + payloadPubkey: stealthkey.payloadPubkey, + scanPubkey: stealthkey.scanPubkey + }); + return this; +}; + +StealthAddress.prototype.fromBuffer = function(buf) { + if (!Buffer.isBuffer(buf) || buf.length !== 66) + throw new Error('stealthkey: A stealth address must have length 66'); + + var pPubBuf = buf.slice(0, 33); + var sPubBuf = buf.slice(33, 66); + + this.payloadPubkey = Pubkey().fromDER(pPubBuf); + this.scanPubkey = Pubkey().fromDER(sPubBuf); + + return this; +}; + +StealthAddress.prototype.fromString = function(str) { + var buf = Base58check.decode(str); + this.fromBuffer(buf); + + return this; +}; + +StealthAddress.prototype.toBuffer = function() { + var pBuf = this.payloadPubkey.toDER(true); + var sBuf = this.scanPubkey.toDER(true); + + return Buffer.concat([pBuf, sBuf]); +}; + +StealthAddress.prototype.toString = function() { + var buf = this.toBuffer(); + var b58 = Base58check.encode(buf); + + return b58; +}; + +module.exports = StealthAddress; diff --git a/test/stealthaddress.js b/test/stealthaddress.js new file mode 100644 index 0000000..a976b6c --- /dev/null +++ b/test/stealthaddress.js @@ -0,0 +1,77 @@ +var StealthAddress = require('../lib/expmt/stealthaddress'); +var should = require('chai').should(); +var Stealthkey = require('../lib/expmt/stealthkey'); +var Keypair = require('../lib/keypair'); +var Privkey = require('../lib/privkey'); +var Pubkey = require('../lib/pubkey'); +var BN = require('../lib/bn'); +var Hash = require('../lib/Hash'); +var Base58check = require('../lib/base58check'); + +describe('StealthAddress', function() { + + var stealthkey = Stealthkey(); + stealthkey.payloadKeypair = Keypair(); + stealthkey.payloadKeypair.privkey = Privkey(); + stealthkey.payloadKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 1'))); + stealthkey.payloadKeypair.privkey2pubkey(); + stealthkey.scanKeypair = Keypair(); + stealthkey.scanKeypair.privkey = Privkey(); + stealthkey.scanKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 2'))); + stealthkey.scanKeypair.privkey2pubkey(); + + var senderKeypair = Keypair(); + senderKeypair.privkey = Privkey(); + senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); + senderKeypair.privkey2pubkey(); + + var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW'; + + it('should make a new stealth address', function() { + var sa = new StealthAddress(); + should.exist(sa); + sa = StealthAddress(); + should.exist(sa); + }); + + describe('#fromBuffer', function() { + + it('should give a stealthkey address with the right pubkeys', function() { + var sa = new StealthAddress(); + var buf = Base58check.decode(addressString); + sa.fromBuffer(buf); + sa.payloadPubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); + sa.scanPubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); + }); + + }); + + describe('#fromString', function() { + + it('should give a stealthkey address with the right pubkeys', function() { + var sa = new StealthAddress(); + sa.fromString(addressString); + sa.payloadPubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); + sa.scanPubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); + }); + + }); + + describe('#toBuffer', function() { + + it('should return this known address buffer', function() { + var buf = Base58check.decode(addressString); + StealthAddress().fromBuffer(buf).toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + }); + + describe('#toString', function() { + + it('should return this known address string', function() { + StealthAddress().fromString(addressString).toString().should.equal(addressString); + }); + + }); + +}); From 36fd6b2d9c0a6592ffc85a6a4116e8d4c7d8aeb2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 12:53:41 -0700 Subject: [PATCH 166/280] take address stuff out of stealthkey ...that is what StealthAddress is now for --- lib/expmt/stealthkey.js | 36 ------------------------------------ test/stealthkey.js | 40 ---------------------------------------- 2 files changed, 76 deletions(-) diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index f249d48..2382cd0 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -28,28 +28,6 @@ Stealthkey.prototype.set = function(obj) { return this; }; -Stealthkey.prototype.fromAddressBuffer = function(buf) { - if (!Buffer.isBuffer(buf) || buf.length !== 66) - throw new Error('stealthkey: A stealthkey address must have length 66'); - - var pPubBuf = buf.slice(0, 33); - var sPubBuf = buf.slice(33, 66); - - var payloadPubkey = Pubkey().fromDER(pPubBuf); - this.payloadKeypair = Keypair({pubkey: payloadPubkey}); - var scanPubkey = Pubkey().fromDER(sPubBuf); - this.scanKeypair = Keypair({pubkey: scanPubkey}); - - return this; -}; - -Stealthkey.prototype.fromAddressString = function(str) { - var buf = base58check.decode(str); - this.fromAddressBuffer(buf); - - return this; -}; - Stealthkey.prototype.fromRandom = function() { this.payloadKeypair = Keypair().fromRandom(); this.scanKeypair = Keypair().fromRandom(); @@ -109,18 +87,4 @@ Stealthkey.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) { return false; }; -Stealthkey.prototype.toAddressBuffer = function() { - var pBuf = this.payloadKeypair.pubkey.toDER(true); - var sBuf = this.scanKeypair.pubkey.toDER(true); - - return Buffer.concat([pBuf, sBuf]); -}; - -Stealthkey.prototype.toAddressString = function() { - var buf = this.toAddressBuffer(); - var b58 = base58check.encode(buf); - - return b58; -}; - module.exports = Stealthkey; diff --git a/test/stealthkey.js b/test/stealthkey.js index 5a44fa6..09638ed 100644 --- a/test/stealthkey.js +++ b/test/stealthkey.js @@ -52,29 +52,6 @@ describe('Stealthkey', function() { }); - describe('#fromAddressBuffer', function() { - - it('should give a stealthkey address with the right pubkeys', function() { - var stealthkey2 = new Stealthkey(); - var buf = base58check.decode(addressString); - stealthkey2.fromAddressBuffer(buf); - stealthkey2.payloadKeypair.pubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); - stealthkey2.scanKeypair.pubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); - }); - - }); - - describe('#fromAddressString', function() { - - it('should give a stealthkey address with the right pubkeys', function() { - var stealthkey2 = new Stealthkey(); - stealthkey2.fromAddressString(addressString); - stealthkey2.payloadKeypair.pubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); - stealthkey2.scanKeypair.pubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); - }); - - }); - describe('#fromRandom', function() { it('should create a new stealthkey from random', function() { @@ -178,21 +155,4 @@ describe('Stealthkey', function() { }); - describe('#toAddressBuffer', function() { - - it('should return this known address buffer', function() { - var buf = stealthkey.toAddressBuffer(); - base58check.encode(buf).should.equal(addressString); - }); - - }); - - describe('#toAddressString', function() { - - it('should return this known address string', function() { - stealthkey.toAddressString().should.equal(addressString); - }); - - }); - }); From 48ae69cab06d0f8278091c1a0bdda1b3d6a3fbf2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 14:23:11 -0700 Subject: [PATCH 167/280] convenience: StealthAddress(str) or StealthAddress(buf) --- lib/expmt/stealthaddress.js | 17 +++++++++++++---- test/stealthaddress.js | 4 ++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/expmt/stealthaddress.js b/lib/expmt/stealthaddress.js index b7d58a6..9f6e3f9 100644 --- a/lib/expmt/stealthaddress.js +++ b/lib/expmt/stealthaddress.js @@ -2,12 +2,21 @@ var Stealthkey = require('./stealthkey'); var Base58check = require('../base58check'); var Pubkey = require('../pubkey'); -var StealthAddress = function StealthAddress(obj) { +var StealthAddress = function StealthAddress(addrstr) { if (!(this instanceof StealthAddress)) - return new StealthAddress(obj); - - if (obj) + return new StealthAddress(addrstr); + + if (typeof addrstr === 'string') { + this.fromString(addrstr) + } + else if (Buffer.isBuffer(addrstr)) { + var buf = addrstr; + this.fromBuffer(buf); + } + else if (addrstr) { + var obj = addrstr; this.set(obj); + } }; StealthAddress.prototype.set = function(obj) { diff --git a/test/stealthaddress.js b/test/stealthaddress.js index a976b6c..872b1de 100644 --- a/test/stealthaddress.js +++ b/test/stealthaddress.js @@ -32,6 +32,10 @@ describe('StealthAddress', function() { should.exist(sa); sa = StealthAddress(); should.exist(sa); + sa = StealthAddress(addressString); + should.exist(sa); + sa = StealthAddress(Base58check.decode(addressString)); + should.exist(sa); }); describe('#fromBuffer', function() { From d1a570135d6b9ed75914c4e58963bcfd82a492c6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 15:25:16 -0700 Subject: [PATCH 168/280] getSharedKeypair & getReceivePubkey --- lib/expmt/stealthaddress.js | 21 +++++++++++++++++++-- test/stealthaddress.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/lib/expmt/stealthaddress.js b/lib/expmt/stealthaddress.js index 9f6e3f9..0d82128 100644 --- a/lib/expmt/stealthaddress.js +++ b/lib/expmt/stealthaddress.js @@ -1,6 +1,7 @@ var Stealthkey = require('./stealthkey'); var Base58check = require('../base58check'); var Pubkey = require('../pubkey'); +var KDF = require('../kdf'); var StealthAddress = function StealthAddress(addrstr) { if (!(this instanceof StealthAddress)) @@ -27,8 +28,8 @@ StealthAddress.prototype.set = function(obj) { StealthAddress.prototype.fromStealthkey = function(stealthkey) { this.set({ - payloadPubkey: stealthkey.payloadPubkey, - scanPubkey: stealthkey.scanPubkey + payloadPubkey: stealthkey.payloadKeypair.pubkey, + scanPubkey: stealthkey.scanKeypair.pubkey }); return this; }; @@ -53,6 +54,22 @@ StealthAddress.prototype.fromString = function(str) { return this; }; +StealthAddress.prototype.getSharedKeypair = function(senderKeypair) { + var sharedSecretPoint = this.scanPubkey.point.mul(senderKeypair.privkey.bn); + var sharedSecretPubkey = Pubkey(sharedSecretPoint); + var buf = sharedSecretPubkey.toDER(true); + var sharedKeypair = KDF.sha256hmac2keypair(buf); + + return sharedKeypair; +}; + +StealthAddress.prototype.getReceivePubkey = function(senderKeypair) { + var sharedKeypair = this.getSharedKeypair(senderKeypair); + var pubkey = Pubkey(this.payloadPubkey.point.add(sharedKeypair.pubkey.point)); + + return pubkey; +}; + StealthAddress.prototype.toBuffer = function() { var pBuf = this.payloadPubkey.toDER(true); var sBuf = this.scanPubkey.toDER(true); diff --git a/test/stealthaddress.js b/test/stealthaddress.js index 872b1de..e4962be 100644 --- a/test/stealthaddress.js +++ b/test/stealthaddress.js @@ -61,6 +61,43 @@ describe('StealthAddress', function() { }); + describe('#getSharedKeypair', function() { + + it('should return a key', function() { + var sa = new StealthAddress(); + sa.payloadPubkey = stealthkey.payloadKeypair.pubkey; + sa.scanPubkey = stealthkey.scanKeypair.pubkey; + var key = sa.getSharedKeypair(senderKeypair); + (key instanceof Keypair).should.equal(true); + }); + + it('should return the same key as Stealthkey.prototype.getSharedKeypairAsReceiver', function() { + var sa = new StealthAddress(); + sa.payloadPubkey = stealthkey.payloadKeypair.pubkey; + sa.scanPubkey = stealthkey.scanKeypair.pubkey; + var key = sa.getSharedKeypair(senderKeypair); + + var key2 = stealthkey.getSharedKeypairAsReceiver(senderKeypair.pubkey); + key.toString().should.equal(key2.toString()); + }); + + }); + + describe('#getReceivePubkey', function() { + + it('should return a pubkey', function() { + var pubkey = StealthAddress().fromStealthkey(stealthkey).getReceivePubkey(senderKeypair); + (pubkey instanceof Pubkey).should.equal(true); + }); + + it('should return the same pubkey as getReceivePubkeyAsReceiver', function() { + var pubkey = StealthAddress().fromStealthkey(stealthkey).getReceivePubkey(senderKeypair); + var pubkey2 = stealthkey.getReceivePubkeyAsReceiver(senderKeypair.pubkey); + pubkey2.toString().should.equal(pubkey.toString()); + }); + + }); + describe('#toBuffer', function() { it('should return this known address buffer', function() { From 779e48d5627d1d8d2e545d75664ffb05bc47d355 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 15:27:25 -0700 Subject: [PATCH 169/280] expose new StealthAddress class --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index e47ef15..9cf8bc8 100644 --- a/index.js +++ b/index.js @@ -29,6 +29,7 @@ bitcore.expmt.AESCBC = require('./lib/expmt/aescbc'); bitcore.expmt.CBC = require('./lib/expmt/cbc'); bitcore.expmt.ECIES = require('./lib/expmt/ecies'); bitcore.expmt.Stealthkey = require('./lib/expmt/stealthkey'); +bitcore.expmt.StealthAddress = require('./lib/expmt/stealthaddress'); //dependencies, subject to change bitcore.deps = {}; From 558a7672ef8578beb491d3c74be10ef5d5667c8d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 15:28:32 -0700 Subject: [PATCH 170/280] Stealthkey AsSender functions obsolete use StealthAddress instead --- lib/expmt/stealthkey.js | 16 ---------------- test/stealthkey.js | 41 ----------------------------------------- 2 files changed, 57 deletions(-) diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index 2382cd0..224203f 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -44,15 +44,6 @@ Stealthkey.prototype.getSharedKeypairAsReceiver = function(senderPubkey) { return sharedKeypair; }; -Stealthkey.prototype.getSharedKeypairAsSender = function(senderKeypair) { - var sharedSecretPoint = this.scanKeypair.pubkey.point.mul(senderKeypair.privkey.bn); - var sharedSecretPubkey = Pubkey({point: sharedSecretPoint}); - var buf = sharedSecretPubkey.toDER(true); - var sharedKeypair = KDF.sha256hmac2keypair(buf); - - return sharedKeypair; -}; - Stealthkey.prototype.getReceivePubkeyAsReceiver = function(senderPubkey) { var sharedKeypair = this.getSharedKeypairAsReceiver(senderPubkey); var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); @@ -60,13 +51,6 @@ Stealthkey.prototype.getReceivePubkeyAsReceiver = function(senderPubkey) { return pubkey; }; -Stealthkey.prototype.getReceivePubkeyAsSender = function(senderKeypair) { - var sharedKeypair = this.getSharedKeypairAsSender(senderKeypair); - var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); - - return pubkey; -}; - Stealthkey.prototype.getReceiveKeypair = function(senderPubkey) { var sharedKeypair = this.getSharedKeypairAsReceiver(senderPubkey); var privkey = Privkey({bn: this.payloadKeypair.privkey.bn.add(sharedKeypair.privkey.bn).mod(Point.getN())}); diff --git a/test/stealthkey.js b/test/stealthkey.js index 09638ed..222a027 100644 --- a/test/stealthkey.js +++ b/test/stealthkey.js @@ -71,32 +71,6 @@ describe('Stealthkey', function() { }); - describe('#getSharedKeypairAsSender', function() { - - it('should return a key', function() { - var stealthkey2 = new Stealthkey(); - stealthkey2.payloadKeypair = new Keypair(); - stealthkey2.payloadKeypair.pubkey = stealthkey.payloadKeypair.pubkey; - stealthkey2.scanKeypair = new Keypair(); - stealthkey2.scanKeypair.pubkey = stealthkey.scanKeypair.pubkey; - var key = stealthkey2.getSharedKeypairAsSender(senderKeypair); - (key instanceof Keypair).should.equal(true); - }); - - it('should return the same key as getSharedKeypairAsReceiver', function() { - var stealthkey2 = new Stealthkey(); - stealthkey2.payloadKeypair = new Keypair(); - stealthkey2.payloadKeypair.pubkey = stealthkey.payloadKeypair.pubkey; - stealthkey2.scanKeypair = new Keypair(); - stealthkey2.scanKeypair.pubkey = stealthkey.scanKeypair.pubkey; - var key = stealthkey2.getSharedKeypairAsSender(senderKeypair); - - var key2 = stealthkey.getSharedKeypairAsReceiver(senderKeypair.pubkey); - key.toString().should.equal(key2.toString()); - }); - - }); - describe('#getReceivePubkeyAsReceiver', function() { it('should return a pubkey', function() { @@ -106,21 +80,6 @@ describe('Stealthkey', function() { }); - describe('#getReceivePubkeyAsSender', function() { - - it('should return a pubkey', function() { - var pubkey = stealthkey.getReceivePubkeyAsSender(senderKeypair); - (pubkey instanceof Pubkey).should.equal(true); - }); - - it('should return the same pubkey as getReceivePubkeyAsReceiver', function() { - var pubkey = stealthkey.getReceivePubkeyAsSender(senderKeypair); - var pubkey2 = stealthkey.getReceivePubkeyAsReceiver(senderKeypair.pubkey); - pubkey2.toString().should.equal(pubkey.toString()); - }); - - }); - describe('#getReceiveKeypair', function() { it('should return a key', function() { From 219aa528c84286edfeb8353b2ea50b8c243d8f76 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 15:54:24 -0700 Subject: [PATCH 171/280] AsReceiver implied --- lib/expmt/stealthkey.js | 10 +++++----- test/stealthaddress.js | 8 ++++---- test/stealthkey.js | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index 224203f..6a2e2dc 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -35,7 +35,7 @@ Stealthkey.prototype.fromRandom = function() { return this; }; -Stealthkey.prototype.getSharedKeypairAsReceiver = function(senderPubkey) { +Stealthkey.prototype.getSharedKeypair = function(senderPubkey) { var sharedSecretPoint = senderPubkey.point.mul(this.scanKeypair.privkey.bn); var sharedSecretPubkey = Pubkey({point: sharedSecretPoint}); var buf = sharedSecretPubkey.toDER(true); @@ -44,15 +44,15 @@ Stealthkey.prototype.getSharedKeypairAsReceiver = function(senderPubkey) { return sharedKeypair; }; -Stealthkey.prototype.getReceivePubkeyAsReceiver = function(senderPubkey) { - var sharedKeypair = this.getSharedKeypairAsReceiver(senderPubkey); +Stealthkey.prototype.getReceivePubkey = function(senderPubkey) { + var sharedKeypair = this.getSharedKeypair(senderPubkey); var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); return pubkey; }; Stealthkey.prototype.getReceiveKeypair = function(senderPubkey) { - var sharedKeypair = this.getSharedKeypairAsReceiver(senderPubkey); + var sharedKeypair = this.getSharedKeypair(senderPubkey); var privkey = Privkey({bn: this.payloadKeypair.privkey.bn.add(sharedKeypair.privkey.bn).mod(Point.getN())}); var key = Keypair({privkey: privkey}); key.privkey2pubkey(); @@ -61,7 +61,7 @@ Stealthkey.prototype.getReceiveKeypair = function(senderPubkey) { }; Stealthkey.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) { - var pubkey = this.getReceivePubkeyAsReceiver(senderPubkey); + var pubkey = this.getReceivePubkey(senderPubkey); var pubkeybuf = pubkey.toDER(true); var pubkeyhash = Hash.sha256ripemd160(pubkeybuf); diff --git a/test/stealthaddress.js b/test/stealthaddress.js index e4962be..6fcc6e5 100644 --- a/test/stealthaddress.js +++ b/test/stealthaddress.js @@ -71,13 +71,13 @@ describe('StealthAddress', function() { (key instanceof Keypair).should.equal(true); }); - it('should return the same key as Stealthkey.prototype.getSharedKeypairAsReceiver', function() { + it('should return the same key as Stealthkey.prototype.getSharedKeypair', function() { var sa = new StealthAddress(); sa.payloadPubkey = stealthkey.payloadKeypair.pubkey; sa.scanPubkey = stealthkey.scanKeypair.pubkey; var key = sa.getSharedKeypair(senderKeypair); - var key2 = stealthkey.getSharedKeypairAsReceiver(senderKeypair.pubkey); + var key2 = stealthkey.getSharedKeypair(senderKeypair.pubkey); key.toString().should.equal(key2.toString()); }); @@ -90,9 +90,9 @@ describe('StealthAddress', function() { (pubkey instanceof Pubkey).should.equal(true); }); - it('should return the same pubkey as getReceivePubkeyAsReceiver', function() { + it('should return the same pubkey as getReceivePubkey', function() { var pubkey = StealthAddress().fromStealthkey(stealthkey).getReceivePubkey(senderKeypair); - var pubkey2 = stealthkey.getReceivePubkeyAsReceiver(senderKeypair.pubkey); + var pubkey2 = stealthkey.getReceivePubkey(senderKeypair.pubkey); pubkey2.toString().should.equal(pubkey.toString()); }); diff --git a/test/stealthkey.js b/test/stealthkey.js index 222a027..472cd75 100644 --- a/test/stealthkey.js +++ b/test/stealthkey.js @@ -62,19 +62,19 @@ describe('Stealthkey', function() { }); - describe('#getSharedKeypairAsReceiver', function() { + describe('#getSharedKeypair', function() { it('should return a key', function() { - var key = stealthkey.getSharedKeypairAsReceiver(senderKeypair.pubkey); + var key = stealthkey.getSharedKeypair(senderKeypair.pubkey); (key instanceof Keypair).should.equal(true); }); }); - describe('#getReceivePubkeyAsReceiver', function() { + describe('#getReceivePubkey', function() { it('should return a pubkey', function() { - var pubkey = stealthkey.getReceivePubkeyAsReceiver(senderKeypair.pubkey); + var pubkey = stealthkey.getReceivePubkey(senderKeypair.pubkey); (pubkey instanceof Pubkey).should.equal(true); }); @@ -87,9 +87,9 @@ describe('Stealthkey', function() { (key instanceof Keypair).should.equal(true); }); - it('should return a key with the same pubkey as getReceivePubkeyAsReceiver', function() { + it('should return a key with the same pubkey as getReceivePubkey', function() { var key = stealthkey.getReceiveKeypair(senderKeypair.pubkey); - var pubkey = stealthkey.getReceivePubkeyAsReceiver(senderKeypair.pubkey); + var pubkey = stealthkey.getReceivePubkey(senderKeypair.pubkey); key.pubkey.toString().should.equal(pubkey.toString()); }); From 74549a53ec3109e251a3fe04aa10b778ce273ea9 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 2 Sep 2014 16:36:21 -0700 Subject: [PATCH 172/280] key -> keypair Since the class has been renamed Key -> Keypair, instances should be renamed key -> keypair. --- lib/ecdsa.js | 22 +++++++++++----------- lib/message.js | 14 +++++++------- test/ecdsa.js | 22 +++++++++++----------- test/message.js | 32 ++++++++++++++++---------------- 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index d4b6429..f6bc3d3 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -14,7 +14,7 @@ var ECDSA = function ECDSA(obj) { ECDSA.prototype.set = function(obj) { this.hashbuf = obj.hashbuf || this.hashbuf || undefined; - this.key = obj.key || this.key || undefined; + this.keypair = obj.keypair || this.keypair || undefined; this.sig = obj.sig || this.sig || undefined; this.k = obj.k || this.k || undefined; this.verified = obj.verified || this.verified || undefined; @@ -30,8 +30,8 @@ ECDSA.prototype.calci = function() { continue; } - if (Qprime.point.eq(this.key.pubkey.point)) { - this.sig.compressed = this.key.pubkey.compressed; + if (Qprime.point.eq(this.keypair.pubkey.point)) { + this.sig.compressed = this.keypair.pubkey.compressed; return this; } } @@ -44,8 +44,8 @@ ECDSA.prototype.fromString = function(str) { var obj = JSON.parse(str); if (obj.hashbuf) this.hashbuf = new Buffer(obj.hashbuf, 'hex'); - if (obj.key) - this.key = Keypair().fromString(obj.key); + if (obj.keypair) + this.keypair = Keypair().fromString(obj.keypair); if (obj.sig) this.sig = Signature().fromString(obj.sig); if (obj.k) @@ -117,7 +117,7 @@ ECDSA.prototype.sigError = function() { return 'Invalid hash'; try { - this.key.pubkey.validate(); + this.keypair.pubkey.validate(); } catch (e) { return 'Invalid pubkey: ' + e; } @@ -134,7 +134,7 @@ ECDSA.prototype.sigError = function() { var u1 = sinv.mul(e).mod(n); var u2 = sinv.mul(r).mod(n); - var p = Point.getG().mulAdd(u1, this.key.pubkey.point, u2); + var p = Point.getG().mulAdd(u1, this.keypair.pubkey.point, u2); if (p.isInfinity()) return 'p is infinity'; @@ -146,7 +146,7 @@ ECDSA.prototype.sigError = function() { ECDSA.prototype.sign = function() { var hashbuf = this.hashbuf; - var privkey = this.key.privkey; + var privkey = this.keypair.privkey; var k = this.k; var d = privkey.bn; @@ -166,7 +166,7 @@ ECDSA.prototype.sign = function() { var s = k.invm(N).mul(e.add(d.mul(r))).mod(N); } while (r.cmp(0) <= 0 || s.cmp(0) <= 0); - this.sig = new Signature({r: r, s: s, compressed: this.key.pubkey.compressed}); + this.sig = new Signature({r: r, s: s, compressed: this.keypair.pubkey.compressed}); return this.sig; }; @@ -179,8 +179,8 @@ ECDSA.prototype.toString = function() { var obj = {}; if (this.hashbuf) obj.hashbuf = this.hashbuf.toString('hex'); - if (this.key) - obj.key = this.key.toString(); + if (this.keypair) + obj.keypair = this.keypair.toString(); if (this.sig) obj.sig = this.sig.toString(); if (this.k) diff --git a/lib/message.js b/lib/message.js index 1cfbe3c..0536d43 100644 --- a/lib/message.js +++ b/lib/message.js @@ -16,7 +16,7 @@ var Message = function Message(obj) { Message.prototype.set = function(obj) { this.messagebuf = obj.messagebuf || this.messagebuf; - this.key = obj.key || this.key; + this.keypair = obj.keypair || this.keypair; this.sig = obj.sig || this.sig; this.address = obj.address || this.address; this.verified = typeof obj.verified !== 'undefined' ? obj.verified : this.verified; @@ -40,8 +40,8 @@ Message.magicHash = function(messagebuf) { return hashbuf; }; -Message.sign = function(messagebuf, key) { - var m = Message({messagebuf: messagebuf, key: key}); +Message.sign = function(messagebuf, keypair) { + var m = Message({messagebuf: messagebuf, keypair: keypair}); m.sign(); var sigbuf = m.sig.toCompact(); var sigstr = sigbuf.toString('base64'); @@ -60,7 +60,7 @@ Message.verify = function(messagebuf, sigstr, address) { Message.prototype.sign = function() { var hashbuf = Message.magicHash(this.messagebuf); - var ecdsa = ECDSA({hashbuf: hashbuf, key: this.key}); + var ecdsa = ECDSA({hashbuf: hashbuf, keypair: this.keypair}); ecdsa.signRandomK(); ecdsa.calci(); this.sig = ecdsa.sig; @@ -73,15 +73,15 @@ Message.prototype.verify = function() { var ecdsa = new ECDSA(); ecdsa.hashbuf = hashbuf; ecdsa.sig = this.sig; - ecdsa.key = new Keypair(); - ecdsa.key.pubkey = ecdsa.sig2pubkey(); + ecdsa.keypair = new Keypair(); + ecdsa.keypair.pubkey = ecdsa.sig2pubkey(); if (!ecdsa.verify()) { this.verified = false; return this; } - var address = Address().fromPubkey(ecdsa.key.pubkey, undefined, this.sig.compressed); + var address = Address().fromPubkey(ecdsa.keypair.pubkey, undefined, this.sig.compressed); //TODO: what if livenet/testnet mismatch? if (address.hashbuf.toString('hex') === this.address.hashbuf.toString('hex')) this.verified = true; diff --git a/test/ecdsa.js b/test/ecdsa.js index 0a9bff9..c285a4d 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -16,9 +16,9 @@ describe("ECDSA", function() { var ecdsa = new ECDSA(); ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); - ecdsa.key = new Keypair(); - ecdsa.key.privkey = new Privkey({bn: BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))}); - ecdsa.key.pubkey = new Pubkey({ + ecdsa.keypair = new Keypair(); + ecdsa.keypair.privkey = new Privkey({bn: BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))}); + ecdsa.keypair.pubkey = new Pubkey({ point: point(BN().fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')), BN().fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex'))) }); @@ -45,10 +45,10 @@ describe("ECDSA", function() { var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10); var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10); var ecdsa = new ECDSA(); - ecdsa.key = new Keypair(); - ecdsa.key.privkey = Privkey(); - ecdsa.key.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test'))); - ecdsa.key.privkey2pubkey(); + ecdsa.keypair = new Keypair(); + ecdsa.keypair.privkey = Privkey(); + ecdsa.keypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test'))); + ecdsa.keypair.privkey2pubkey(); ecdsa.hashbuf = hashbuf; ecdsa.sig = new Signature({r: r, s: s}); @@ -65,7 +65,7 @@ describe("ECDSA", function() { var ecdsa2 = new ECDSA(); ecdsa2.fromString(str); should.exist(ecdsa.hashbuf); - should.exist(ecdsa.key); + should.exist(ecdsa.keypair); }); }); @@ -96,7 +96,7 @@ describe("ECDSA", function() { ecdsa.sign(); ecdsa.sig.i = 1; var pubkey = ecdsa.sig2pubkey(); - pubkey.point.eq(ecdsa.key.pubkey.point).should.equal(true); + pubkey.point.eq(ecdsa.keypair.pubkey.point).should.equal(true); }); }); @@ -119,8 +119,8 @@ describe("ECDSA", function() { ecdsa.hashbuf = Hash.sha256(new Buffer('test')); var pk = new Pubkey(); pk.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); - ecdsa.key = new Keypair(); - ecdsa.key.pubkey = pk; + ecdsa.keypair = new Keypair(); + ecdsa.keypair.pubkey = pk; ecdsa.sig = new Signature(); ecdsa.sig.r = BN(0); ecdsa.sig.s = BN(0); diff --git a/test/message.js b/test/message.js index f90e5f6..7138271 100644 --- a/test/message.js +++ b/test/message.js @@ -26,27 +26,27 @@ describe('Message', function() { describe('@sign', function() { var messagebuf = new Buffer('this is my message'); - var key = Keypair().fromRandom(); + var keypair = Keypair().fromRandom(); it('should return a base64 string', function() { - var sigstr = Message.sign(messagebuf, key); + var sigstr = Message.sign(messagebuf, keypair); var sigbuf = new Buffer(sigstr, 'base64'); sigbuf.length.should.equal(1 + 32 + 32); }); it('should sign with a compressed pubkey', function() { - var key = Keypair().fromRandom(); - key.pubkey.compressed = true; - var sigstr = Message.sign(messagebuf, key); + var keypair = Keypair().fromRandom(); + keypair.pubkey.compressed = true; + var sigstr = Message.sign(messagebuf, keypair); var sigbuf = new Buffer(sigstr, 'base64'); sigbuf[0].should.be.above(27 + 4 - 1); sigbuf[0].should.be.below(27 + 4 + 4 - 1); }); it('should sign with an uncompressed pubkey', function() { - var key = Keypair().fromRandom(); - key.pubkey.compressed = false; - var sigstr = Message.sign(messagebuf, key); + var keypair = Keypair().fromRandom(); + keypair.pubkey.compressed = false; + var sigstr = Message.sign(messagebuf, keypair); var sigbuf = new Buffer(sigstr, 'base64'); sigbuf[0].should.be.above(27 - 1); sigbuf[0].should.be.below(27 + 4 - 1); @@ -56,11 +56,11 @@ describe('Message', function() { describe('@verify', function() { var messagebuf = new Buffer('this is my message'); - var key = Keypair().fromRandom(); + var keypair = Keypair().fromRandom(); it('should verify a signed message', function() { - var sigstr = Message.sign(messagebuf, key); - var addr = Address().fromPubkey(key.pubkey); + var sigstr = Message.sign(messagebuf, keypair); + var addr = Address().fromPubkey(keypair.pubkey); Message.verify(messagebuf, sigstr, addr).should.equal(true); }); @@ -75,12 +75,12 @@ describe('Message', function() { describe('#sign', function() { var messagebuf = new Buffer('this is my message'); - var key = Keypair().fromRandom(); + var keypair = Keypair().fromRandom(); it('should sign a message', function() { var message = new Message(); message.messagebuf = messagebuf; - message.key = key; + message.keypair = keypair; message.sign(); var sig = message.sig; should.exist(sig); @@ -90,13 +90,13 @@ describe('Message', function() { describe('#verify', function() { var messagebuf = new Buffer('this is my message'); - var key = Keypair().fromRandom(); + var keypair = Keypair().fromRandom(); it('should verify a message that was just signed', function() { var message = new Message(); message.messagebuf = messagebuf; - message.key = key; - message.address = Address().fromPubkey(key.pubkey); + message.keypair = keypair; + message.address = Address().fromPubkey(keypair.pubkey); message.sign(); message.verify(); message.verified.should.equal(true); From 5a86a1a5c6d2fc6612491259496c1ed6d33b3275 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 10 Sep 2014 14:00:53 -0700 Subject: [PATCH 173/280] StealthMessage This code should be regarded as being a proof-of-concept, and needs more review before being used in production code. At least one thing is guaranteed to change, and that is the format of a stealth address. --- index.js | 3 +- lib/expmt/stealthkey.js | 4 +- lib/expmt/stealthmessage.js | 85 +++++++++++++++++++++++ test/stealthmessage.js | 130 ++++++++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 lib/expmt/stealthmessage.js create mode 100644 test/stealthmessage.js diff --git a/index.js b/index.js index 9cf8bc8..e7889dc 100644 --- a/index.js +++ b/index.js @@ -28,8 +28,9 @@ bitcore.expmt.AES = require('./lib/expmt/aes'); bitcore.expmt.AESCBC = require('./lib/expmt/aescbc'); bitcore.expmt.CBC = require('./lib/expmt/cbc'); bitcore.expmt.ECIES = require('./lib/expmt/ecies'); -bitcore.expmt.Stealthkey = require('./lib/expmt/stealthkey'); bitcore.expmt.StealthAddress = require('./lib/expmt/stealthaddress'); +bitcore.expmt.Stealthkey = require('./lib/expmt/stealthkey'); +bitcore.expmt.StealthMessage = require('./lib/expmt/stealthmessage'); //dependencies, subject to change bitcore.deps = {}; diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index 6a2e2dc..2570c77 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -60,12 +60,12 @@ Stealthkey.prototype.getReceiveKeypair = function(senderPubkey) { return key; }; -Stealthkey.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhash) { +Stealthkey.prototype.isForMe = function(senderPubkey, myPossiblePubkeyhashbuf) { var pubkey = this.getReceivePubkey(senderPubkey); var pubkeybuf = pubkey.toDER(true); var pubkeyhash = Hash.sha256ripemd160(pubkeybuf); - if (pubkeyhash.toString('hex') === myPossiblePubkeyhash.toString('hex')) + if (pubkeyhash.toString('hex') === myPossiblePubkeyhashbuf.toString('hex')) return true; else return false; diff --git a/lib/expmt/stealthmessage.js b/lib/expmt/stealthmessage.js new file mode 100644 index 0000000..de7b607 --- /dev/null +++ b/lib/expmt/stealthmessage.js @@ -0,0 +1,85 @@ +var Stealthkey = require('./stealthkey'); +var StealthAddress = require('./stealthaddress'); +var ECIES = require('./ecies'); +var Message = require('../message'); +var Keypair = require('../keypair'); +var Address = require('../address'); +var Pubkey = require('../pubkey'); + +var StealthMessage = function StealthMessage(obj) { + if (!(this instanceof StealthMessage)) + return new StealthMessage(obj); + if (obj) + this.set(obj); +}; + +StealthMessage.prototype.set = function(obj) { + this.messagebuf = obj.messagebuf || this.messagebuf; + this.encbuf = obj.encbuf || this.encbuf; + this.toStealthAddress = obj.toStealthAddress || this.toStealthAddress; + this.toStealthkey = obj.toStealthkey || this.toStealthkey; + this.fromKeypair = obj.fromKeypair || this.fromKeypair; + this.receiveAddress = obj.receiveAddress || this.receiveAddress; + return this; +}; + +StealthMessage.encrypt = function(messagebuf, toStealthAddress, fromKeypair, ivbuf) { + var sm = StealthMessage().set({ + messagebuf: messagebuf, + toStealthAddress: toStealthAddress, + fromKeypair: fromKeypair + }); + sm.encrypt(ivbuf); + var buf = Buffer.concat([ + sm.receiveAddress.hashbuf, + sm.fromKeypair.pubkey.toDER(true), + sm.encbuf + ]); + return buf; +}; + +StealthMessage.decrypt = function(buf, toStealthkey) { + var sm = StealthMessage().set({ + toStealthkey: toStealthkey, + receiveAddress: Address().set({hashbuf: buf.slice(0, 20)}), + fromKeypair: Keypair().set({pubkey: Pubkey().fromDER(buf.slice(20, 20 + 33))}), + encbuf: buf.slice(20 + 33) + }); + return sm.decrypt().messagebuf; +}; + +StealthMessage.isForMe = function(buf, toStealthkey) { + var sm = StealthMessage().set({ + toStealthkey: toStealthkey, + receiveAddress: Address().set({hashbuf: buf.slice(0, 20)}), + fromKeypair: Keypair().set({pubkey: Pubkey().fromDER(buf.slice(20, 20 + 33))}), + encbuf: buf.slice(20 + 33) + }); + return sm.isForMe(); +}; + +StealthMessage.prototype.encrypt = function(ivbuf) { + if (!this.fromKeypair) + this.fromKeypair = Keypair().fromRandom(); + var receivePubkey = this.toStealthAddress.getReceivePubkey(this.fromKeypair); + this.receiveAddress = Address().fromPubkey(receivePubkey); + this.encbuf = ECIES.encrypt(this.messagebuf, Keypair().set({pubkey: receivePubkey}), this.fromKeypair, ivbuf); + return this; +}; + +StealthMessage.prototype.decrypt = function() { + var receiveKeypair = this.toStealthkey.getReceiveKeypair(this.fromKeypair.pubkey); + this.messagebuf = ECIES.decrypt(this.encbuf, receiveKeypair); + return this; +}; + +StealthMessage.prototype.isForMe = function() { + var receivePubkey = this.toStealthkey.getReceivePubkey(this.fromKeypair.pubkey); + var receiveAddress = Address().fromPubkey(receivePubkey); + if (receiveAddress.toString('hex') === this.receiveAddress.toString('hex')) + return true; + else + return false; +}; + +module.exports = StealthMessage; diff --git a/test/stealthmessage.js b/test/stealthmessage.js new file mode 100644 index 0000000..3a0015c --- /dev/null +++ b/test/stealthmessage.js @@ -0,0 +1,130 @@ +var StealthMessage = require('../lib/expmt/stealthmessage'); +var Stealthkey = require('../lib/expmt/stealthkey'); +var StealthAddress = require('../lib/expmt/stealthAddress'); +var KDF = require('../lib/kdf'); +var Hash = require('../lib/hash'); +var should = require('chai').should(); +var Address = require('../lib/address'); + +describe('StealthMessage', function() { + + var payloadKeypair = KDF.buf2keypair(new Buffer('key1')); + var scanKeypair = KDF.buf2keypair(new Buffer('key2')); + var fromKeypair = KDF.buf2keypair(new Buffer('key3')); + var enchex = 'f557994f16d0d628fa4fdb4ab3d7e0bc5f2754f20381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec9f86d081884c7d659a2feaa0c55ad01560ba2904d3bc8395b6c4a6f87648edb33db6a22170e5e26f340c7ba943169210234cd6a753ad13919b0ab7d678b47b5e7d63e452382de2c2590fb57ef048f7b3'; + var encbuf = new Buffer(enchex, 'hex'); + var ivbuf = Hash.sha256(new Buffer('test')).slice(0, 128 / 8); + var sk = Stealthkey().set({payloadKeypair: payloadKeypair, scanKeypair: scanKeypair}); + var sa = StealthAddress().fromStealthkey(sk); + var messagebuf = new Buffer('this is my message'); + + it('should make a new stealthmessage', function() { + var sm = new StealthMessage(); + should.exist(sm); + sm = StealthMessage() + should.exist(sm); + }); + + it('should allow "set" style syntax', function() { + var encbuf = StealthMessage().set({ + messagebuf: messagebuf, + toStealthAddress: sa + }).encrypt().encbuf; + should.exist(encbuf); + encbuf.length.should.equal(113); + }); + + describe('#set', function() { + + it('should set the messagebuf', function() { + var sm = StealthMessage().set({messagebuf: messagebuf}); + should.exist(sm.messagebuf); + }); + + }); + + describe('@encrypt', function() { + + it('should encrypt a message', function() { + var encbuf = StealthMessage.encrypt(messagebuf, sa); + encbuf.length.should.equal(166); + }); + + it('should encrypt a message with this fromKeypair and ivbuf the same each time', function() { + var encbuf = StealthMessage.encrypt(messagebuf, sa, fromKeypair, ivbuf); + encbuf.length.should.equal(166); + encbuf.toString('hex').should.equal(enchex); + }); + + }); + + describe('@decrypt', function() { + + it('should decrypt this known message correctly', function() { + var messagebuf2 = StealthMessage.decrypt(encbuf, sk); + messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); + }); + + }); + + describe('@isForMe', function() { + + it('should know that this message is for me', function() { + StealthMessage.isForMe(encbuf, sk).should.equal(true); + }); + + }); + + describe('#encrypt', function() { + + it('should encrypt this message', function() { + var sm = StealthMessage().set({ + messagebuf: messagebuf, + toStealthAddress: sa, + fromKeypair: fromKeypair + }); + sm.encrypt().encbuf.length.should.equal(113); + }); + + }); + + describe('#decrypt', function() { + + it('should decrypt that which was encrypted', function() { + var sm = StealthMessage().set({ + messagebuf: messagebuf, + toStealthAddress: sa + }).encrypt(); + var messagebuf2 = StealthMessage().set({ + encbuf: sm.encbuf, + fromKeypair: sm.fromKeypair, + toStealthkey: sk + }).decrypt().messagebuf; + messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); + }); + + }); + + describe('#isForMe', function() { + + it('should know that this message is for me', function() { + StealthMessage().set({ + encbuf: encbuf, + toStealthkey: sk, + fromKeypair: fromKeypair, + receiveAddress: Address().set({hashbuf: encbuf.slice(0, 20)}) + }).isForMe().should.equal(true); + }); + + it('should know that this message is not for me', function() { + StealthMessage().set({ + encbuf: encbuf, + toStealthkey: sk, + fromKeypair: fromKeypair, + receiveAddress: Address().set({hashbuf: encbuf.slice(0, 20)}) + }).isForMe().should.equal(true); + }); + + }); + +}); From 88f3690ef6b6fec4f1e4a1912506886211d9a436 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 12 Sep 2014 12:55:11 -0700 Subject: [PATCH 174/280] StealthMessage example --- examples/stealthmessage.js | 53 ++++++++++++++++++++++++++++++++++++++ test/examples.js | 29 +++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 examples/stealthmessage.js create mode 100644 test/examples.js diff --git a/examples/stealthmessage.js b/examples/stealthmessage.js new file mode 100644 index 0000000..f827acd --- /dev/null +++ b/examples/stealthmessage.js @@ -0,0 +1,53 @@ +var Pubkey = require('../lib/pubkey'); +var Address = require('../lib/address'); +var Stealthkey = require('../lib/expmt/stealthkey'); +var StealthAddress = require('../lib/expmt/stealthaddress'); +var StealthMessage = require('../lib/expmt/stealthmessage'); +var Keypair = require('../lib/keypair') + +//First, the person receiving must make a stealth key. + +var sk = Stealthkey().fromRandom(); + +//It has an associated stealth address. + +var sa = StealthAddress().fromStealthkey(sk); + +console.log('Stealth address: ' + sa); + +//The person sending must have a keypair. +//It is best to make a new one for each message sent. + +var keypair = Keypair().fromRandom(); + +//Now make a message. + +var messagebuf = new Buffer('Hello there. Only you know this message is to, and only you know what it says.'); + +//Encrypt the message with the stealth address. + +var encbuf = StealthMessage.encrypt(messagebuf, sa); + +console.log('Hex of the encrypted message: ' + encbuf.toString('hex')); + +//Note that the first 20 bytes are a pubkeyhash, which may be interpreted as a bitcoin address. +//This address has never been seen before in public. + +var address = Address().set({hashbuf: encbuf.slice(0, 20)}); + +console.log('The randomly generated address the message is to: ' + address); + +//And the next 33 bytes are a nonce public key, which the message is "from". +//It has never been seen before in public. + +var pubkey = Pubkey().fromDER(encbuf.slice(20, 20 + 33)); + +//The owner of the stealth key can check to see if it is for them. + +console.log('Is the message for me? ' + (StealthMessage.isForMe(encbuf, sk) ? "yes" : "no")); + +//The owner can decrypt it. + +var messagebuf2 = StealthMessage.decrypt(encbuf, sk); + +console.log('Decrypted message: ' + messagebuf2.toString()); diff --git a/test/examples.js b/test/examples.js new file mode 100644 index 0000000..ace8c26 --- /dev/null +++ b/test/examples.js @@ -0,0 +1,29 @@ +var should = require('chai').should(); +var fs = require('fs'); + +describe('Examples', function() { + + var filenames = fs.readdirSync(__dirname + '/../examples/'); + + filenames.forEach(function(filename) { + + if (filename.slice(filename.length - 3) === '.js') { + + describe(filename, function() { + + it('should not throw any errors', function() { + (function() { + var save = console.log; + console.log = function() {}; + require('../examples/' + filename); + console.log = save; + }).should.not.throw(); + }); + + }); + + } + + }); + +}); From aa4251bff685b824fd90b90f1ac19c918530abfd Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 12 Sep 2014 13:00:52 -0700 Subject: [PATCH 175/280] typo --- examples/stealthmessage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stealthmessage.js b/examples/stealthmessage.js index f827acd..cec3535 100644 --- a/examples/stealthmessage.js +++ b/examples/stealthmessage.js @@ -22,7 +22,7 @@ var keypair = Keypair().fromRandom(); //Now make a message. -var messagebuf = new Buffer('Hello there. Only you know this message is to, and only you know what it says.'); +var messagebuf = new Buffer('Hello there. Only you know this message is to you, and only you know what it says.'); //Encrypt the message with the stealth address. From 8b875a5926a7ca3233a8bbd356881318323a048a Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 12 Sep 2014 13:26:14 -0700 Subject: [PATCH 176/280] making a keypair is unnecessary --- examples/stealthmessage.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/stealthmessage.js b/examples/stealthmessage.js index cec3535..7841e70 100644 --- a/examples/stealthmessage.js +++ b/examples/stealthmessage.js @@ -15,11 +15,6 @@ var sa = StealthAddress().fromStealthkey(sk); console.log('Stealth address: ' + sa); -//The person sending must have a keypair. -//It is best to make a new one for each message sent. - -var keypair = Keypair().fromRandom(); - //Now make a message. var messagebuf = new Buffer('Hello there. Only you know this message is to you, and only you know what it says.'); From a095341a074abb286b36c10307058ed24385e013 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 12 Sep 2014 13:37:22 -0700 Subject: [PATCH 177/280] print the public key --- examples/stealthmessage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/stealthmessage.js b/examples/stealthmessage.js index 7841e70..5ed227d 100644 --- a/examples/stealthmessage.js +++ b/examples/stealthmessage.js @@ -37,6 +37,8 @@ console.log('The randomly generated address the message is to: ' + address); var pubkey = Pubkey().fromDER(encbuf.slice(20, 20 + 33)); +console.log('Nonce public key: ' + pubkey); + //The owner of the stealth key can check to see if it is for them. console.log('Is the message for me? ' + (StealthMessage.isForMe(encbuf, sk) ? "yes" : "no")); From 79d79012d4a62f38f8136e7c1b1979f1d7f30683 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 12 Sep 2014 17:24:00 -0700 Subject: [PATCH 178/280] fix bug where you can't use isForMe without payloadKeypair It should be possible to check to see if a message isForMe with only the scanKeypair, and not the payloadKeypair. There was a bug where only the scanKeypair was being used to produce the receiveKeypair, but this was a mistake. Both the scanPubkey and payloadPubkey should be necessary to produce the receivePubkey, and both the scanPrivkey and payloadPrivkey should be necessary to produce the receivePrivkey. If an online computer has only the public keys of both (and the scanPrivkey), then that is good enough to check for isForMe. --- examples/stealthmessage.js | 13 +++++++++++++ lib/expmt/stealthaddress.js | 2 +- lib/expmt/stealthkey.js | 4 ++-- test/stealthkey.js | 4 ++-- test/stealthmessage.js | 11 ++++++++++- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/examples/stealthmessage.js b/examples/stealthmessage.js index 5ed227d..809af13 100644 --- a/examples/stealthmessage.js +++ b/examples/stealthmessage.js @@ -48,3 +48,16 @@ console.log('Is the message for me? ' + (StealthMessage.isForMe(encbuf, sk) ? "y var messagebuf2 = StealthMessage.decrypt(encbuf, sk); console.log('Decrypted message: ' + messagebuf2.toString()); + +//If you do not have the payload privkey, you can still use isForMe. +sk.payloadKeypair.privkey = undefined; + +console.log('Without payload privkey, is the message for me? ' + (StealthMessage.isForMe(encbuf, sk) ? "yes" : "no")); + +//...but not decrypt + +try { + StealthMessage.decrypt(encbuf, sk); +} catch (e) { + console.log("...but without the payload privkey, I can't decrypt."); +} diff --git a/lib/expmt/stealthaddress.js b/lib/expmt/stealthaddress.js index 0d82128..2c5cb8b 100644 --- a/lib/expmt/stealthaddress.js +++ b/lib/expmt/stealthaddress.js @@ -65,7 +65,7 @@ StealthAddress.prototype.getSharedKeypair = function(senderKeypair) { StealthAddress.prototype.getReceivePubkey = function(senderKeypair) { var sharedKeypair = this.getSharedKeypair(senderKeypair); - var pubkey = Pubkey(this.payloadPubkey.point.add(sharedKeypair.pubkey.point)); + var pubkey = Pubkey(this.scanPubkey.point.add(sharedKeypair.pubkey.point).add(this.payloadPubkey.point)); return pubkey; }; diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index 2570c77..385983b 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -46,14 +46,14 @@ Stealthkey.prototype.getSharedKeypair = function(senderPubkey) { Stealthkey.prototype.getReceivePubkey = function(senderPubkey) { var sharedKeypair = this.getSharedKeypair(senderPubkey); - var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); + var pubkey = Pubkey({point: this.scanKeypair.pubkey.point.add(sharedKeypair.pubkey.point).add(this.payloadKeypair.pubkey.point)}); return pubkey; }; Stealthkey.prototype.getReceiveKeypair = function(senderPubkey) { var sharedKeypair = this.getSharedKeypair(senderPubkey); - var privkey = Privkey({bn: this.payloadKeypair.privkey.bn.add(sharedKeypair.privkey.bn).mod(Point.getN())}); + var privkey = Privkey({bn: this.scanKeypair.privkey.bn.add(sharedKeypair.privkey.bn).add(this.payloadKeypair.privkey.bn).mod(Point.getN())}); var key = Keypair({privkey: privkey}); key.privkey2pubkey(); diff --git a/test/stealthkey.js b/test/stealthkey.js index 472cd75..69b8b22 100644 --- a/test/stealthkey.js +++ b/test/stealthkey.js @@ -103,12 +103,12 @@ describe('Stealthkey', function() { describe('#isForMe', function() { it('should return true if it (the transaction or message) is for me', function() { - var pubkeyhash = new Buffer('3cb64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); + var pubkeyhash = new Buffer('0db7f06cffb913322e211e8b0688efe8ff52aa8d', 'hex'); stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(true); }); it('should return false if it (the transaction or message) is not for me', function() { - var pubkeyhash = new Buffer('00b64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); + var pubkeyhash = new Buffer('ffb7f06cffb913322e211e8b0688efe8ff52aa8d', 'hex'); stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(false); }); diff --git a/test/stealthmessage.js b/test/stealthmessage.js index 3a0015c..2fd3769 100644 --- a/test/stealthmessage.js +++ b/test/stealthmessage.js @@ -1,3 +1,4 @@ +var Keypair = require('../lib/keypair'); var StealthMessage = require('../lib/expmt/stealthmessage'); var Stealthkey = require('../lib/expmt/stealthkey'); var StealthAddress = require('../lib/expmt/stealthAddress'); @@ -11,7 +12,7 @@ describe('StealthMessage', function() { var payloadKeypair = KDF.buf2keypair(new Buffer('key1')); var scanKeypair = KDF.buf2keypair(new Buffer('key2')); var fromKeypair = KDF.buf2keypair(new Buffer('key3')); - var enchex = 'f557994f16d0d628fa4fdb4ab3d7e0bc5f2754f20381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec9f86d081884c7d659a2feaa0c55ad01560ba2904d3bc8395b6c4a6f87648edb33db6a22170e5e26f340c7ba943169210234cd6a753ad13919b0ab7d678b47b5e7d63e452382de2c2590fb57ef048f7b3'; + var enchex = '3867dce7be22e8551512b25f329b51fd5fe8ecfe0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec9f86d081884c7d659a2feaa0c55ad015aca97de5af3a34a0f47eee0a1f7774dcb759187555003282bd23b047bceb5b2f2c1ee35b8f0be1fda7a41424f6cc8030559c8c32ea8cc812860f4c8123f1417a'; var encbuf = new Buffer(enchex, 'hex'); var ivbuf = Hash.sha256(new Buffer('test')).slice(0, 128 / 8); var sk = Stealthkey().set({payloadKeypair: payloadKeypair, scanKeypair: scanKeypair}); @@ -73,6 +74,14 @@ describe('StealthMessage', function() { StealthMessage.isForMe(encbuf, sk).should.equal(true); }); + it('should know that this message is for me even if my payloadPrivkey is not present', function() { + var sk2 = new Stealthkey(); + sk2.scanKeypair = sk.scanKeypair; + sk2.payloadKeypair = Keypair().set({pubkey: sk.payloadKeypair.pubkey}); + should.not.exist(sk2.payloadKeypair.privkey); + StealthMessage.isForMe(encbuf, sk2).should.equal(true); + }); + }); describe('#encrypt', function() { From 4a027e260a3397af17e2e9f31737356b9938d533 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 12 Sep 2014 17:49:01 -0700 Subject: [PATCH 179/280] Address().fromScript(script) It is convenient to be able to derive an address directly from a script for p2sh transactions. --- lib/address.js | 8 ++++++++ test/address.js | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/address.js b/lib/address.js index 43912c0..4b65eee 100644 --- a/lib/address.js +++ b/lib/address.js @@ -2,6 +2,7 @@ var base58check = require('./base58check'); var constants = require('./constants'); var Hash = require('./hash'); var Pubkey = require('./pubkey'); +var Script = require('./script'); function Address(obj) { if (!(this instanceof Address)) @@ -24,6 +25,13 @@ Address.prototype.fromPubkey = function(pubkey, networkstr) { return this; }; +Address.prototype.fromScript = function(script, networkstr) { + this.hashbuf = Hash.sha256ripemd160(script.toBuffer()); + this.networkstr = networkstr || 'mainnet'; + this.typestr = 'scripthash'; + return this; +}; + Address.prototype.fromString = function(str) { var buf = base58check.decode(str); if (buf.length !== 1 + 20) diff --git a/test/address.js b/test/address.js index 44dde17..697cc29 100644 --- a/test/address.js +++ b/test/address.js @@ -2,6 +2,7 @@ var should = require('chai').should(); var constants = require('../lib/constants'); var Pubkey = require('../lib/pubkey'); var Address = require('../lib/address'); +var Script = require('../lib/script'); describe('Address', function() { var pubkeyhash = new Buffer('3c3fa3d4adcaf8f52d5b1843975e122548269937', 'hex'); @@ -45,6 +46,22 @@ describe('Address', function() { }); + describe('#fromScript', function() { + + it('should make this address from a script', function() { + var script = Script().fromString("OP_CHECKMULTISIG"); + var address = Address().fromScript(script); + address.toString().should.equal('3BYmEwgV2vANrmfRymr1mFnHXgLjD6gAWm'); + }); + + it('should make this address from other script', function() { + var script = Script().fromString("OP_CHECKSIG OP_HASH160"); + var address = Address().fromScript(script); + address.toString().should.equal('347iRqVwks5r493N1rsLN4k9J7Ljg488W7'); + }); + + }); + describe('#fromString', function() { it('should derive from this known address string mainnet', function() { From fbfa64582ca85620ea0ef37d686408b3a4fd932b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 10:43:14 -0700 Subject: [PATCH 180/280] autogenerated fromkeypair if not present --- lib/expmt/ecies.js | 2 ++ test/ecies.js | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/lib/expmt/ecies.js b/lib/expmt/ecies.js index 2ddb852..30dfb54 100644 --- a/lib/expmt/ecies.js +++ b/lib/expmt/ecies.js @@ -11,6 +11,8 @@ var ECIES = function ECIES() { }; ECIES.encrypt = function(messagebuf, tokeypair, fromkeypair, ivbuf) { + if (!fromkeypair) + fromkeypair = Keypair().fromRandom(); var r = fromkeypair.privkey.bn; var R = fromkeypair.pubkey.point; var Rpubkey = fromkeypair.pubkey; diff --git a/test/ecies.js b/test/ecies.js index 213d21b..8231ae3 100644 --- a/test/ecies.js +++ b/test/ecies.js @@ -26,6 +26,11 @@ describe('#ECIES', function() { Buffer.isBuffer(encbuf).should.equal(true); }); + it('should return a buffer if fromkey is not present', function() { + var encbuf = ECIES.encrypt(messagebuf, tokey); + Buffer.isBuffer(encbuf).should.equal(true); + }); + }); describe('@decrypt', function() { From 8dc6a5023a0a790eafe799873401781f1368373e Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 10:52:32 -0700 Subject: [PATCH 181/280] test the case when no fromkeypair is used ...during encryption, and make sure you can still decrypt the message. --- test/ecies.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/ecies.js b/test/ecies.js index 8231ae3..2effee1 100644 --- a/test/ecies.js +++ b/test/ecies.js @@ -41,6 +41,12 @@ describe('#ECIES', function() { messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); }); + it('should decrypt that which was encrypted if fromkeypair was randomly generated', function() { + var encbuf = ECIES.encrypt(messagebuf, tokey); + var messagebuf2 = ECIES.decrypt(encbuf, tokey); + messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); + }); + }); }); From 385edbcc92f85bf30d5776ca1adc9bd79a0feba6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 11:19:18 -0700 Subject: [PATCH 182/280] simplify ECIES interface the toKeypair doesn't really need to be a keypair. upon encrypting, it merely needs to be a pubkey. and upon decrypting, it needs to be a privkey. --- lib/expmt/ecies.js | 9 +++++---- lib/expmt/stealthmessage.js | 4 ++-- test/ecies.js | 12 ++++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/expmt/ecies.js b/lib/expmt/ecies.js index 30dfb54..4eb4bff 100644 --- a/lib/expmt/ecies.js +++ b/lib/expmt/ecies.js @@ -3,6 +3,7 @@ var Keypair = require('../keypair'); var Point = require('../point'); var Hash = require('../hash'); var Pubkey = require('../pubkey'); +var Privkey = require('../privkey'); // http://en.wikipedia.org/wiki/Integrated_Encryption_Scheme var ECIES = function ECIES() { @@ -10,14 +11,14 @@ var ECIES = function ECIES() { return new ECIES(); }; -ECIES.encrypt = function(messagebuf, tokeypair, fromkeypair, ivbuf) { +ECIES.encrypt = function(messagebuf, topubkey, fromkeypair, ivbuf) { if (!fromkeypair) fromkeypair = Keypair().fromRandom(); var r = fromkeypair.privkey.bn; var R = fromkeypair.pubkey.point; var Rpubkey = fromkeypair.pubkey; var Rbuf = Rpubkey.toDER(true); - var KB = tokeypair.pubkey.point; + var KB = topubkey.point; var P = KB.mul(r); var S = P.getX(); var Sbuf = S.toBuffer({size: 32}); @@ -30,8 +31,8 @@ ECIES.encrypt = function(messagebuf, tokeypair, fromkeypair, ivbuf) { return encbuf; }; -ECIES.decrypt = function(encbuf, tokeypair) { - var kB = tokeypair.privkey.bn; +ECIES.decrypt = function(encbuf, toprivkey) { + var kB = toprivkey.bn; var frompubkey = Pubkey().fromDER(encbuf.slice(0, 33)); var R = frompubkey.point; var P = R.mul(kB); diff --git a/lib/expmt/stealthmessage.js b/lib/expmt/stealthmessage.js index de7b607..71efd61 100644 --- a/lib/expmt/stealthmessage.js +++ b/lib/expmt/stealthmessage.js @@ -63,13 +63,13 @@ StealthMessage.prototype.encrypt = function(ivbuf) { this.fromKeypair = Keypair().fromRandom(); var receivePubkey = this.toStealthAddress.getReceivePubkey(this.fromKeypair); this.receiveAddress = Address().fromPubkey(receivePubkey); - this.encbuf = ECIES.encrypt(this.messagebuf, Keypair().set({pubkey: receivePubkey}), this.fromKeypair, ivbuf); + this.encbuf = ECIES.encrypt(this.messagebuf, receivePubkey, this.fromKeypair, ivbuf); return this; }; StealthMessage.prototype.decrypt = function() { var receiveKeypair = this.toStealthkey.getReceiveKeypair(this.fromKeypair.pubkey); - this.messagebuf = ECIES.decrypt(this.encbuf, receiveKeypair); + this.messagebuf = ECIES.decrypt(this.encbuf, receiveKeypair.privkey); return this; }; diff --git a/test/ecies.js b/test/ecies.js index 2effee1..5e8078b 100644 --- a/test/ecies.js +++ b/test/ecies.js @@ -22,12 +22,12 @@ describe('#ECIES', function() { describe('@encrypt', function() { it('should return a buffer', function() { - var encbuf = ECIES.encrypt(messagebuf, tokey, fromkey); + var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey, fromkey); Buffer.isBuffer(encbuf).should.equal(true); }); it('should return a buffer if fromkey is not present', function() { - var encbuf = ECIES.encrypt(messagebuf, tokey); + var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey); Buffer.isBuffer(encbuf).should.equal(true); }); @@ -36,14 +36,14 @@ describe('#ECIES', function() { describe('@decrypt', function() { it('should decrypt that which was encrypted', function() { - var encbuf = ECIES.encrypt(messagebuf, tokey, fromkey); - var messagebuf2 = ECIES.decrypt(encbuf, tokey); + var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey, fromkey); + var messagebuf2 = ECIES.decrypt(encbuf, tokey.privkey); messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); }); it('should decrypt that which was encrypted if fromkeypair was randomly generated', function() { - var encbuf = ECIES.encrypt(messagebuf, tokey); - var messagebuf2 = ECIES.decrypt(encbuf, tokey); + var encbuf = ECIES.encrypt(messagebuf, tokey.pubkey); + var messagebuf2 = ECIES.decrypt(encbuf, tokey.privkey); messagebuf2.toString('hex').should.equal(messagebuf.toString('hex')); }); From 543762bc671c397b92aa609929ad9a71131ac0e9 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 11:32:39 -0700 Subject: [PATCH 183/280] fromBuffer convenience function for pubkey --- lib/pubkey.js | 4 ++++ test/pubkey.js | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/pubkey.js b/lib/pubkey.js index a0e0c99..4996247 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -29,6 +29,10 @@ Pubkey.prototype.fromPrivkey = function(privkey) { return this; }; +Pubkey.prototype.fromBuffer = function(buf) { + return this.fromDER(buf); +}; + Pubkey.prototype.fromDER = function(buf) { if (buf[0] == 0x04) { var xbuf = buf.slice(1, 33); diff --git a/test/pubkey.js b/test/pubkey.js index 03e7534..f3e3c1f 100644 --- a/test/pubkey.js +++ b/test/pubkey.js @@ -40,6 +40,31 @@ describe('Pubkey', function() { }); + describe('#fromBuffer', function() { + + it('should parse this uncompressed public key', function() { + var pk = new Pubkey(); + pk.fromBuffer(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex')); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + it('should parse this compressed public key', function() { + var pk = new Pubkey(); + pk.fromBuffer(new Buffer('031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + it('should throw an error on this invalid public key', function() { + var pk = new Pubkey(); + (function() { + pk.fromBuffer(new Buffer('091ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a', 'hex')); + }).should.throw(); + }); + + }); + describe('#fromDER', function() { it('should parse this uncompressed public key', function() { From adbb260e700e89967f064728c2e3c09efb5273ce Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 13:35:50 -0700 Subject: [PATCH 184/280] also have keys and messages --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e0686e..5ef74cb 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ property(s) that it modifies. ------------------------- Features over bitcore: -* Stealth addresses +* Stealth keys, addresses, message * Proper handling of reading and writing big varInts * Browserifiable * A proper point class From 9d5d13699332df0513c9168c1797c06a5277f9b6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 13:48:51 -0700 Subject: [PATCH 185/280] "function" not used --- lib/bufferreader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index f33ec90..15369fb 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -18,7 +18,7 @@ BufferReader.prototype.set = function(obj) { return this; }; -BufferReader.prototype.eof = function eof() { +BufferReader.prototype.eof = function() { return this.pos >= this.buf.length; }; From cf4668f5018733d1ec7bc1f85d039323fec7f1ef Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 13:56:42 -0700 Subject: [PATCH 186/280] typo --- lib/bufferwriter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bufferwriter.js b/lib/bufferwriter.js index 5bf5800..d813bcb 100644 --- a/lib/bufferwriter.js +++ b/lib/bufferwriter.js @@ -2,7 +2,7 @@ var BN = require('./bn'); var BufferWriter = function BufferWriter(obj) { if (!(this instanceof BufferWriter)) - return new BufferReader(obj); + return new BufferWriter(obj); if (obj) this.set(obj); else From 8e959426e25ed8e411332d2d10e14e49b90977ed Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 13:58:24 -0700 Subject: [PATCH 187/280] support reading varInt nums up to js precision Javascript only supports 64 bit floating points, which have uint precision up to Math.pow(2, 53). We now support reading variable sized numbers up to that size. If the number is bigger than that, then we need to use BN. --- lib/bufferreader.js | 7 ++++++- test/bufferreader.js | 13 +++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index 15369fb..27fa495 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -87,7 +87,12 @@ BufferReader.prototype.readVarInt = function() { case 0xFE: return this.readUInt32LE(); case 0xFF: - throw new Error('number too large to retain precision - use readVarIntBN'); + var bn = this.readUInt64LEBN(); + var n = bn.toNumber(); + if (n <= Math.pow(2, 53)) + return n; + else + throw new Error('number too large to retain precision - use readVarIntBN'); default: return first; } diff --git a/test/bufferreader.js b/test/bufferreader.js index 3bbce17..670f9d6 100644 --- a/test/bufferreader.js +++ b/test/bufferreader.js @@ -1,6 +1,7 @@ var BufferWriter = require('../lib/bufferwriter'); var BufferReader = require('../lib/bufferreader'); var should = require('chai').should(); +var BN = require('../lib/bn'); describe('BufferReader', function() { @@ -188,14 +189,22 @@ describe('BufferReader', function() { br.readVarInt().should.equal(50000); }); - it('should throw an error on a 9 byte varint', function() { - var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]); + it('should throw an error on a 9 byte varint over the javascript uint precision limit', function() { + var buf = BufferWriter().writeVarIntBN(BN(Math.pow(2, 54).toString())).concat(); var br = new BufferReader({buf: buf}); (function() { br.readVarInt(); }).should.throw('number too large to retain precision - use readVarIntBN'); }); + it('should not throw an error on a 9 byte varint not over the javascript uint precision limit', function() { + var buf = BufferWriter().writeVarIntBN(BN(Math.pow(2, 53).toString())).concat(); + var br = new BufferReader({buf: buf}); + (function() { + br.readVarInt(); + }).should.not.throw('number too large to retain precision - use readVarIntBN'); + }); + }); describe('#readVarIntBN', function() { From 3c668c9cf0bf3fc06d22eaa3d3628d6bc6819acf Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 14:19:08 -0700 Subject: [PATCH 188/280] readVarInt -> readVarIntNum "varInt" refers to the varInt object, which is not actually implemented yet. This function should therefore really be called readVarIntNum. --- lib/bufferreader.js | 2 +- test/bufferreader.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index 27fa495..21fea1a 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -79,7 +79,7 @@ BufferReader.prototype.readUInt64LEBN = function() { return bn; }; -BufferReader.prototype.readVarInt = function() { +BufferReader.prototype.readVarIntNum = function() { var first = this.readUInt8(); switch (first) { case 0xFD: diff --git a/test/bufferreader.js b/test/bufferreader.js index 670f9d6..2c9adf2 100644 --- a/test/bufferreader.js +++ b/test/bufferreader.js @@ -168,32 +168,32 @@ describe('BufferReader', function() { }); - describe('#readVarInt', function() { + describe('#readVarIntNum', function() { it('should read a 1 byte varint', function() { var buf = new Buffer([50]); var br = new BufferReader({buf: buf}); - br.readVarInt().should.equal(50); + br.readVarIntNum().should.equal(50); }); it('should read a 3 byte varint', function() { var buf = new Buffer([253, 253, 0]); var br = new BufferReader({buf: buf}); - br.readVarInt().should.equal(253); + br.readVarIntNum().should.equal(253); }); it('should read a 5 byte varint', function() { var buf = new Buffer([254, 0, 0, 0, 0]); buf.writeUInt32LE(50000, 1); var br = new BufferReader({buf: buf}); - br.readVarInt().should.equal(50000); + br.readVarIntNum().should.equal(50000); }); it('should throw an error on a 9 byte varint over the javascript uint precision limit', function() { var buf = BufferWriter().writeVarIntBN(BN(Math.pow(2, 54).toString())).concat(); var br = new BufferReader({buf: buf}); (function() { - br.readVarInt(); + br.readVarIntNum(); }).should.throw('number too large to retain precision - use readVarIntBN'); }); @@ -201,7 +201,7 @@ describe('BufferReader', function() { var buf = BufferWriter().writeVarIntBN(BN(Math.pow(2, 53).toString())).concat(); var br = new BufferReader({buf: buf}); (function() { - br.readVarInt(); + br.readVarIntNum(); }).should.not.throw('number too large to retain precision - use readVarIntBN'); }); From d363956ba1f2a27262691619cbc1853e4c482ab5 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 14:27:42 -0700 Subject: [PATCH 189/280] add readVarIntBuf function ...will be useful for new Varint class --- lib/bufferreader.js | 14 ++++++++++++++ test/bufferreader.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index 21fea1a..7ce44a3 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -98,6 +98,20 @@ BufferReader.prototype.readVarIntNum = function() { } }; +BufferReader.prototype.readVarIntBuf = function() { + var first = this.buf.readUInt8(this.pos); + switch (first) { + case 0xFD: + return this.buffer(1 + 2);; + case 0xFE: + return this.buffer(1 + 4); + case 0xFF: + return this.buffer(1 + 8); + default: + return this.buffer(1); + } +}; + BufferReader.prototype.readVarIntBN = function() { var first = this.readUInt8(); switch (first) { diff --git a/test/bufferreader.js b/test/bufferreader.js index 2c9adf2..862bd96 100644 --- a/test/bufferreader.js +++ b/test/bufferreader.js @@ -168,6 +168,35 @@ describe('BufferReader', function() { }); + describe('#readVarIntBuf', function() { + + it('should read a 1 byte varint', function() { + var buf = new Buffer([50]); + var br = new BufferReader({buf: buf}); + br.readVarIntBuf().length.should.equal(1); + }); + + it('should read a 3 byte varint', function() { + var buf = new Buffer([253, 253, 0]); + var br = new BufferReader({buf: buf}); + br.readVarIntBuf().length.should.equal(3); + }); + + it('should read a 5 byte varint', function() { + var buf = new Buffer([254, 0, 0, 0, 0]); + buf.writeUInt32LE(50000, 1); + var br = new BufferReader({buf: buf}); + br.readVarIntBuf().length.should.equal(5); + }); + + it('should read a 9 byte varint', function() { + var buf = BufferWriter().writeVarIntBN(BN(Math.pow(2, 54).toString())).concat(); + var br = new BufferReader({buf: buf}); + br.readVarIntBuf().length.should.equal(9); + }); + + }); + describe('#readVarIntNum', function() { it('should read a 1 byte varint', function() { From 30c96721a05e70dc70f4c2f4e4e241aa57a47d83 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 14:36:31 -0700 Subject: [PATCH 190/280] varIntNum --- lib/bufferwriter.js | 6 +++--- lib/message.js | 4 ++-- test/bufferwriter.js | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/bufferwriter.js b/lib/bufferwriter.js index d813bcb..1e1a6b7 100644 --- a/lib/bufferwriter.js +++ b/lib/bufferwriter.js @@ -71,8 +71,8 @@ BufferWriter.prototype.writeUInt64LEBN = function(bn) { return this; }; -BufferWriter.prototype.writeVarInt = function(n) { - var buf = BufferWriter.varIntBuf(n); +BufferWriter.prototype.writeVarIntNum = function(n) { + var buf = BufferWriter.varIntBufNum(n); this.write(buf); return this; }; @@ -83,7 +83,7 @@ BufferWriter.prototype.writeVarIntBN = function(bn) { return this; }; -BufferWriter.varIntBuf = function(n) { +BufferWriter.varIntBufNum = function(n) { var buf = undefined; if (n < 253) { buf = new Buffer(1); diff --git a/lib/message.js b/lib/message.js index 0536d43..8174e4e 100644 --- a/lib/message.js +++ b/lib/message.js @@ -29,9 +29,9 @@ Message.magicHash = function(messagebuf) { if (!Buffer.isBuffer(messagebuf)) throw new Error('messagebuf must be a buffer'); var bw = new BufferWriter(); - bw.writeVarInt(Message.magicBytes.length); + bw.writeVarIntNum(Message.magicBytes.length); bw.write(Message.magicBytes); - bw.writeVarInt(messagebuf.length); + bw.writeVarIntNum(messagebuf.length); bw.write(messagebuf); var buf = bw.concat(); diff --git a/test/bufferwriter.js b/test/bufferwriter.js index acbc8f1..802c831 100644 --- a/test/bufferwriter.js +++ b/test/bufferwriter.js @@ -111,25 +111,25 @@ describe('BufferWriter', function() { it('should write a 1 byte varInt', function() { var bw = new BufferWriter(); - bw.writeVarInt(1); + bw.writeVarIntNum(1); bw.concat().length.should.equal(1); }); it('should write a 3 byte varInt', function() { var bw = new BufferWriter(); - bw.writeVarInt(1000); + bw.writeVarIntNum(1000); bw.concat().length.should.equal(3); }); it('should write a 5 byte varInt', function() { var bw = new BufferWriter(); - bw.writeVarInt(Math.pow(2, 16 + 1)); + bw.writeVarIntNum(Math.pow(2, 16 + 1)); bw.concat().length.should.equal(5); }); it('should write a 9 byte varInt', function() { var bw = new BufferWriter(); - bw.writeVarInt(Math.pow(2, 32 + 1)); + bw.writeVarIntNum(Math.pow(2, 32 + 1)); bw.concat().length.should.equal(9); }); @@ -137,7 +137,7 @@ describe('BufferWriter', function() { var bw = new BufferWriter(); var n = Math.pow(2, 53); n.should.equal(n + 1); //javascript number precision limit - bw.writeVarInt(n); + bw.writeVarIntNum(n); var br = new BufferReader({buf: bw.concat()}); br.readVarIntBN().toNumber().should.equal(n); }); From 4bb9105b2d320c78dfac1d48a4716e2ad10da42c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 14:41:46 -0700 Subject: [PATCH 191/280] varInt -> varint I don't like having to capitalize things unnecessarily --- lib/bufferreader.js | 8 ++++---- lib/bufferwriter.js | 12 ++++++------ lib/message.js | 4 ++-- test/bufferreader.js | 42 +++++++++++++++++++++--------------------- test/bufferwriter.js | 42 +++++++++++++++++++++--------------------- 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index 7ce44a3..76f8b40 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -79,7 +79,7 @@ BufferReader.prototype.readUInt64LEBN = function() { return bn; }; -BufferReader.prototype.readVarIntNum = function() { +BufferReader.prototype.readVarintNum = function() { var first = this.readUInt8(); switch (first) { case 0xFD: @@ -92,13 +92,13 @@ BufferReader.prototype.readVarIntNum = function() { if (n <= Math.pow(2, 53)) return n; else - throw new Error('number too large to retain precision - use readVarIntBN'); + throw new Error('number too large to retain precision - use readVarintBN'); default: return first; } }; -BufferReader.prototype.readVarIntBuf = function() { +BufferReader.prototype.readVarintBuf = function() { var first = this.buf.readUInt8(this.pos); switch (first) { case 0xFD: @@ -112,7 +112,7 @@ BufferReader.prototype.readVarIntBuf = function() { } }; -BufferReader.prototype.readVarIntBN = function() { +BufferReader.prototype.readVarintBN = function() { var first = this.readUInt8(); switch (first) { case 0xFD: diff --git a/lib/bufferwriter.js b/lib/bufferwriter.js index 1e1a6b7..fa8e8cd 100644 --- a/lib/bufferwriter.js +++ b/lib/bufferwriter.js @@ -71,19 +71,19 @@ BufferWriter.prototype.writeUInt64LEBN = function(bn) { return this; }; -BufferWriter.prototype.writeVarIntNum = function(n) { - var buf = BufferWriter.varIntBufNum(n); +BufferWriter.prototype.writeVarintNum = function(n) { + var buf = BufferWriter.varintBufNum(n); this.write(buf); return this; }; -BufferWriter.prototype.writeVarIntBN = function(bn) { - var buf = BufferWriter.varIntBufBN(bn); +BufferWriter.prototype.writeVarintBN = function(bn) { + var buf = BufferWriter.varintBufBN(bn); this.write(buf); return this; }; -BufferWriter.varIntBufNum = function(n) { +BufferWriter.varintBufNum = function(n) { var buf = undefined; if (n < 253) { buf = new Buffer(1); @@ -105,7 +105,7 @@ BufferWriter.varIntBufNum = function(n) { return buf; }; -BufferWriter.varIntBufBN = function(bn) { +BufferWriter.varintBufBN = function(bn) { var buf = undefined; var n = bn.toNumber(); if (n < 253) { diff --git a/lib/message.js b/lib/message.js index 8174e4e..636a7da 100644 --- a/lib/message.js +++ b/lib/message.js @@ -29,9 +29,9 @@ Message.magicHash = function(messagebuf) { if (!Buffer.isBuffer(messagebuf)) throw new Error('messagebuf must be a buffer'); var bw = new BufferWriter(); - bw.writeVarIntNum(Message.magicBytes.length); + bw.writeVarintNum(Message.magicBytes.length); bw.write(Message.magicBytes); - bw.writeVarIntNum(messagebuf.length); + bw.writeVarintNum(messagebuf.length); bw.write(messagebuf); var buf = bw.concat(); diff --git a/test/bufferreader.js b/test/bufferreader.js index 862bd96..650389f 100644 --- a/test/bufferreader.js +++ b/test/bufferreader.js @@ -168,99 +168,99 @@ describe('BufferReader', function() { }); - describe('#readVarIntBuf', function() { + describe('#readVarintBuf', function() { it('should read a 1 byte varint', function() { var buf = new Buffer([50]); var br = new BufferReader({buf: buf}); - br.readVarIntBuf().length.should.equal(1); + br.readVarintBuf().length.should.equal(1); }); it('should read a 3 byte varint', function() { var buf = new Buffer([253, 253, 0]); var br = new BufferReader({buf: buf}); - br.readVarIntBuf().length.should.equal(3); + br.readVarintBuf().length.should.equal(3); }); it('should read a 5 byte varint', function() { var buf = new Buffer([254, 0, 0, 0, 0]); buf.writeUInt32LE(50000, 1); var br = new BufferReader({buf: buf}); - br.readVarIntBuf().length.should.equal(5); + br.readVarintBuf().length.should.equal(5); }); it('should read a 9 byte varint', function() { - var buf = BufferWriter().writeVarIntBN(BN(Math.pow(2, 54).toString())).concat(); + var buf = BufferWriter().writeVarintBN(BN(Math.pow(2, 54).toString())).concat(); var br = new BufferReader({buf: buf}); - br.readVarIntBuf().length.should.equal(9); + br.readVarintBuf().length.should.equal(9); }); }); - describe('#readVarIntNum', function() { + describe('#readVarintNum', function() { it('should read a 1 byte varint', function() { var buf = new Buffer([50]); var br = new BufferReader({buf: buf}); - br.readVarIntNum().should.equal(50); + br.readVarintNum().should.equal(50); }); it('should read a 3 byte varint', function() { var buf = new Buffer([253, 253, 0]); var br = new BufferReader({buf: buf}); - br.readVarIntNum().should.equal(253); + br.readVarintNum().should.equal(253); }); it('should read a 5 byte varint', function() { var buf = new Buffer([254, 0, 0, 0, 0]); buf.writeUInt32LE(50000, 1); var br = new BufferReader({buf: buf}); - br.readVarIntNum().should.equal(50000); + br.readVarintNum().should.equal(50000); }); it('should throw an error on a 9 byte varint over the javascript uint precision limit', function() { - var buf = BufferWriter().writeVarIntBN(BN(Math.pow(2, 54).toString())).concat(); + var buf = BufferWriter().writeVarintBN(BN(Math.pow(2, 54).toString())).concat(); var br = new BufferReader({buf: buf}); (function() { - br.readVarIntNum(); - }).should.throw('number too large to retain precision - use readVarIntBN'); + br.readVarintNum(); + }).should.throw('number too large to retain precision - use readVarintBN'); }); it('should not throw an error on a 9 byte varint not over the javascript uint precision limit', function() { - var buf = BufferWriter().writeVarIntBN(BN(Math.pow(2, 53).toString())).concat(); + var buf = BufferWriter().writeVarintBN(BN(Math.pow(2, 53).toString())).concat(); var br = new BufferReader({buf: buf}); (function() { - br.readVarIntNum(); - }).should.not.throw('number too large to retain precision - use readVarIntBN'); + br.readVarintNum(); + }).should.not.throw('number too large to retain precision - use readVarintBN'); }); }); - describe('#readVarIntBN', function() { + describe('#readVarintBN', function() { it('should read a 1 byte varint', function() { var buf = new Buffer([50]); var br = new BufferReader({buf: buf}); - br.readVarIntBN().toNumber().should.equal(50); + br.readVarintBN().toNumber().should.equal(50); }); it('should read a 3 byte varint', function() { var buf = new Buffer([253, 253, 0]); var br = new BufferReader({buf: buf}); - br.readVarIntBN().toNumber().should.equal(253); + br.readVarintBN().toNumber().should.equal(253); }); it('should read a 5 byte varint', function() { var buf = new Buffer([254, 0, 0, 0, 0]); buf.writeUInt32LE(50000, 1); var br = new BufferReader({buf: buf}); - br.readVarIntBN().toNumber().should.equal(50000); + br.readVarintBN().toNumber().should.equal(50000); }); it('should read a 9 byte varint', function() { var buf = Buffer.concat([new Buffer([255]), new Buffer('ffffffffffffffff', 'hex')]); var br = new BufferReader({buf: buf}); - br.readVarIntBN().toNumber().should.equal(Math.pow(2, 64)); + br.readVarintBN().toNumber().should.equal(Math.pow(2, 64)); }); }); diff --git a/test/bufferwriter.js b/test/bufferwriter.js index 802c831..7042533 100644 --- a/test/bufferwriter.js +++ b/test/bufferwriter.js @@ -107,66 +107,66 @@ describe('BufferWriter', function() { }); - describe('#writeVarInt', function() { + describe('#writeVarint', function() { - it('should write a 1 byte varInt', function() { + it('should write a 1 byte varint', function() { var bw = new BufferWriter(); - bw.writeVarIntNum(1); + bw.writeVarintNum(1); bw.concat().length.should.equal(1); }); - it('should write a 3 byte varInt', function() { + it('should write a 3 byte varint', function() { var bw = new BufferWriter(); - bw.writeVarIntNum(1000); + bw.writeVarintNum(1000); bw.concat().length.should.equal(3); }); - it('should write a 5 byte varInt', function() { + it('should write a 5 byte varint', function() { var bw = new BufferWriter(); - bw.writeVarIntNum(Math.pow(2, 16 + 1)); + bw.writeVarintNum(Math.pow(2, 16 + 1)); bw.concat().length.should.equal(5); }); - it('should write a 9 byte varInt', function() { + it('should write a 9 byte varint', function() { var bw = new BufferWriter(); - bw.writeVarIntNum(Math.pow(2, 32 + 1)); + bw.writeVarintNum(Math.pow(2, 32 + 1)); bw.concat().length.should.equal(9); }); - it('should read back the same value it wrote for a 9 byte varInt', function() { + it('should read back the same value it wrote for a 9 byte varint', function() { var bw = new BufferWriter(); var n = Math.pow(2, 53); n.should.equal(n + 1); //javascript number precision limit - bw.writeVarIntNum(n); + bw.writeVarintNum(n); var br = new BufferReader({buf: bw.concat()}); - br.readVarIntBN().toNumber().should.equal(n); + br.readVarintBN().toNumber().should.equal(n); }); }); - describe('#writeVarIntBN', function() { + describe('#writeVarintBN', function() { - it('should write a 1 byte varInt', function() { + it('should write a 1 byte varint', function() { var bw = new BufferWriter(); - bw.writeVarIntBN(BN(1)); + bw.writeVarintBN(BN(1)); bw.concat().length.should.equal(1); }); - it('should write a 3 byte varInt', function() { + it('should write a 3 byte varint', function() { var bw = new BufferWriter(); - bw.writeVarIntBN(BN(1000)); + bw.writeVarintBN(BN(1000)); bw.concat().length.should.equal(3); }); - it('should write a 5 byte varInt', function() { + it('should write a 5 byte varint', function() { var bw = new BufferWriter(); - bw.writeVarIntBN(BN(Math.pow(2, 16 + 1))); + bw.writeVarintBN(BN(Math.pow(2, 16 + 1))); bw.concat().length.should.equal(5); }); - it('should write a 9 byte varInt', function() { + it('should write a 9 byte varint', function() { var bw = new BufferWriter(); - bw.writeVarIntBN(BN(Math.pow(2, 32 + 1))); + bw.writeVarintBN(BN(Math.pow(2, 32 + 1))); bw.concat().length.should.equal(9); }); From 9cb31ebb6a7505858d9fc38953ec54a2580a711e Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 14:52:43 -0700 Subject: [PATCH 192/280] extra semicolon --- lib/bufferreader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index 76f8b40..3cd587f 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -102,7 +102,7 @@ BufferReader.prototype.readVarintBuf = function() { var first = this.buf.readUInt8(this.pos); switch (first) { case 0xFD: - return this.buffer(1 + 2);; + return this.buffer(1 + 2); case 0xFE: return this.buffer(1 + 4); case 0xFF: From 54e705f7e87aed71a287682771c7920bfd15b331 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 14:53:42 -0700 Subject: [PATCH 193/280] missing semicolon --- lib/bufferreader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index 3cd587f..ffac561 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -129,7 +129,7 @@ BufferReader.prototype.readVarintBN = function() { BufferReader.prototype.reverse = function() { var buf = new Buffer(this.buf.length); for (var i = 0; i < buf.length; i++) - buf[i] = this.buf[this.buf.length - 1 - i] + buf[i] = this.buf[this.buf.length - 1 - i]; this.buf = buf; return this; }; From f6a8878aa2b0aba9bdc8b7b24c8e501cf723e058 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 15:05:54 -0700 Subject: [PATCH 194/280] Varint class ...will be useful in transactions. Note that we already have a primitive understanding of Varints in the BufferReader and BufferWriter classes. However, the new Varint class is a varint object which actually depends on BufferReader and BufferWriter for reading and writing varints. This class is for keeping track of the raw buffer that is read in from a buffer. --- lib/varint.js | 47 ++++++++++++++++++++++++++++++ test/varint.js | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 lib/varint.js create mode 100644 test/varint.js diff --git a/lib/varint.js b/lib/varint.js new file mode 100644 index 0000000..5d8fc11 --- /dev/null +++ b/lib/varint.js @@ -0,0 +1,47 @@ +var BufferWriter = require('./bufferwriter'); +var BufferReader = require('./bufferreader'); +var BN = require('./bn'); + +var Varint = function Varint(buf) { + if (!(this instanceof Varint)) + return new Varint(buf); + if (Buffer.isBuffer(buf)) { + this.buf = buf; + } else if (typeof buf === 'number') { + var num = buf; + this.fromNumber(num); + } else if (buf) { + var obj = buf; + this.set(obj); + } +}; + +Varint.prototype.set = function(obj) { + this.buf = obj.buf || this.buf; + return this; +}; + +Varint.prototype.fromBuffer = function(buf) { + this.buf = buf; + return this; +}; + +Varint.prototype.fromBufferReader = function(br) { + this.buf = br.readVarintBuf(); + return this; +}; + +Varint.prototype.fromNumber = function(num) { + this.buf = BufferWriter().writeVarintNum(num).concat(); + return this; +}; + +Varint.prototype.toBuffer = function() { + return this.buf; +}; + +Varint.prototype.toNumber = function() { + return BufferReader(this.buf).readVarintNum(); +}; + +module.exports = Varint; diff --git a/test/varint.js b/test/varint.js new file mode 100644 index 0000000..86ad0c9 --- /dev/null +++ b/test/varint.js @@ -0,0 +1,79 @@ +var should = require('chai').should(); +var BufferReader = require('../lib/bufferreader'); +var BufferWriter = require('../lib/bufferwriter'); +var Varint = require('../lib/varint'); + +describe('Varint', function() { + + it('should make a new varint', function() { + var buf = new Buffer('00', 'hex'); + var varint = new Varint(buf); + should.exist(varint); + varint.buf.toString('hex').should.equal('00'); + varint = Varint(buf); + should.exist(varint); + varint.buf.toString('hex').should.equal('00'); + }); + + describe('#set', function() { + + it('should set a buffer', function() { + var buf = new Buffer('00', 'hex'); + var varint = Varint().set({buf: buf}); + varint.buf.toString('hex').should.equal('00'); + varint.set({}); + varint.buf.toString('hex').should.equal('00'); + }); + + }); + + describe('#fromBuffer', function() { + + it('should set a buffer', function() { + var buf = BufferWriter().writeVarintNum(5).concat(); + var varint = Varint().fromBuffer(buf); + varint.toNumber().should.equal(5); + }); + + }); + + describe('#fromBufferReader', function() { + + it('should set a buffer reader', function() { + var buf = BufferWriter().writeVarintNum(5).concat(); + var br = BufferReader(buf); + var varint = Varint().fromBufferReader(br); + varint.toNumber().should.equal(5); + }); + + }); + + describe('#fromNumber', function() { + + it('should set a number', function() { + var varint = Varint().fromNumber(5); + varint.toNumber().should.equal(5); + }); + + }); + + describe('#toBuffer', function() { + + it('should return a buffer', function() { + buf = BufferWriter().writeVarintNum(5).concat(); + var varint = Varint(buf); + varint.toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + }); + + describe('#toNumber', function() { + + it('should return a buffer', function() { + var varint = Varint(5); + varint.toNumber().should.equal(5); + }); + + }); + +}); From 3fa651e55371b246fa6a66fe2e78639965b4bb7b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 15:09:50 -0700 Subject: [PATCH 195/280] disable examples in browser --- test/examples.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/examples.js b/test/examples.js index ace8c26..4216361 100644 --- a/test/examples.js +++ b/test/examples.js @@ -1,3 +1,6 @@ +if (process.browser) + return; //examples are loaded from files, which doesn't work in the browser + var should = require('chai').should(); var fs = require('fs'); From 2f9bc222e5381e0df1e05567439798218fc2bd44 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 15:10:24 -0700 Subject: [PATCH 196/280] expose varint in bitcore bundle --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index e7889dc..ee1c702 100644 --- a/index.js +++ b/index.js @@ -21,6 +21,7 @@ bitcore.Pubkey = require('./lib/pubkey'); bitcore.Random = require('./lib/random'); bitcore.Script = require('./lib/script'); bitcore.Signature = require('./lib/signature'); +bitcore.Varint = require('./lib/varint'); //experimental, nonstandard, or unstable features bitcore.expmt = {}; From 4594cbb9e79124138c6016b155666722ababe1ea Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 15:29:39 -0700 Subject: [PATCH 197/280] add bn support to varint class --- lib/varint.js | 9 +++++++++ test/varint.js | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/varint.js b/lib/varint.js index 5d8fc11..c114ae2 100644 --- a/lib/varint.js +++ b/lib/varint.js @@ -31,6 +31,11 @@ Varint.prototype.fromBufferReader = function(br) { return this; }; +Varint.prototype.fromBN = function(bn) { + this.buf = BufferWriter().writeVarintBN(bn).concat(); + return this; +}; + Varint.prototype.fromNumber = function(num) { this.buf = BufferWriter().writeVarintNum(num).concat(); return this; @@ -40,6 +45,10 @@ Varint.prototype.toBuffer = function() { return this.buf; }; +Varint.prototype.toBN = function() { + return BufferReader(this.buf).readVarintBN(); +}; + Varint.prototype.toNumber = function() { return BufferReader(this.buf).readVarintNum(); }; diff --git a/test/varint.js b/test/varint.js index 86ad0c9..fc4ae25 100644 --- a/test/varint.js +++ b/test/varint.js @@ -1,3 +1,4 @@ +var BN = require('../lib/bn'); var should = require('chai').should(); var BufferReader = require('../lib/bufferreader'); var BufferWriter = require('../lib/bufferwriter'); @@ -48,6 +49,15 @@ describe('Varint', function() { }); + describe('#fromBN', function() { + + it('should set a number', function() { + var varint = Varint().fromNumber(BN(5)); + varint.toNumber().should.equal(5); + }); + + }); + describe('#fromNumber', function() { it('should set a number', function() { @@ -67,6 +77,15 @@ describe('Varint', function() { }); + describe('#toBN', function() { + + it('should return a buffer', function() { + var varint = Varint(5); + varint.toBN().toString().should.equal(BN(5).toString()); + }); + + }); + describe('#toNumber', function() { it('should return a buffer', function() { From 42130c78a0d3624f0c2eef20b29ace206ac9b4d9 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 17:08:09 -0700 Subject: [PATCH 198/280] Txin --- lib/txin.js | 58 +++++++++++++++++++++++++++++++++ test/txin.js | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 lib/txin.js create mode 100644 test/txin.js diff --git a/lib/txin.js b/lib/txin.js new file mode 100644 index 0000000..15a715f --- /dev/null +++ b/lib/txin.js @@ -0,0 +1,58 @@ +var BufferReader = require('./bufferreader'); +var BufferWriter = require('./bufferwriter'); +var Varint = require('./varint'); +var Script = require('./script'); + +var Txin = function Txin(txidbuf, txoutnum, varint, script, seqnum) { + if (!(this instanceof Txin)) + return new Txin(txidbuf, txoutnum, varint, script, seqnum); + if (Buffer.isBuffer(txidbuf)) { + this.txidbuf = txidbuf; + this.txoutnum = txoutnum; + this.varint = varint; + this.script = script; + this.seqnum = seqnum; + } else if (txidbuf) { + var obj = txidbuf; + this.set(obj); + } +}; + +Txin.prototype.set = function(obj) { + this.txidbuf = obj.txidbuf || this.txidbuf; + this.txoutnum = typeof obj.txoutnum !== 'undefined' ? obj.txoutnum : this.txoutnum; + this.varint = typeof obj.varint !== 'undefined' ? obj.varint : this.varint; + this.script = obj.script || this.script; + this.seqnum = typeof obj.seqnum !== 'undefined' ? obj.seqnum : this.seqnum; + return this; +}; + +Txin.prototype.fromBuffer = function(buf) { + return this.fromBufferReader(BufferReader(buf)); +}; + +Txin.prototype.fromBufferReader = function(br) { + this.txidbuf = br.buffer(32); + this.txoutnum = br.readUInt32LE(); + this.varint = Varint(br.readVarintBuf()); + this.script = Script().fromBuffer(br.buffer(this.varint.toNumber())); + this.seqnum = br.readUInt32LE(); + return this; +}; + +Txin.prototype.toBuffer = function() { + return this.toBufferWriter().concat(); +}; + +Txin.prototype.toBufferWriter = function(bw) { + if (!bw) + bw = new BufferWriter(); + bw.write(this.txidbuf); + bw.writeUInt32LE(this.txoutnum); + bw.write(this.varint.buf); + bw.write(this.script.toBuffer()); + bw.writeUInt32LE(this.seqnum); + return bw; +}; + +module.exports = Txin; diff --git a/test/txin.js b/test/txin.js new file mode 100644 index 0000000..ec3e1e8 --- /dev/null +++ b/test/txin.js @@ -0,0 +1,90 @@ +var should = require('chai').should(); +var Script = require('../lib/script'); +var Txin = require('../lib/txin'); +var Varint = require('../lib/varint'); +var BufferReader = require('../lib/bufferreader'); + +describe('Txin', function() { + + it('should make a new txin', function() { + var txin = new Txin(); + should.exist(txin); + txin = Txin(); + should.exist(txin); + }); + + var txidbuf = new Buffer(32); + txidbuf.fill(0); + var txoutnum = 0; + var script = Script().fromString("OP_CHECKMULTISIG"); + var varint = Varint(script.toBuffer().length); + var seqnum = 0; + var txin = Txin().set({ + txidbuf: txidbuf, + txoutnum: txoutnum, + varint: varint, + script: script, + seqnum: seqnum + }); + + describe('#set', function() { + + it('should set these vars', function() { + var txin = Txin().set({ + txidbuf: txidbuf, + txoutnum: txoutnum, + varint: varint, + script: script, + seqnum: seqnum + }); + should.exist(txin.txidbuf); + should.exist(txin.txoutnum); + should.exist(txin.varint); + should.exist(txin.script); + should.exist(txin.seqnum); + }); + + }); + + describe('#fromBuffer', function() { + + it('should convert this known buffer', function() { + var hex = '00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000'; + var buf = new Buffer(hex, 'hex'); + var txin = Txin().fromBuffer(buf); + txin.varint.toNumber().should.equal(1); + txin.script.toString().should.equal('OP_CHECKMULTISIG'); + }); + + }); + + describe('#fromBufferReader', function() { + + it('should convert this known buffer', function() { + var hex = '00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000'; + var buf = new Buffer(hex, 'hex'); + var br = BufferReader(buf); + var txin = Txin().fromBufferReader(br); + txin.varint.toNumber().should.equal(1); + txin.script.toString().should.equal('OP_CHECKMULTISIG'); + }); + + }); + + describe('#toBuffer', function() { + + it('should convert this known buffer', function() { + txin.toBuffer().toString('hex').should.equal('00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000'); + }); + + }); + + describe('#toBufferWriter', function() { + + it('should convert this known buffer', function() { + txin.toBufferWriter().concat().toString('hex').should.equal('00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000'); + }); + + }); + +}); From ced35a07eb2790bdab7fa65c7242e17ea4426dd6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 15 Sep 2014 18:38:21 -0700 Subject: [PATCH 199/280] Txout --- lib/txout.js | 54 +++++++++++++++++++++++++++++++++++ test/txout.js | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 lib/txout.js create mode 100644 test/txout.js diff --git a/lib/txout.js b/lib/txout.js new file mode 100644 index 0000000..9ccd082 --- /dev/null +++ b/lib/txout.js @@ -0,0 +1,54 @@ +var BN = require('./bn'); +var BufferReader = require('./bufferreader'); +var BufferWriter = require('./bufferwriter'); +var Varint = require('./varint'); +var Script = require('./script'); + +var Txout = function Txout(valuebn, varint, script) { + if (!(this instanceof Txout)) + return new Txout(valuebn, varint, script); + if (valuebn instanceof BN) { + this.set({ + valuebn: valuebn, + varint: varint, + script: script + }); + } else if (valuebn) { + var obj = valuebn; + this.set(obj); + } +}; + +Txout.prototype.set = function(obj) { + this.valuebn = obj.valuebn || this.valuebn; + this.varint = obj.varint || this.varint; + this.script = obj.script || this.script; + return this; +}; + +Txout.prototype.fromBuffer = function(buf) { + return this.fromBufferReader(BufferReader(buf)); +}; + +Txout.prototype.fromBufferReader = function(br) { + this.valuebn = br.readUInt64LEBN(); + this.varint = Varint(br.readVarintNum()); + this.script = Script().fromBuffer(br.buffer(this.varint.toNumber())); + return this; +}; + +Txout.prototype.toBuffer = function() { + var bw = new BufferWriter(); + return this.toBufferWriter(bw).concat(); +}; + +Txout.prototype.toBufferWriter = function(bw) { + if (!bw) + bw = new BufferWriter(); + bw.writeUInt64LEBN(this.valuebn); + bw.write(this.varint.buf); + bw.write(this.script.toBuffer()); + return bw; +}; + +module.exports = Txout; diff --git a/test/txout.js b/test/txout.js new file mode 100644 index 0000000..cfc05fc --- /dev/null +++ b/test/txout.js @@ -0,0 +1,78 @@ +var should = require('chai').should(); +var BN = require('../lib/bn'); +var Txout = require('../lib/txout'); +var Script = require('../lib/script'); +var Varint = require('../lib/varint'); +var BufferReader = require('../lib/bufferreader'); +var BufferWriter = require('../lib/bufferwriter'); + +describe('Txout', function() { + + it('should make a new txout', function() { + var txout = new Txout(); + should.exist(txout); + txout = Txout(); + should.exist(txout); + }); + + var valuebn = BN(5); + var script = Script().fromString("OP_CHECKMULTISIG"); + var varint = Varint(script.toBuffer().length); + var txout = new Txout().set({ + valuebn: valuebn, + varint: varint, + script: script + }); + + describe('#set', function() { + + it('should set this object', function() { + var txout = new Txout().set({ + valuebn: valuebn, + varint: varint, + script: script + }); + should.exist(txout.valuebn); + should.exist(txout.varint); + should.exist(txout.script); + }); + + }); + + describe('#fromBuffer', function() { + + it('should make this txin from this known buffer', function() { + var txout = Txout().fromBuffer(new Buffer('050000000000000001ae', 'hex')); + txout.toBuffer().toString('hex').should.equal('050000000000000001ae'); + }); + + }); + + describe('#fromBufferReader', function() { + + it('should make this txin from this known buffer', function() { + var txout = Txout().fromBufferReader(BufferReader(new Buffer('050000000000000001ae', 'hex'))); + txout.toBuffer().toString('hex').should.equal('050000000000000001ae'); + }); + + }); + + describe('#toBuffer', function() { + + it('should output this known buffer', function() { + var txout = Txout().fromBufferReader(BufferReader(new Buffer('050000000000000001ae', 'hex'))); + txout.toBuffer().toString('hex').should.equal('050000000000000001ae'); + }); + + }); + + describe('#toBufferWriter', function() { + + it('should output this known buffer', function() { + var txout = Txout().fromBufferReader(BufferReader(new Buffer('050000000000000001ae', 'hex'))); + txout.toBufferWriter().concat().toString('hex').should.equal('050000000000000001ae'); + }); + + }); + +}); From 3daeabaf30baf69a7af09e2c7a308f9f39596767 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 16 Sep 2014 10:10:06 -0700 Subject: [PATCH 200/280] "undefined"s are unnecessary --- lib/ecdsa.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index f6bc3d3..14eee00 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -13,11 +13,11 @@ var ECDSA = function ECDSA(obj) { }; ECDSA.prototype.set = function(obj) { - this.hashbuf = obj.hashbuf || this.hashbuf || undefined; - this.keypair = obj.keypair || this.keypair || undefined; - this.sig = obj.sig || this.sig || undefined; - this.k = obj.k || this.k || undefined; - this.verified = obj.verified || this.verified || undefined; + this.hashbuf = obj.hashbuf || this.hashbuf; + this.keypair = obj.keypair || this.keypair; + this.sig = obj.sig || this.sig; + this.k = obj.k || this.k; + this.verified = obj.verified || this.verified; return this; }; From ed335f35f778225b9a0bedb0e5a2922284100f5a Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 16 Sep 2014 11:33:49 -0700 Subject: [PATCH 201/280] throw error if hashbuf is not 32 bytes --- lib/ecdsa.js | 3 +++ test/ecdsa.js | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 14eee00..b22c409 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -156,6 +156,9 @@ ECDSA.prototype.sign = function() { if (!hashbuf || !privkey || !d) throw new Error('invalid parameters'); + if (!Buffer.isBuffer(hashbuf) || hashbuf.length !== 32) + throw new Error('hashbuf must be a 32 byte buffer'); + var N = Point.getN(); var G = Point.getG(); var e = BN().fromBuffer(hashbuf); diff --git a/test/ecdsa.js b/test/ecdsa.js index c285a4d..0291e21 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -144,6 +144,17 @@ describe("ECDSA", function() { ecdsa.verify().should.equal(true); }); + it('should should throw an error if hashbuf is not 32 bytes', function() { + var ecdsa2 = ECDSA().set({ + hashbuf: ecdsa.hashbuf.slice(0, 31), + keypair: ecdsa.keypair + }); + ecdsa2.randomK(); + (function() { + ecdsa2.sign(); + }).should.throw('hashbuf must be a 32 byte buffer'); + }); + }); describe('#signRandomK', function() { From 6e9755b00916cd294aee73ca19693246e1815d1a Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 16 Sep 2014 11:34:28 -0700 Subject: [PATCH 202/280] sign, verify convenience functions --- lib/ecdsa.js | 15 +++++++++++++++ test/ecdsa.js | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index b22c409..f33ea51 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -198,4 +198,19 @@ ECDSA.prototype.verify = function() { return false; }; +ECDSA.sign = function(hashbuf, keypair) { + return ECDSA().set({ + hashbuf: hashbuf, + keypair: keypair + }).signRandomK(); +}; + +ECDSA.verify = function(hashbuf, sig, pubkey) { + return ECDSA().set({ + hashbuf: hashbuf, + sig: sig, + keypair: Keypair().set({pubkey: pubkey}) + }).verify(); +}; + module.exports = ECDSA; diff --git a/test/ecdsa.js b/test/ecdsa.js index 0291e21..bf04979 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -190,4 +190,24 @@ describe("ECDSA", function() { }); + describe('@sign', function() { + + it('should produce a signature', function() { + var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.keypair); + (sig instanceof Signature).should.equal(true); + }); + + }); + + describe('@verify', function() { + + it('should verify a valid signature, and unverify an invalid signature', function() { + var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.keypair); + ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.keypair.pubkey).should.equal(true); + var fakesig = Signature(sig.r.add(1), sig.s); + ECDSA.verify(ecdsa.hashbuf, fakesig, ecdsa.keypair.pubkey).should.equal(false); + }); + + }); + }); From 6c42969d01e855cc7959f14e0782e8dbf4fc0718 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 16 Sep 2014 11:44:51 -0700 Subject: [PATCH 203/280] improve error message --- lib/ecdsa.js | 2 +- test/ecdsa.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ecdsa.js b/lib/ecdsa.js index f33ea51..ddb4af6 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -114,7 +114,7 @@ ECDSA.prototype.sig2pubkey = function() { ECDSA.prototype.sigError = function() { if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 32) - return 'Invalid hash'; + return 'hashbuf must be a 32 byte buffer'; try { this.keypair.pubkey.validate(); diff --git a/test/ecdsa.js b/test/ecdsa.js index bf04979..5de755e 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -105,7 +105,7 @@ describe("ECDSA", function() { it('should return an error if the hash is invalid', function() { var ecdsa = new ECDSA(); - ecdsa.sigError().should.equal('Invalid hash'); + ecdsa.sigError().should.equal('hashbuf must be a 32 byte buffer'); }); it('should return an error if the pubkey is invalid', function() { From 03291f9f8919943335b6dc1211423dc0d8fe5d6d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 16 Sep 2014 11:54:38 -0700 Subject: [PATCH 204/280] add ECDSA example --- examples/ecdsa.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 examples/ecdsa.js diff --git a/examples/ecdsa.js b/examples/ecdsa.js new file mode 100644 index 0000000..c0bc155 --- /dev/null +++ b/examples/ecdsa.js @@ -0,0 +1,21 @@ +var ECDSA = require('../lib/ecdsa'); +var Keypair = require('../lib/keypair'); +var Hash = require('../lib/hash'); + +//ECDSA is the signature algorithm used in bitcoin + +//start with a keypair that you will use for signing +var keypair = Keypair().fromRandom(); + +//a message to be signed (normally you would have the hash of a transaction) +var messagebuf = new Buffer('This is a message I would like to sign'); + +//calculate a 32 byte hash for use in ECDSA. one way to do that is sha256. +var hashbuf = Hash.sha256(messagebuf); + +var sig = ECDSA.sign(hashbuf, keypair); + +//Anyone with the public key can verify +var pubkey = keypair.pubkey; +console.log('Valid signature? ' + ECDSA.verify(hashbuf, sig, pubkey)); + From 5b25679e15058cd6b9021b0213afab218d540ec0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 16 Sep 2014 14:35:26 -0700 Subject: [PATCH 205/280] Transaction --- lib/transaction.js | 77 +++++++++++++++++++++++++++++++++++++++++ test/transaction.js | 84 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 lib/transaction.js create mode 100644 test/transaction.js diff --git a/lib/transaction.js b/lib/transaction.js new file mode 100644 index 0000000..51c27a1 --- /dev/null +++ b/lib/transaction.js @@ -0,0 +1,77 @@ +var Txin = require('./txin'); +var Txout = require('./txout'); +var BufferWriter = require('./bufferwriter'); +var BufferReader = require('./bufferreader'); +var Varint = require('./varint'); + +var Transaction = function Transaction(version, txinsvarint, txins, txoutsvarint, txouts, nlocktime) { + if (!(this instanceof Transaction)) + return new Transaction(version, txinsvarint, txins, txoutsvarint, txouts, nlocktime); + if (typeof version === 'number') { + this.set({ + version: version, + txinsvarint: txinsvarint, + txins: txins, + txoutsvarint: txoutsvarint, + txouts: txouts, + nlocktime: nlocktime + }); + } else if (version) { + var obj = version; + this.set(obj); + } +}; + +Transaction.prototype.set = function(obj) { + this.version = typeof obj.version !== 'undefined' ? obj.version : this.version; + this.txinsvarint = obj.txinsvarint || this.txinsvarint; + this.txins = obj.txins || this.txins; + this.txoutsvarint = obj.txoutsvarint || this.txoutsvarint; + this.txouts = obj.txouts || this.txouts; + this.nlocktime = typeof obj.nlocktime !== 'undefined' ? obj.nlocktime : this.nlocktime; + return this; +}; + +Transaction.prototype.fromBuffer = function(buf) { + return this.fromBufferReader(BufferReader(buf)); +}; + +Transaction.prototype.fromBufferReader = function(br) { + this.version = br.readUInt32LE(); + this.txinsvarint = Varint(br.readVarintBuf()); + var txinsnum = this.txinsvarint.toNumber(); + this.txins = []; + for (var i = 0; i < txinsnum; i++) { + this.txins.push(Txin().fromBufferReader(br)); + } + this.txoutsvarint = Varint(br.readVarintBuf()); + var txoutsnum = this.txoutsvarint.toNumber(); + this.txouts = []; + for (var i = 0; i < txoutsnum; i++) { + this.txouts.push(Txout().fromBufferReader(br)); + } + this.nlocktime = br.readUInt32LE(); + return this; +}; + +Transaction.prototype.toBuffer = function() { + return this.toBufferWriter().concat(); +}; + +Transaction.prototype.toBufferWriter = function(bw) { + if (!bw) + bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.write(this.txinsvarint.buf); + for (var i = 0; i < this.txins.length; i++) { + this.txins[i].toBufferWriter(bw); + } + bw.write(this.txoutsvarint.buf) + for (var i = 0; i < this.txouts.length; i++) { + this.txouts[i].toBufferWriter(bw); + } + bw.writeUInt32LE(this.nlocktime); + return bw; +}; + +module.exports = Transaction; diff --git a/test/transaction.js b/test/transaction.js new file mode 100644 index 0000000..13f23ca --- /dev/null +++ b/test/transaction.js @@ -0,0 +1,84 @@ +var Varint = require('../lib/varint'); +var Transaction = require('../lib/transaction'); +var Txin = require('../lib/txin'); +var Txout = require('../lib/txout'); +var should = require('chai').should(); +var BufferReader = require('../lib/bufferreader'); +var BufferWriter = require('../lib/bufferwriter'); + +describe('Transaction', function() { + + it('should make a new transaction', function() { + var tx = new Transaction(); + should.exist(tx); + tx = Transaction(); + should.exist(tx); + }); + + var txin = Txin().fromBuffer(new Buffer('00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000', 'hex')); + var txout = Txout().fromBuffer(new Buffer('050000000000000001ae', 'hex')); + var tx = Transaction().set({ + version: 0, + txinsvarint: Varint(1), + txins: [txin], + txoutsvarint: Varint(1), + txouts: [txout], + nlocktime: 0 + }); + var txhex = '000000000100000000000000000000000000000000000000000000000000000000000000000000000001ae0000000001050000000000000001ae00000000'; + var txbuf = new Buffer(txhex, 'hex'); + + describe('#set', function() { + + it('should set all the basic parameters', function() { + var tx = Transaction().set({ + version: 0, + txinsvarint: Varint(1), + txins: [txin], + txoutsvarint: Varint(1), + txouts: [txout], + nlocktime: 0 + }); + should.exist(tx.version); + should.exist(tx.txinsvarint); + should.exist(tx.txins); + should.exist(tx.txoutsvarint); + should.exist(tx.txouts); + should.exist(tx.nlocktime); + }); + + }); + + describe('#fromBuffer', function() { + + it('should recover from this known tx', function() { + Transaction().fromBuffer(txbuf).toBuffer().toString('hex').should.equal(txhex); + }); + + }); + + describe('#fromBufferReader', function() { + + it('should recover from this known tx', function() { + Transaction().fromBufferReader(BufferReader(txbuf)).toBuffer().toString('hex').should.equal(txhex); + }); + + }); + + describe('#toBuffer', function() { + + it('should produce this known tx', function() { + Transaction().fromBuffer(txbuf).toBuffer().toString('hex').should.equal(txhex); + }); + + }); + + describe('#toBufferWriter', function() { + + it('should produce this known tx', function() { + Transaction().fromBuffer(txbuf).toBufferWriter().concat().toString('hex').should.equal(txhex); + }); + + }); + +}); From 7ea66e032f77ad6ed24f0a3c2603a62c556bc12d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 16 Sep 2014 14:39:51 -0700 Subject: [PATCH 206/280] add real-world tx to transaction test --- test/transaction.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/transaction.js b/test/transaction.js index 13f23ca..685c33f 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -28,6 +28,10 @@ describe('Transaction', function() { var txhex = '000000000100000000000000000000000000000000000000000000000000000000000000000000000001ae0000000001050000000000000001ae00000000'; var txbuf = new Buffer(txhex, 'hex'); + var tx2idhex = '8c9aa966d35bfeaf031409e0001b90ccdafd8d859799eb945a3c515b8260bcf2'; + var tx2hex = '01000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000'; + var tx2buf = new Buffer(tx2hex, 'hex'); + describe('#set', function() { it('should set all the basic parameters', function() { @@ -55,6 +59,10 @@ describe('Transaction', function() { Transaction().fromBuffer(txbuf).toBuffer().toString('hex').should.equal(txhex); }); + it('should recover from this known tx from the blockchain', function() { + Transaction().fromBuffer(tx2buf).toBuffer().toString('hex').should.equal(tx2hex); + }); + }); describe('#fromBufferReader', function() { From 0d180810defd2fc0b63ec21cb7e307c42a5ae949 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 16 Sep 2014 17:28:00 -0700 Subject: [PATCH 207/280] Blockheader --- lib/blockheader.js | 62 ++++++++++++++++++++++++++++++++ test/blockheader.js | 88 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 lib/blockheader.js create mode 100644 test/blockheader.js diff --git a/lib/blockheader.js b/lib/blockheader.js new file mode 100644 index 0000000..cc17ea6 --- /dev/null +++ b/lib/blockheader.js @@ -0,0 +1,62 @@ +var BufferReader = require('./bufferreader'); +var BufferWriter = require('./bufferwriter'); + +var Blockheader = function Blockheader(version, prevblockidbuf, merklerootbuf, time, bits, nonce) { + if (!(this instanceof Blockheader)) + return new Blockheader(version, prevblockidbuf, merklerootbuf, time, bits, nonce); + if (typeof version === 'number') { + this.set({ + version: version, + prevblockidbuf: prevblockidbuf, + merklerootbuf: merklerootbuf, + time: time, + bits: bits, + nonce: nonce + }); + } else if (version) { + var obj = version; + this.set(obj); + } +} + +Blockheader.prototype.set = function(obj) { + this.version = typeof obj.version !== 'undefined' ? obj.version : this.version; + this.prevblockidbuf = obj.prevblockidbuf || this.prevblockidbuf; + this.merklerootbuf = obj.merklerootbuf || this.merklerootbuf; + this.time = typeof obj.time !== 'undefined' ? obj.time : this.time; + this.bits = typeof obj.bits !== 'undefined' ? obj.bits : this.bits; + this.nonce = typeof obj.nonce !== 'undefined' ? obj.nonce : this.nonce; + return this; +}; + +Blockheader.prototype.fromBuffer = function(buf) { + return this.fromBufferReader(BufferReader(buf)); +}; + +Blockheader.prototype.fromBufferReader = function(br) { + this.version = br.readUInt32LE(); + this.prevblockidbuf = br.buffer(32); + this.merklerootbuf = br.buffer(32); + this.time = br.readUInt32LE(); + this.bits = br.readUInt32LE(); + this.nonce = br.readUInt32LE(); + return this; +}; + +Blockheader.prototype.toBuffer = function() { + return this.toBufferWriter().concat(); +}; + +Blockheader.prototype.toBufferWriter = function(bw) { + if (!bw) + bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.write(this.prevblockidbuf); + bw.write(this.merklerootbuf); + bw.writeUInt32LE(this.time); + bw.writeUInt32LE(this.bits); + bw.writeUInt32LE(this.nonce); + return bw; +}; + +module.exports = Blockheader; diff --git a/test/blockheader.js b/test/blockheader.js new file mode 100644 index 0000000..03a335f --- /dev/null +++ b/test/blockheader.js @@ -0,0 +1,88 @@ +var Blockheader = require('../lib/blockheader'); +var BufferWriter = require('../lib/bufferwriter'); +var BufferReader = require('../lib/bufferreader'); +var should = require('chai').should(); + +describe('Blockheader', function() { + + it('should make a new blockheader', function() { + var blockheader = new Blockheader(); + should.exist(blockheader); + blockheader = Blockheader(); + should.exist(blockheader); + }); + + var bh = new Blockheader(); + var version = 1; + var prevblockidbuf = new Buffer(32); + prevblockidbuf.fill(5); + var merklerootbuf = new Buffer(32); + merklerootbuf.fill(9); + var time = 2; + var bits = 3; + var nonce = 4; + bh.set({ + version: version, + prevblockidbuf: prevblockidbuf, + merklerootbuf: merklerootbuf, + time: time, + bits: bits, + nonce: nonce + }); + bhhex = '0100000005050505050505050505050505050505050505050505050505050505050505050909090909090909090909090909090909090909090909090909090909090909020000000300000004000000'; + bhbuf = new Buffer(bhhex, 'hex'); + + describe('#set', function() { + + it('should set all the variables', function() { + bh.set({ + version: version, + prevblockidbuf: prevblockidbuf, + merklerootbuf: merklerootbuf, + time: time, + bits: bits, + nonce: nonce + }); + should.exist(bh.version); + should.exist(bh.prevblockidbuf); + should.exist(bh.merklerootbuf); + should.exist(bh.time); + should.exist(bh.bits); + should.exist(bh.nonce); + }); + + }); + + describe('#fromBuffer', function() { + + it('should parse this known buffer', function() { + Blockheader().fromBuffer(bhbuf).toBuffer().toString('hex').should.equal(bhhex); + }); + + }); + + describe('#fromBufferReader', function() { + + it('should parse this known buffer', function() { + Blockheader().fromBufferReader(BufferReader(bhbuf)).toBuffer().toString('hex').should.equal(bhhex); + }); + + }); + + describe('#toBuffer', function() { + + it('should output this known buffer', function() { + Blockheader().fromBuffer(bhbuf).toBuffer().toString('hex').should.equal(bhhex); + }); + + }); + + describe('#toBufferWriter', function() { + + it('should output this known buffer', function() { + Blockheader().fromBuffer(bhbuf).toBufferWriter().concat().toString('hex').should.equal(bhhex); + }); + + }); + +}); From 8a52e6c3168d25b620d6724c072933bad7fc71c4 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 16 Sep 2014 18:42:11 -0700 Subject: [PATCH 208/280] Block --- lib/block.js | 67 ++++++++++++++++++++++++++++++++++++ test/block.js | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 lib/block.js create mode 100644 test/block.js diff --git a/lib/block.js b/lib/block.js new file mode 100644 index 0000000..35fc2d7 --- /dev/null +++ b/lib/block.js @@ -0,0 +1,67 @@ +var Transaction = require('./transaction'); +var BufferReader = require('./bufferreader'); +var BufferWriter = require('./bufferwriter'); +var Blockheader = require('./blockheader'); +var Varint = require('./varint'); + +var Block = function Block(magicnum, blocksize, blockheader, txsvarint, txs) { + if (!(this instanceof Block)) + return new Block(magicnum, blocksize, blockheader, txsvarint, txs); + if (typeof magicnum === 'number') { + this.set({ + magicnum: magicnum, + blocksize: blocksize, + blockheader: blockheader, + txsvarint: txsvarint, + txs: txs + }); + } else if (magicnum) { + var obj = magicnum; + } +}; + +Block.prototype.set = function(obj) { + this.magicnum = typeof obj.magicnum !== 'undefined' ? obj.magicnum : this.magicnum; + this.blocksize = typeof obj.blocksize !== 'undefined' ? obj.blocksize : this.blocksize; + this.blockheader = obj.blockheader || this.blockheader; + this.txsvarint = obj.txsvarint || this.txsvarint; + this.txs = obj.txs || this.txs; + return this; +}; + +Block.prototype.fromBuffer = function(buf) { + return this.fromBufferReader(BufferReader(buf)); +}; + +Block.prototype.fromBufferReader = function(br) { + this.magicnum = br.readUInt32LE(); + this.blocksize = br.readUInt32LE(); + this.blockheader = Blockheader().fromBufferReader(br); + this.txsvarint = Varint(br.readVarintBuf()); + var txslen = this.txsvarint.toNumber(); + this.txs = []; + for (var i = 0; i < txslen; i++) { + this.txs.push(Transaction().fromBufferReader(br)); + } + return this; +}; + +Block.prototype.toBuffer = function() { + return this.toBufferWriter().concat(); +}; + +Block.prototype.toBufferWriter = function(bw) { + if (!bw) + bw = new BufferWriter(); + bw.writeUInt32LE(this.magicnum); + bw.writeUInt32LE(this.blocksize); + bw.write(this.blockheader.toBuffer()); + bw.write(this.txsvarint.buf); + var txslen = this.txsvarint.toNumber(); + for (var i = 0; i < txslen; i++) { + this.txs[i].toBufferWriter(bw); + } + return bw; +}; + +module.exports = Block; diff --git a/test/block.js b/test/block.js new file mode 100644 index 0000000..b563254 --- /dev/null +++ b/test/block.js @@ -0,0 +1,94 @@ +var Blockheader = require('../lib/blockheader'); +var Block = require('../lib/block'); +var BufferWriter = require('../lib/bufferwriter'); +var BufferReader = require('../lib/bufferreader'); +var Varint = require('../lib/varint'); +var should = require('chai').should(); +var Transaction = require('../lib/transaction'); + +describe('Block', function() { + + it('should make a new block', function() { + var block = new Block(); + should.exist(block); + block = Block(); + should.exist(block); + }); + + var txidhex = '8c9aa966d35bfeaf031409e0001b90ccdafd8d859799eb945a3c515b8260bcf2'; + var txhex = '01000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000'; + var txbuf = new Buffer(txhex, 'hex'); + var tx = Transaction().fromBuffer(txbuf); + var magicnum = 0xd9b4bef9; + var blocksize = 50; + bhhex = '0100000005050505050505050505050505050505050505050505050505050505050505050909090909090909090909090909090909090909090909090909090909090909020000000300000004000000'; + bhbuf = new Buffer(bhhex, 'hex'); + var bh = Blockheader().fromBuffer(bhbuf); + var txsvarint = Varint(1); + var txs = [Transaction().fromBuffer(txbuf)]; + var block = Block().set({ + magicnum: magicnum, + blocksize: blocksize, + blockheader: bh, + txsvarint: txsvarint, + txs: txs + }); + var blockhex = 'f9beb4d93200000001000000050505050505050505050505050505050505050505050505050505050505050509090909090909090909090909090909090909090909090909090909090909090200000003000000040000000101000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000'; + var blockbuf = new Buffer(blockhex, 'hex'); + + describe('#set', function() { + + it('should set these known values', function() { + var block = Block().set({ + magicnum: magicnum, + blocksize: blocksize, + blockheader: bh, + txsvarint: txsvarint, + txs: txs + }); + should.exist(block.magicnum); + should.exist(block.blocksize); + should.exist(block.blockheader); + should.exist(block.txsvarint); + should.exist(block.txs); + }); + + }); + + describe('#fromBuffer', function() { + + it('should make a block from this known buffer', function() { + var block = Block().fromBuffer(blockbuf); + block.toBuffer().toString('hex').should.equal(blockhex); + }); + + }); + + describe('#fromBufferReader', function() { + + it('should make a block from this known buffer', function() { + var block = Block().fromBufferReader(BufferReader(blockbuf)); + block.toBuffer().toString('hex').should.equal(blockhex); + }); + + }); + + describe('#toBuffer', function() { + + it('should recover a block from this known buffer', function() { + var block = Block().fromBuffer(blockbuf); + block.toBuffer().toString('hex').should.equal(blockhex); + }); + + }); + + describe('#toBufferWriter', function() { + + it('should recover a block from this known buffer', function() { + var block = Block().fromBuffer(blockbuf); + block.toBufferWriter().concat().toString('hex').should.equal(blockhex); + }); + + }); + +}); From ac85264a2865bb151d9734b28e480478b88967c0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:22:18 -0700 Subject: [PATCH 209/280] Address().fromBuffer(buf); --- lib/address.js | 49 ++++++++++++++++++++++++++++++------------------- test/address.js | 13 +++++++++++++ 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/lib/address.js b/lib/address.js index 4b65eee..5a066ee 100644 --- a/lib/address.js +++ b/lib/address.js @@ -4,11 +4,18 @@ var Hash = require('./hash'); var Pubkey = require('./pubkey'); var Script = require('./script'); -function Address(obj) { +function Address(buf) { if (!(this instanceof Address)) - return new Address(obj); - if (obj) + return new Address(buf); + if (Buffer.isBuffer(buf)) { + this.fromBuffer(buf); + } else if (typeof buf === 'string') { + var str = buf; + this.fromString(str); + } else if (buf) { + var obj = buf; this.set(obj); + } }; Address.prototype.set = function(obj) { @@ -18,22 +25,7 @@ Address.prototype.set = function(obj) { return this; }; -Address.prototype.fromPubkey = function(pubkey, networkstr) { - this.hashbuf = Hash.sha256ripemd160(pubkey.toBuffer()); - this.networkstr = networkstr || 'mainnet'; - this.typestr = 'pubkeyhash'; - return this; -}; - -Address.prototype.fromScript = function(script, networkstr) { - this.hashbuf = Hash.sha256ripemd160(script.toBuffer()); - this.networkstr = networkstr || 'mainnet'; - this.typestr = 'scripthash'; - return this; -}; - -Address.prototype.fromString = function(str) { - var buf = base58check.decode(str); +Address.prototype.fromBuffer = function(buf) { if (buf.length !== 1 + 20) throw new Error('Address buffers must be exactly 21 bytes'); var version = buf[0]; @@ -57,6 +49,25 @@ Address.prototype.fromString = function(str) { this.hashbuf = buf.slice(1); return this; +}; + +Address.prototype.fromPubkey = function(pubkey, networkstr) { + this.hashbuf = Hash.sha256ripemd160(pubkey.toBuffer()); + this.networkstr = networkstr || 'mainnet'; + this.typestr = 'pubkeyhash'; + return this; +}; + +Address.prototype.fromScript = function(script, networkstr) { + this.hashbuf = Hash.sha256ripemd160(script.toBuffer()); + this.networkstr = networkstr || 'mainnet'; + this.typestr = 'scripthash'; + return this; +}; + +Address.prototype.fromString = function(str) { + var buf = base58check.decode(str); + return this.fromBuffer(buf); } Address.isValid = function(addrstr) { diff --git a/test/address.js b/test/address.js index 697cc29..e1bacf0 100644 --- a/test/address.js +++ b/test/address.js @@ -6,11 +6,16 @@ var Script = require('../lib/script'); describe('Address', function() { var pubkeyhash = new Buffer('3c3fa3d4adcaf8f52d5b1843975e122548269937', 'hex'); + var buf = Buffer.concat([new Buffer([0]), pubkeyhash]); var str = '16VZnHwRhwrExfeHFHGjwrgEMq8VcYPs9r'; it('should create a new address object', function() { var address = new Address(); should.exist(address); + address = Address(buf); + should.exist(address); + address = Address(str); + should.exist(address); }); describe('@isValid', function() { @@ -25,6 +30,14 @@ describe('Address', function() { }); + describe('#fromBuffer', function() { + + it('should make an address from a buffer', function() { + Address().fromBuffer(buf).toString().should.equal(str); + }); + + }); + describe('#fromPubkey', function() { it('should make this address from a compressed pubkey', function() { From 40ea68a3ff335609bdde4ad7bf947b658d89e977 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:26:19 -0700 Subject: [PATCH 210/280] more convenient Base58 constructor --- lib/base58.js | 9 ++++++++- test/base58.js | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/base58.js b/lib/base58.js index da977ca..cf13573 100644 --- a/lib/base58.js +++ b/lib/base58.js @@ -3,8 +3,15 @@ var bs58 = require('bs58'); var Base58 = function Base58(obj) { if (!(this instanceof Base58)) return new Base58(obj); - if (obj) + if (Buffer.isBuffer(obj)) { + var buf = obj; + this.fromBuffer(buf); + } else if (typeof obj === 'string') { + var str = obj; + this.fromString(str); + } else if (obj) { this.set(obj); + } }; Base58.prototype.set = function(obj) { diff --git a/test/base58.js b/test/base58.js index fca7d9a..11fe976 100644 --- a/test/base58.js +++ b/test/base58.js @@ -15,6 +15,11 @@ describe('Base58', function() { should.exist(b58); }); + it('should allow this handy syntax', function() { + Base58(buf).toString().should.equal(enc); + Base58(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')) + }); + describe('#set', function() { it('should set a blank buffer', function() { From 78ef76eb2fe1f4503c2c79895ec4c9417a539ce8 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:29:53 -0700 Subject: [PATCH 211/280] more convenient constructor ...allow inputing strings or buffers in the constructor. --- lib/base58check.js | 9 ++++++++- test/base58check.js | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/base58check.js b/lib/base58check.js index 9bc78ee..7b5fb95 100644 --- a/lib/base58check.js +++ b/lib/base58check.js @@ -4,8 +4,15 @@ var sha256sha256 = require('./hash').sha256sha256; var Base58Check = function Base58Check(obj) { if (!(this instanceof Base58Check)) return new Base58Check(obj); - if (obj) + if (Buffer.isBuffer(obj)) { + var buf = obj; + this.fromBuffer(buf); + } else if (typeof obj === 'string') { + var str = obj; + this.fromString(str); + } else if (obj) { this.set(obj); + } }; Base58Check.prototype.set = function(obj) { diff --git a/test/base58check.js b/test/base58check.js index 635198c..bc7081f 100644 --- a/test/base58check.js +++ b/test/base58check.js @@ -16,6 +16,11 @@ describe('Base58Check', function() { should.exist(b58); }); + it('should allow this handy syntax', function() { + Base58Check(buf).toString().should.equal(enc); + Base58Check(enc).toBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + describe('#set', function() { it('should set a buf', function() { From af6932a3139a60435ebf9f852924cbed662cc56c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:35:20 -0700 Subject: [PATCH 212/280] console.log? this must have been a remnant of some earlier debugging, either in bitcore2 or in bitcore. --- lib/bip32.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 0592a1a..b042731 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -151,8 +151,6 @@ BIP32.prototype.buildExtendedPublicKey = function() { BIP32.prototype.extendedPublicKeyString = function(format) { if (format === undefined || format === 'base58') { - if (!Buffer.isBuffer(this.extendedPublicKey)) - console.log('extendedPublicKey: ' + this.extendedPublicKey); var hash = Hash.sha256sha256(this.extendedPublicKey); var checksum = hash.slice(0, 4); var data = Buffer.concat([this.extendedPublicKey, checksum]); From 334c443d8b960312018f84d513f805b6035234b9 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:37:53 -0700 Subject: [PATCH 213/280] "gc" was some find-replace error from earlier --- lib/bip32.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index b042731..29f4bcb 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -31,14 +31,14 @@ BIP32.prototype.fromRandom = function(networkstr) { BIP32.prototype.fromString = function(str) { var decoded = base58.decode(str); if (decoded.length != 82) - throw new Error('gcNot enough data, expected 82 and received ' + decoded.length); + throw new Error('Not enough data, expected 82 and received ' + decoded.length); var checksum = decoded.slice(78, 82); var bytes = decoded.slice(0, 78); var hash = Hash.sha256sha256(bytes); if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) - throw new Error('gcInvalid checksum'); + throw new Error('Invalid checksum'); if (bytes !== undefined && bytes !== null) this.initFromBytes(bytes); @@ -51,11 +51,11 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) { networkstr = 'mainnet'; if (!Buffer.isBuffer(bytes)) - throw new Error('gcbytes must be a buffer'); + throw new Error('bytes must be a buffer'); if (bytes.length < 128 / 8) - throw new Error('gcNeed more than 128 bytes of entropy'); + throw new Error('Need more than 128 bytes of entropy'); if (bytes.length > 512 / 8) - throw new Error('gcMore than 512 bytes of entropy is nonstandard'); + throw new Error('More than 512 bytes of entropy is nonstandard'); var hash = Hash.sha512hmac(bytes, new Buffer('Bitcoin seed')); this.depth = 0x00; @@ -78,7 +78,7 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) { BIP32.prototype.initFromBytes = function(bytes) { // Both pub and private extended keys are 78 bytes if (bytes.length != 78) - throw new Error('gcnot enough data'); + throw new Error('not enough data'); this.version = u32(bytes.slice(0, 4)); this.depth = u8(bytes.slice(4, 5)); @@ -108,7 +108,7 @@ BIP32.prototype.initFromBytes = function(bytes) { this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); this.hasPrivateKey = false; } else { - throw new Error('gcInvalid key'); + throw new Error('Invalid key'); } this.buildExtendedPublicKey(); @@ -129,7 +129,7 @@ BIP32.prototype.buildExtendedPublicKey = function() { v = constants.testnet.bip32pubkey; break; default: - throw new Error('gcUnknown version'); + throw new Error('Unknown version'); } // Version @@ -158,7 +158,7 @@ BIP32.prototype.extendedPublicKeyString = function(format) { } else if (format === 'hex') { return this.extendedPublicKey.toString('hex');; } else { - throw new Error('gcbad format'); + throw new Error('bad format'); } } @@ -194,7 +194,7 @@ BIP32.prototype.extendedPrivateKeyString = function(format) { } else if (format === 'hex') { return this.extendedPrivateKey.toString('hex'); } else { - throw new Error('gcbad format'); + throw new Error('bad format'); } } @@ -211,12 +211,12 @@ BIP32.prototype.derive = function(path) { var c = e[i]; if (i == 0) { - if (c != 'm') throw new Error('gcinvalid path'); + if (c != 'm') throw new Error('invalid path'); continue; } if (parseInt(c.replace("'", "")).toString() !== c.replace("'", "")) - throw new Error('gcinvalid path'); + throw new Error('invalid path'); var usePrivate = (c.length > 1) && (c[c.length - 1] == '\''); var childIndex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c) & 0x7fffffff; @@ -232,7 +232,7 @@ BIP32.prototype.derive = function(path) { BIP32.prototype.deriveChild = function(i) { if (typeof i !== 'number') - throw new Error('gci must be a number'); + throw new Error('i must be a number'); var ib = []; ib.push((i >> 24) & 0xff); @@ -248,7 +248,7 @@ BIP32.prototype.deriveChild = function(i) { this.version == constants.testnet.bip32privkey); if (usePrivate && (!this.hasPrivateKey || !isPrivate)) - throw new Error('gcCannot do private key derivation without private key'); + throw new Error('Cannot do private key derivation without private key'); var ret = null; if (this.hasPrivateKey) { From 70659ad9d42393edc1a65cd0eda54a12729ffdd0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:40:29 -0700 Subject: [PATCH 214/280] use base58check in fromString --- lib/bip32.js | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 29f4bcb..b5c34d1 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -1,4 +1,5 @@ var base58 = require('./base58'); +var Base58Check = require('./base58check'); var Hash = require('./hash'); var Keypair = require('./keypair'); var Pubkey = require('./pubkey'); @@ -29,20 +30,8 @@ BIP32.prototype.fromRandom = function(networkstr) { }; BIP32.prototype.fromString = function(str) { - var decoded = base58.decode(str); - if (decoded.length != 82) - throw new Error('Not enough data, expected 82 and received ' + decoded.length); - var checksum = decoded.slice(78, 82); - var bytes = decoded.slice(0, 78); - - var hash = Hash.sha256sha256(bytes); - - if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) - throw new Error('Invalid checksum'); - - if (bytes !== undefined && bytes !== null) - this.initFromBytes(bytes); - + var bytes = Base58Check.decode(str); + this.initFromBytes(bytes); return this; }; From 70d9f0db93012a4a1f2a018ac4104b2b601cef80 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:41:51 -0700 Subject: [PATCH 215/280] use base58check in extendedPublicKeyString --- lib/bip32.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index b5c34d1..a4e6876 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -140,12 +140,9 @@ BIP32.prototype.buildExtendedPublicKey = function() { BIP32.prototype.extendedPublicKeyString = function(format) { if (format === undefined || format === 'base58') { - var hash = Hash.sha256sha256(this.extendedPublicKey); - var checksum = hash.slice(0, 4); - var data = Buffer.concat([this.extendedPublicKey, checksum]); - return base58.encode(data); + return Base58Check.encode(this.extendedPublicKey); } else if (format === 'hex') { - return this.extendedPublicKey.toString('hex');; + return this.extendedPublicKey.toString('hex'); } else { throw new Error('bad format'); } From 9e188574f70ee97a04c8bf1fe5d448aeb0e2278e Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:43:14 -0700 Subject: [PATCH 216/280] base58check in extendedPrivateKeyString --- lib/bip32.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index a4e6876..e09d9f4 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -173,10 +173,7 @@ BIP32.prototype.buildExtendedPrivateKey = function() { BIP32.prototype.extendedPrivateKeyString = function(format) { if (format === undefined || format === 'base58') { - var hash = Hash.sha256sha256(this.extendedPrivateKey); - var checksum = hash.slice(0, 4); - var data = Buffer.concat([this.extendedPrivateKey, checksum]); - return base58.encode(data); + return Base58Check.encode(this.extendedPrivateKey); } else if (format === 'hex') { return this.extendedPrivateKey.toString('hex'); } else { From 5da964739d81a843ed061d492cc98b749f06a6e0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:44:16 -0700 Subject: [PATCH 217/280] remove unused base58 dependency --- lib/bip32.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/bip32.js b/lib/bip32.js index e09d9f4..1444071 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -1,4 +1,3 @@ -var base58 = require('./base58'); var Base58Check = require('./base58check'); var Hash = require('./hash'); var Keypair = require('./keypair'); From c41419b6ef6d33db27a04563197afedd86410735 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 14:49:17 -0700 Subject: [PATCH 218/280] use buffer functions ...instead of adhoc uint functions --- lib/bip32.js | 33 +++------------------------------ test/bip32.js | 1 - 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 1444071..3264e76 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -68,10 +68,10 @@ BIP32.prototype.initFromBytes = function(bytes) { if (bytes.length != 78) throw new Error('not enough data'); - this.version = u32(bytes.slice(0, 4)); - this.depth = u8(bytes.slice(4, 5)); + this.version = bytes.slice(0, 4).readUInt32BE(0); + this.depth = bytes.slice(4, 5).readUInt8(0); this.parentFingerprint = bytes.slice(5, 9); - this.childIndex = u32(bytes.slice(9, 13)); + this.childIndex = bytes.slice(9, 13).readUInt32BE(0); this.chainCode = bytes.slice(13, 45); var keyBytes = bytes.slice(45, 78); @@ -303,31 +303,4 @@ BIP32.prototype.toString = function() { return this.extendedPublicKeyString(); }; -function uint(f, size) { - if (f.length < size) - throw new Error('not enough data'); - var n = 0; - for (var i = 0; i < size; i++) { - n *= 256; - n += f[i]; - } - return n; -} - -function u8(f) { - return uint(f, 1); -} - -function u16(f) { - return uint(f, 2); -} - -function u32(f) { - return uint(f, 4); -} - -function u64(f) { - return uint(f, 8); -} - module.exports = BIP32; diff --git a/test/bip32.js b/test/bip32.js index 82643b7..96f0d33 100644 --- a/test/bip32.js +++ b/test/bip32.js @@ -324,7 +324,6 @@ describe('BIP32', function() { tip32b.toString().slice(0, 4).should.equal('tpub'); }); - }); }); From 0641184e84d28abeb6a0c00fda43ff16e18f9495 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:02:11 -0700 Subject: [PATCH 219/280] support string input in constructor --- lib/bip32.js | 8 ++++++-- test/bip32.js | 44 ++++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 3264e76..a7f03a6 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -8,9 +8,13 @@ var Random = require('./random'); var bn = require('./bn'); var constants = require('./constants'); -var BIP32 = function BIP32() { +var BIP32 = function BIP32(obj) { if (!(this instanceof BIP32)) - return new BIP32(); + return new BIP32(obj); + if (typeof obj === 'string') { + var str = obj; + this.fromString(str); + } } BIP32.prototype.fromRandom = function(networkstr) { diff --git a/test/bip32.js b/test/bip32.js index 96f0d33..72e4fa9 100644 --- a/test/bip32.js +++ b/test/bip32.js @@ -32,14 +32,14 @@ describe('BIP32', function() { var vector2_m02147483647h12147483646h2_public = 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt'; var vector2_m02147483647h12147483646h2_private = 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j'; - - it('should initialize the class', function() { - should.exist(BIP32); - }); - - it('should create a bip32', function() { - var bip32 = new BIP32(); + it('should make a new a bip32', function() { + var bip32; + bip32 = new BIP32(); should.exist(bip32); + bip32 = BIP32(); + should.exist(bip32); + new BIP32(vector1_m_private).toString().should.equal(vector1_m_private); + BIP32(vector1_m_private).toString().should.equal(vector1_m_private); }); it('should initialize test vector 1 from the extended public key', function() { @@ -266,6 +266,21 @@ describe('BIP32', function() { child2.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h2_public); }); + describe('testnet', function() { + it('should initialize a new BIP32 correctly from a random BIP32', function() { + var b1 = new BIP32(); + b1.fromRandom('testnet'); + var b2 = new BIP32().fromString(b1.extendedPublicKeyString()); + b2.extendedPublicKeyString().should.equal(b1.extendedPublicKeyString()); + }); + + it('should generate valid ext pub key for testnet', function() { + var b = new BIP32(); + b.fromRandom('testnet'); + b.extendedPublicKeyString().substring(0,4).should.equal('tpub'); + }); + }); + describe('#seed', function() { it('should initialize a new BIP32 correctly from test vector 1 seed', function() { @@ -285,21 +300,6 @@ describe('BIP32', function() { }); }); - describe('testnet', function() { - it('should initialize a new BIP32 correctly from a random BIP32', function() { - var b1 = new BIP32(); - b1.fromRandom('testnet'); - var b2 = new BIP32().fromString(b1.extendedPublicKeyString()); - b2.extendedPublicKeyString().should.equal(b1.extendedPublicKeyString()); - }); - - it('should generate valid ext pub key for testnet', function() { - var b = new BIP32(); - b.fromRandom('testnet'); - b.extendedPublicKeyString().substring(0,4).should.equal('tpub'); - }); - }); - describe('#toString', function() { var bip32 = new BIP32(); bip32.fromRandom('mainnet'); From 7390b15f8946850367dc10dc5a1d7e7a74691f92 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:11:16 -0700 Subject: [PATCH 220/280] add set function to bip32 --- lib/bip32.js | 16 ++++++++++++++++ test/bip32.js | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/bip32.js b/lib/bip32.js index a7f03a6..1046317 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -14,9 +14,25 @@ var BIP32 = function BIP32(obj) { if (typeof obj === 'string') { var str = obj; this.fromString(str); + } else if (obj ) { + this.set(obj); } } +BIP32.prototype.set = function(obj) { + this.version = typeof obj.version !== 'undefined' ? obj.version : this.version; + this.depth = typeof obj.depth !== 'undefined' ? obj.depth : this.depth; + this.parentFingerprint = obj.parentFingerprint || this.parentFingerprint; + this.childIndex = obj.childIndex || this.childIndex; + this.chainCode = obj.chainCode || this.chainCode; + this.key = obj.key || this.key; + this.hasPrivateKey = typeof obj.hasPrivateKey !== 'undefined' ? obj.hasPrivateKey : this.hasPrivateKey; + this.pubKeyHash = obj.pubKeyHash || this.pubKeyHash; + this.extendedPublicKey = obj.extendedPublicKey || this.extendedPublicKey; + this.extendedPrivateKey = obj.extendedPrivateKey || this.extendedPrivateKey; + return this; +}; + BIP32.prototype.fromRandom = function(networkstr) { if (!networkstr) networkstr = 'mainnet'; diff --git a/test/bip32.js b/test/bip32.js index 72e4fa9..7a87f19 100644 --- a/test/bip32.js +++ b/test/bip32.js @@ -40,6 +40,7 @@ describe('BIP32', function() { should.exist(bip32); new BIP32(vector1_m_private).toString().should.equal(vector1_m_private); BIP32(vector1_m_private).toString().should.equal(vector1_m_private); + BIP32(BIP32(vector1_m_private)).toString().should.equal(vector1_m_private); }); it('should initialize test vector 1 from the extended public key', function() { @@ -281,6 +282,24 @@ describe('BIP32', function() { }); }); + describe('#set', function() { + var bip32 = BIP32(vector1_m_private); + var bip322 = BIP32().set({ + version: bip32.version, + depth: bip32.depth, + parentFingerprint: bip32.parentFingerprint, + childIndex: bip32.childIndex, + chainCode: bip32.chainCode, + key: bip32.key, + hasPrivateKey: bip32.hasPrivateKey, + pubKeyHash: bip32.pubKeyhash, + extendedPublicKey: bip32.extendedPublicKey, + extendedPrivateKey: bip32.extendedPrivateKey + }); + bip322.toString().should.equal(bip32.toString()); + bip322.set({}).toString().should.equal(bip32.toString()); + }); + describe('#seed', function() { it('should initialize a new BIP32 correctly from test vector 1 seed', function() { From bd16eddf54bfd6730296f8997444cf5db036b58f Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:13:57 -0700 Subject: [PATCH 221/280] Key -> Keypair --- lib/bip32.js | 56 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 1046317..86497bb 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -25,7 +25,7 @@ BIP32.prototype.set = function(obj) { this.parentFingerprint = obj.parentFingerprint || this.parentFingerprint; this.childIndex = obj.childIndex || this.childIndex; this.chainCode = obj.chainCode || this.chainCode; - this.key = obj.key || this.key; + this.keypair = obj.keypair || this.keypair; this.hasPrivateKey = typeof obj.hasPrivateKey !== 'undefined' ? obj.hasPrivateKey : this.hasPrivateKey; this.pubKeyHash = obj.pubKeyHash || this.pubKeyHash; this.extendedPublicKey = obj.extendedPublicKey || this.extendedPublicKey; @@ -41,9 +41,9 @@ BIP32.prototype.fromRandom = function(networkstr) { this.parentFingerprint = new Buffer([0, 0, 0, 0]); this.childIndex = new Buffer([0, 0, 0, 0]); this.chainCode = Random.getRandomBuffer(32); - this.key = (new Keypair()).fromRandom(); + this.keypair = (new Keypair()).fromRandom(); this.hasPrivateKey = true; - this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); + this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); this.buildExtendedPublicKey(); this.buildExtendedPrivateKey(); }; @@ -71,11 +71,11 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) { this.childIndex = new Buffer([0, 0, 0, 0]); this.chainCode = hash.slice(32, 64); this.version = constants[networkstr].bip32privkey; - this.key = new Keypair(); - this.key.privkey = new Privkey({bn: bn.fromBuffer(hash.slice(0, 32))}); - this.key.privkey2pubkey(); + this.keypair = new Keypair(); + this.keypair.privkey = new Privkey({bn: bn.fromBuffer(hash.slice(0, 32))}); + this.keypair.privkey2pubkey(); this.hasPrivateKey = true; - this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); + this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); this.buildExtendedPublicKey(); this.buildExtendedPrivateKey(); @@ -105,15 +105,15 @@ BIP32.prototype.initFromBytes = function(bytes) { this.version == constants.testnet.bip32pubkey); if (isPrivate && keyBytes[0] == 0) { - this.key = new Keypair(); - this.key.privkey = new Privkey({bn: bn.fromBuffer(keyBytes.slice(1, 33))}); - this.key.privkey2pubkey(); - this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); + this.keypair = new Keypair(); + this.keypair.privkey = new Privkey({bn: bn.fromBuffer(keyBytes.slice(1, 33))}); + this.keypair.privkey2pubkey(); + this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); this.hasPrivateKey = true; } else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) { - this.key = new Keypair(); - this.key.pubkey = (new Pubkey()).fromDER(keyBytes); - this.pubKeyHash = Hash.sha256ripemd160(this.key.pubkey.toBuffer()); + this.keypair = new Keypair(); + this.keypair.pubkey = (new Pubkey()).fromDER(keyBytes); + this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); this.hasPrivateKey = false; } else { throw new Error('Invalid key'); @@ -153,7 +153,7 @@ BIP32.prototype.buildExtendedPublicKey = function() { new Buffer([(this.childIndex >>> 8) & 0xff]), new Buffer([this.childIndex & 0xff]), this.chainCode, - this.key.pubkey.toBuffer() + this.keypair.pubkey.toBuffer() ]); } @@ -186,7 +186,7 @@ BIP32.prototype.buildExtendedPrivateKey = function() { new Buffer([this.childIndex & 0xff]), this.chainCode, new Buffer([0]), - this.key.privkey.bn.toBuffer({size: 32}) + this.keypair.privkey.bn.toBuffer({size: 32}) ]); } @@ -257,9 +257,9 @@ BIP32.prototype.deriveChild = function(i) { var data = null; if (usePrivate) { - data = Buffer.concat([new Buffer([0]), this.key.privkey.bn.toBuffer({size: 32}), ib]); + data = Buffer.concat([new Buffer([0]), this.keypair.privkey.bn.toBuffer({size: 32}), ib]); } else { - data = Buffer.concat([this.key.pubkey.toBuffer({size: 32}), ib]); + data = Buffer.concat([this.keypair.pubkey.toBuffer({size: 32}), ib]); } var hash = Hash.sha512hmac(data, this.chainCode); @@ -267,25 +267,25 @@ BIP32.prototype.deriveChild = function(i) { var ir = hash.slice(32, 64); // ki = IL + kpar (mod n). - var k = il.add(this.key.privkey.bn).mod(Point.getN()); + var k = il.add(this.keypair.privkey.bn).mod(Point.getN()); ret = new BIP32(); ret.chainCode = ir; - ret.key = new Keypair(); - ret.key.privkey = new Privkey({bn: k}); - ret.key.privkey2pubkey(); + ret.keypair = new Keypair(); + ret.keypair.privkey = new Privkey({bn: k}); + ret.keypair.privkey2pubkey(); ret.hasPrivateKey = true; } else { - var data = Buffer.concat([this.key.pubkey.toBuffer(), ib]); + var data = Buffer.concat([this.keypair.pubkey.toBuffer(), ib]); var hash = Hash.sha512hmac(data, this.chainCode); var il = bn(hash.slice(0, 32)); var ir = hash.slice(32, 64); // Ki = (IL + kpar)*G = IL*G + Kpar var ilG = Point.getG().mul(il); - var Kpar = this.key.pubkey.point; + var Kpar = this.keypair.pubkey.point; var Ki = ilG.add(Kpar); var newpub = new Pubkey(); newpub.point = Ki; @@ -293,9 +293,9 @@ BIP32.prototype.deriveChild = function(i) { ret = new BIP32(); ret.chainCode = ir; - var key = new Keypair(); - key.pubkey = newpub; - ret.key = key; + var keypair = new Keypair(); + keypair.pubkey = newpub; + ret.keypair = keypair; ret.hasPrivateKey = false; } @@ -304,7 +304,7 @@ BIP32.prototype.deriveChild = function(i) { ret.version = this.version; ret.depth = this.depth + 1; - ret.pubKeyHash = Hash.sha256ripemd160(ret.key.pubkey.toBuffer()); + ret.pubKeyHash = Hash.sha256ripemd160(ret.keypair.pubkey.toBuffer()); ret.buildExtendedPublicKey(); ret.buildExtendedPrivateKey(); From 9473bdf9b0e078a3c23565a5f8d41a48877727bd Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:15:56 -0700 Subject: [PATCH 222/280] more convenient name --- lib/bip32.js | 28 ++++++++-------- test/bip32.js | 90 +++++++++++++++++++++++++-------------------------- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 86497bb..cb754a7 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -28,8 +28,8 @@ BIP32.prototype.set = function(obj) { this.keypair = obj.keypair || this.keypair; this.hasPrivateKey = typeof obj.hasPrivateKey !== 'undefined' ? obj.hasPrivateKey : this.hasPrivateKey; this.pubKeyHash = obj.pubKeyHash || this.pubKeyHash; - this.extendedPublicKey = obj.extendedPublicKey || this.extendedPublicKey; - this.extendedPrivateKey = obj.extendedPrivateKey || this.extendedPrivateKey; + this.xpubkey = obj.xpubkey || this.xpubkey; + this.xprivkey = obj.xprivkey || this.xprivkey; return this; }; @@ -124,7 +124,7 @@ BIP32.prototype.initFromBytes = function(bytes) { } BIP32.prototype.buildExtendedPublicKey = function() { - this.extendedPublicKey = new Buffer([]); + this.xpubkey = new Buffer([]); var v = null; switch (this.version) { @@ -141,7 +141,7 @@ BIP32.prototype.buildExtendedPublicKey = function() { } // Version - this.extendedPublicKey = Buffer.concat([ + this.xpubkey = Buffer.concat([ new Buffer([v >> 24]), new Buffer([(v >> 16) & 0xff]), new Buffer([(v >> 8) & 0xff]), @@ -157,11 +157,11 @@ BIP32.prototype.buildExtendedPublicKey = function() { ]); } -BIP32.prototype.extendedPublicKeyString = function(format) { +BIP32.prototype.xpubkeyString = function(format) { if (format === undefined || format === 'base58') { - return Base58Check.encode(this.extendedPublicKey); + return Base58Check.encode(this.xpubkey); } else if (format === 'hex') { - return this.extendedPublicKey.toString('hex'); + return this.xpubkey.toString('hex'); } else { throw new Error('bad format'); } @@ -169,11 +169,11 @@ BIP32.prototype.extendedPublicKeyString = function(format) { BIP32.prototype.buildExtendedPrivateKey = function() { if (!this.hasPrivateKey) return; - this.extendedPrivateKey = new Buffer([]); + this.xprivkey = new Buffer([]); var v = this.version; - this.extendedPrivateKey = Buffer.concat([ + this.xprivkey = Buffer.concat([ new Buffer([v >> 24]), new Buffer([(v >> 16) & 0xff]), new Buffer([(v >> 8) & 0xff]), @@ -190,11 +190,11 @@ BIP32.prototype.buildExtendedPrivateKey = function() { ]); } -BIP32.prototype.extendedPrivateKeyString = function(format) { +BIP32.prototype.xprivkeyString = function(format) { if (format === undefined || format === 'base58') { - return Base58Check.encode(this.extendedPrivateKey); + return Base58Check.encode(this.xprivkey); } else if (format === 'hex') { - return this.extendedPrivateKey.toString('hex'); + return this.xprivkey.toString('hex'); } else { throw new Error('bad format'); } @@ -318,9 +318,9 @@ BIP32.prototype.toString = function() { this.version == constants.testnet.bip32privkey); if (isPrivate) - return this.extendedPrivateKeyString(); + return this.xprivkeyString(); else - return this.extendedPublicKeyString(); + return this.xpubkeyString(); }; module.exports = BIP32; diff --git a/test/bip32.js b/test/bip32.js index 7a87f19..3349627 100644 --- a/test/bip32.js +++ b/test/bip32.js @@ -55,104 +55,104 @@ describe('BIP32', function() { it('should get the extended public key from the extended private key for test vector 1', function() { var bip32 = new BIP32().fromString(vector1_m_private); - bip32.extendedPublicKeyString().should.equal(vector1_m_public); + bip32.xpubkeyString().should.equal(vector1_m_public); }); it("should get m/0' ext. private key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector1_m0h_private); + child.xprivkeyString().should.equal(vector1_m0h_private); }); it("should get m/0' ext. public key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector1_m0h_public); + child.xpubkeyString().should.equal(vector1_m0h_public); }); it("should get m/0'/1 ext. private key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector1_m0h1_private); + child.xprivkeyString().should.equal(vector1_m0h1_private); }); it("should get m/0'/1 ext. public key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector1_m0h1_public); + child.xpubkeyString().should.equal(vector1_m0h1_public); }); it("should get m/0'/1 ext. public key from m/0' public key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'"); - var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.xpubkeyString()); var child2 = child_pub.derive("m/1"); should.exist(child2); - child2.extendedPublicKeyString().should.equal(vector1_m0h1_public); + child2.xpubkeyString().should.equal(vector1_m0h1_public); }); it("should get m/0'/1/2h ext. private key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector1_m0h12h_private); + child.xprivkeyString().should.equal(vector1_m0h12h_private); }); it("should get m/0'/1/2h ext. public key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector1_m0h12h_public); + child.xpubkeyString().should.equal(vector1_m0h12h_public); }); it("should get m/0'/1/2h/2 ext. private key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector1_m0h12h2_private); + child.xprivkeyString().should.equal(vector1_m0h12h2_private); }); it("should get m/0'/1/2'/2 ext. public key from m/0'/1/2' public key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'"); - var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.xpubkeyString()); var child2 = child_pub.derive("m/2"); should.exist(child2); - child2.extendedPublicKeyString().should.equal(vector1_m0h12h2_public); + child2.xpubkeyString().should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2 ext. public key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector1_m0h12h2_public); + child.xpubkeyString().should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2/1000000000 ext. private key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2/1000000000"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector1_m0h12h21000000000_private); + child.xprivkeyString().should.equal(vector1_m0h12h21000000000_private); }); it("should get m/0'/1/2h/2/1000000000 ext. public key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2/1000000000"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector1_m0h12h21000000000_public); + child.xpubkeyString().should.equal(vector1_m0h12h21000000000_public); }); it("should get m/0'/1/2'/2/1000000000 ext. public key from m/0'/1/2'/2 public key from test vector 1", function() { var bip32 = new BIP32().fromString(vector1_m_private); var child = bip32.derive("m/0'/1/2'/2"); - var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.xpubkeyString()); var child2 = child_pub.derive("m/1000000000"); should.exist(child2); - child2.extendedPublicKeyString().should.equal(vector1_m0h12h21000000000_public); + child2.xpubkeyString().should.equal(vector1_m0h12h21000000000_public); }); it('should initialize test vector 2 from the extended public key', function() { @@ -167,118 +167,118 @@ describe('BIP32', function() { it('should get the extended public key from the extended private key for test vector 2', function() { var bip32 = new BIP32().fromString(vector2_m_private); - bip32.extendedPublicKeyString().should.equal(vector2_m_public); + bip32.xpubkeyString().should.equal(vector2_m_public); }); it("should get m/0 ext. private key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector2_m0_private); + child.xprivkeyString().should.equal(vector2_m0_private); }); it("should get m/0 ext. public key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector2_m0_public); + child.xpubkeyString().should.equal(vector2_m0_public); }); it("should get m/0 ext. public key from m public key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m"); - var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.xpubkeyString()); var child2 = child_pub.derive("m/0"); should.exist(child2); - child2.extendedPublicKeyString().should.equal(vector2_m0_public); + child2.xpubkeyString().should.equal(vector2_m0_public); }); it("should get m/0/2147483647h ext. private key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector2_m02147483647h_private); + child.xprivkeyString().should.equal(vector2_m02147483647h_private); }); it("should get m/0/2147483647h ext. public key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector2_m02147483647h_public); + child.xpubkeyString().should.equal(vector2_m02147483647h_public); }); it("should get m/0/2147483647h/1 ext. private key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector2_m02147483647h1_private); + child.xprivkeyString().should.equal(vector2_m02147483647h1_private); }); it("should get m/0/2147483647h/1 ext. public key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector2_m02147483647h1_public); + child.xpubkeyString().should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1 ext. public key from m/0/2147483647h public key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'"); - var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.xpubkeyString()); var child2 = child_pub.derive("m/1"); should.exist(child2); - child2.extendedPublicKeyString().should.equal(vector2_m02147483647h1_public); + child2.xpubkeyString().should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1/2147483646h ext. private key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector2_m02147483647h12147483646h_private); + child.xprivkeyString().should.equal(vector2_m02147483647h12147483646h_private); }); it("should get m/0/2147483647h/1/2147483646h ext. public key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h_public); + child.xpubkeyString().should.equal(vector2_m02147483647h12147483646h_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. private key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'/2"); should.exist(child); - child.extendedPrivateKeyString().should.equal(vector2_m02147483647h12147483646h2_private); + child.xprivkeyString().should.equal(vector2_m02147483647h12147483646h2_private); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'/2"); should.exist(child); - child.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h2_public); + child.xpubkeyString().should.equal(vector2_m02147483647h12147483646h2_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from m/0/2147483647h/2147483646h public key from test vector 2", function() { var bip32 = new BIP32().fromString(vector2_m_private); var child = bip32.derive("m/0/2147483647'/1/2147483646'"); - var child_pub = new BIP32().fromString(child.extendedPublicKeyString()); + var child_pub = new BIP32().fromString(child.xpubkeyString()); var child2 = child_pub.derive("m/2"); should.exist(child2); - child2.extendedPublicKeyString().should.equal(vector2_m02147483647h12147483646h2_public); + child2.xpubkeyString().should.equal(vector2_m02147483647h12147483646h2_public); }); describe('testnet', function() { it('should initialize a new BIP32 correctly from a random BIP32', function() { var b1 = new BIP32(); b1.fromRandom('testnet'); - var b2 = new BIP32().fromString(b1.extendedPublicKeyString()); - b2.extendedPublicKeyString().should.equal(b1.extendedPublicKeyString()); + var b2 = new BIP32().fromString(b1.xpubkeyString()); + b2.xpubkeyString().should.equal(b1.xpubkeyString()); }); it('should generate valid ext pub key for testnet', function() { var b = new BIP32(); b.fromRandom('testnet'); - b.extendedPublicKeyString().substring(0,4).should.equal('tpub'); + b.xpubkeyString().substring(0,4).should.equal('tpub'); }); }); @@ -293,8 +293,8 @@ describe('BIP32', function() { key: bip32.key, hasPrivateKey: bip32.hasPrivateKey, pubKeyHash: bip32.pubKeyhash, - extendedPublicKey: bip32.extendedPublicKey, - extendedPrivateKey: bip32.extendedPrivateKey + xpubkey: bip32.xpubkey, + xprivkey: bip32.xprivkey }); bip322.toString().should.equal(bip32.toString()); bip322.set({}).toString().should.equal(bip32.toString()); @@ -306,16 +306,16 @@ describe('BIP32', function() { var hex = vector1_master; var bip32 = (new BIP32()).fromSeed(new Buffer(hex, 'hex'), 'mainnet'); should.exist(bip32); - bip32.extendedPrivateKeyString().should.equal(vector1_m_private); - bip32.extendedPublicKeyString().should.equal(vector1_m_public); + bip32.xprivkeyString().should.equal(vector1_m_private); + bip32.xpubkeyString().should.equal(vector1_m_public); }); it('should initialize a new BIP32 correctly from test vector 2 seed', function() { var hex = vector2_master; var bip32 = (new BIP32()).fromSeed(new Buffer(hex, 'hex'), 'mainnet'); should.exist(bip32); - bip32.extendedPrivateKeyString().should.equal(vector2_m_private); - bip32.extendedPublicKeyString().should.equal(vector2_m_public); + bip32.xprivkeyString().should.equal(vector2_m_private); + bip32.xpubkeyString().should.equal(vector2_m_public); }); }); @@ -330,7 +330,7 @@ describe('BIP32', function() { }); it('should return an xpub string', function() { - var bip32b = new BIP32().fromString(bip32.extendedPublicKeyString()); + var bip32b = new BIP32().fromString(bip32.xpubkeyString()); bip32b.toString().slice(0, 4).should.equal('xpub'); }); @@ -339,7 +339,7 @@ describe('BIP32', function() { }); it('should return a tpub string', function() { - var tip32b = new BIP32().fromString(tip32.extendedPublicKeyString()); + var tip32b = new BIP32().fromString(tip32.xpubkeyString()); tip32b.toString().slice(0, 4).should.equal('tpub'); }); From eb34a1651108e14b8584e033f9d65c63a7e2e8d6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:17:54 -0700 Subject: [PATCH 223/280] more convenient name --- lib/bip32.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index cb754a7..73abfa4 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -44,8 +44,8 @@ BIP32.prototype.fromRandom = function(networkstr) { this.keypair = (new Keypair()).fromRandom(); this.hasPrivateKey = true; this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); - this.buildExtendedPublicKey(); - this.buildExtendedPrivateKey(); + this.buildxpubkey(); + this.buildxprivkey(); }; BIP32.prototype.fromString = function(str) { @@ -77,8 +77,8 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) { this.hasPrivateKey = true; this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); - this.buildExtendedPublicKey(); - this.buildExtendedPrivateKey(); + this.buildxpubkey(); + this.buildxprivkey(); return this; }; @@ -119,11 +119,11 @@ BIP32.prototype.initFromBytes = function(bytes) { throw new Error('Invalid key'); } - this.buildExtendedPublicKey(); - this.buildExtendedPrivateKey(); + this.buildxpubkey(); + this.buildxprivkey(); } -BIP32.prototype.buildExtendedPublicKey = function() { +BIP32.prototype.buildxpubkey = function() { this.xpubkey = new Buffer([]); var v = null; @@ -167,7 +167,7 @@ BIP32.prototype.xpubkeyString = function(format) { } } -BIP32.prototype.buildExtendedPrivateKey = function() { +BIP32.prototype.buildxprivkey = function() { if (!this.hasPrivateKey) return; this.xprivkey = new Buffer([]); @@ -306,8 +306,8 @@ BIP32.prototype.deriveChild = function(i) { ret.pubKeyHash = Hash.sha256ripemd160(ret.keypair.pubkey.toBuffer()); - ret.buildExtendedPublicKey(); - ret.buildExtendedPrivateKey(); + ret.buildxpubkey(); + ret.buildxprivkey(); return ret; } From 5f21059bf88bad3fbf17b58f3e75c001d5324fd4 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:20:01 -0700 Subject: [PATCH 224/280] more convenient names --- lib/bip32.js | 98 +++++++++++++++++++++++++-------------------------- test/bip32.js | 10 +++--- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 73abfa4..621f65f 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -22,12 +22,12 @@ var BIP32 = function BIP32(obj) { BIP32.prototype.set = function(obj) { this.version = typeof obj.version !== 'undefined' ? obj.version : this.version; this.depth = typeof obj.depth !== 'undefined' ? obj.depth : this.depth; - this.parentFingerprint = obj.parentFingerprint || this.parentFingerprint; - this.childIndex = obj.childIndex || this.childIndex; - this.chainCode = obj.chainCode || this.chainCode; + this.parentfingerprint = obj.parentfingerprint || this.parentfingerprint; + this.childindex = obj.childindex || this.childindex; + this.chaincode = obj.chaincode || this.chaincode; this.keypair = obj.keypair || this.keypair; - this.hasPrivateKey = typeof obj.hasPrivateKey !== 'undefined' ? obj.hasPrivateKey : this.hasPrivateKey; - this.pubKeyHash = obj.pubKeyHash || this.pubKeyHash; + this.hasprivkey = typeof obj.hasprivkey !== 'undefined' ? obj.hasprivkey : this.hasprivkey; + this.pubkeyhash = obj.pubkeyhash || this.pubkeyhash; this.xpubkey = obj.xpubkey || this.xpubkey; this.xprivkey = obj.xprivkey || this.xprivkey; return this; @@ -38,12 +38,12 @@ BIP32.prototype.fromRandom = function(networkstr) { networkstr = 'mainnet'; this.version = constants[networkstr].bip32privkey; this.depth = 0x00; - this.parentFingerprint = new Buffer([0, 0, 0, 0]); - this.childIndex = new Buffer([0, 0, 0, 0]); - this.chainCode = Random.getRandomBuffer(32); + this.parentfingerprint = new Buffer([0, 0, 0, 0]); + this.childindex = new Buffer([0, 0, 0, 0]); + this.chaincode = Random.getRandomBuffer(32); this.keypair = (new Keypair()).fromRandom(); - this.hasPrivateKey = true; - this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); + this.hasprivkey = true; + this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); this.buildxpubkey(); this.buildxprivkey(); }; @@ -67,15 +67,15 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) { var hash = Hash.sha512hmac(bytes, new Buffer('Bitcoin seed')); this.depth = 0x00; - this.parentFingerprint = new Buffer([0, 0, 0, 0]); - this.childIndex = new Buffer([0, 0, 0, 0]); - this.chainCode = hash.slice(32, 64); + this.parentfingerprint = new Buffer([0, 0, 0, 0]); + this.childindex = new Buffer([0, 0, 0, 0]); + this.chaincode = hash.slice(32, 64); this.version = constants[networkstr].bip32privkey; this.keypair = new Keypair(); this.keypair.privkey = new Privkey({bn: bn.fromBuffer(hash.slice(0, 32))}); this.keypair.privkey2pubkey(); - this.hasPrivateKey = true; - this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); + this.hasprivkey = true; + this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); this.buildxpubkey(); this.buildxprivkey(); @@ -90,9 +90,9 @@ BIP32.prototype.initFromBytes = function(bytes) { this.version = bytes.slice(0, 4).readUInt32BE(0); this.depth = bytes.slice(4, 5).readUInt8(0); - this.parentFingerprint = bytes.slice(5, 9); - this.childIndex = bytes.slice(9, 13).readUInt32BE(0); - this.chainCode = bytes.slice(13, 45); + this.parentfingerprint = bytes.slice(5, 9); + this.childindex = bytes.slice(9, 13).readUInt32BE(0); + this.chaincode = bytes.slice(13, 45); var keyBytes = bytes.slice(45, 78); @@ -108,13 +108,13 @@ BIP32.prototype.initFromBytes = function(bytes) { this.keypair = new Keypair(); this.keypair.privkey = new Privkey({bn: bn.fromBuffer(keyBytes.slice(1, 33))}); this.keypair.privkey2pubkey(); - this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); - this.hasPrivateKey = true; + this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); + this.hasprivkey = true; } else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) { this.keypair = new Keypair(); this.keypair.pubkey = (new Pubkey()).fromDER(keyBytes); - this.pubKeyHash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); - this.hasPrivateKey = false; + this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); + this.hasprivkey = false; } else { throw new Error('Invalid key'); } @@ -147,12 +147,12 @@ BIP32.prototype.buildxpubkey = function() { new Buffer([(v >> 8) & 0xff]), new Buffer([v & 0xff]), new Buffer([this.depth]), - this.parentFingerprint, - new Buffer([this.childIndex >>> 24]), - new Buffer([(this.childIndex >>> 16) & 0xff]), - new Buffer([(this.childIndex >>> 8) & 0xff]), - new Buffer([this.childIndex & 0xff]), - this.chainCode, + this.parentfingerprint, + new Buffer([this.childindex >>> 24]), + new Buffer([(this.childindex >>> 16) & 0xff]), + new Buffer([(this.childindex >>> 8) & 0xff]), + new Buffer([this.childindex & 0xff]), + this.chaincode, this.keypair.pubkey.toBuffer() ]); } @@ -168,7 +168,7 @@ BIP32.prototype.xpubkeyString = function(format) { } BIP32.prototype.buildxprivkey = function() { - if (!this.hasPrivateKey) return; + if (!this.hasprivkey) return; this.xprivkey = new Buffer([]); var v = this.version; @@ -179,12 +179,12 @@ BIP32.prototype.buildxprivkey = function() { new Buffer([(v >> 8) & 0xff]), new Buffer([v & 0xff]), new Buffer([this.depth]), - this.parentFingerprint, - new Buffer([this.childIndex >>> 24]), - new Buffer([(this.childIndex >>> 16) & 0xff]), - new Buffer([(this.childIndex >>> 8) & 0xff]), - new Buffer([this.childIndex & 0xff]), - this.chainCode, + this.parentfingerprint, + new Buffer([this.childindex >>> 24]), + new Buffer([(this.childindex >>> 16) & 0xff]), + new Buffer([(this.childindex >>> 8) & 0xff]), + new Buffer([this.childindex & 0xff]), + this.chaincode, new Buffer([0]), this.keypair.privkey.bn.toBuffer({size: 32}) ]); @@ -221,12 +221,12 @@ BIP32.prototype.derive = function(path) { throw new Error('invalid path'); var usePrivate = (c.length > 1) && (c[c.length - 1] == '\''); - var childIndex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c) & 0x7fffffff; + var childindex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c) & 0x7fffffff; if (usePrivate) - childIndex += 0x80000000; + childindex += 0x80000000; - bip32 = bip32.deriveChild(childIndex); + bip32 = bip32.deriveChild(childindex); } return bip32; @@ -249,11 +249,11 @@ BIP32.prototype.deriveChild = function(i) { (this.version == constants.mainnet.bip32privkey || this.version == constants.testnet.bip32privkey); - if (usePrivate && (!this.hasPrivateKey || !isPrivate)) + if (usePrivate && (!this.hasprivkey || !isPrivate)) throw new Error('Cannot do private key derivation without private key'); var ret = null; - if (this.hasPrivateKey) { + if (this.hasprivkey) { var data = null; if (usePrivate) { @@ -262,7 +262,7 @@ BIP32.prototype.deriveChild = function(i) { data = Buffer.concat([this.keypair.pubkey.toBuffer({size: 32}), ib]); } - var hash = Hash.sha512hmac(data, this.chainCode); + var hash = Hash.sha512hmac(data, this.chaincode); var il = bn.fromBuffer(hash.slice(0, 32), {size: 32}); var ir = hash.slice(32, 64); @@ -270,16 +270,16 @@ BIP32.prototype.deriveChild = function(i) { var k = il.add(this.keypair.privkey.bn).mod(Point.getN()); ret = new BIP32(); - ret.chainCode = ir; + ret.chaincode = ir; ret.keypair = new Keypair(); ret.keypair.privkey = new Privkey({bn: k}); ret.keypair.privkey2pubkey(); - ret.hasPrivateKey = true; + ret.hasprivkey = true; } else { var data = Buffer.concat([this.keypair.pubkey.toBuffer(), ib]); - var hash = Hash.sha512hmac(data, this.chainCode); + var hash = Hash.sha512hmac(data, this.chaincode); var il = bn(hash.slice(0, 32)); var ir = hash.slice(32, 64); @@ -291,20 +291,20 @@ BIP32.prototype.deriveChild = function(i) { newpub.point = Ki; ret = new BIP32(); - ret.chainCode = ir; + ret.chaincode = ir; var keypair = new Keypair(); keypair.pubkey = newpub; ret.keypair = keypair; - ret.hasPrivateKey = false; + ret.hasprivkey = false; } - ret.childIndex = i; - ret.parentFingerprint = this.pubKeyHash.slice(0, 4); + ret.childindex = i; + ret.parentfingerprint = this.pubkeyhash.slice(0, 4); ret.version = this.version; ret.depth = this.depth + 1; - ret.pubKeyHash = Hash.sha256ripemd160(ret.keypair.pubkey.toBuffer()); + ret.pubkeyhash = Hash.sha256ripemd160(ret.keypair.pubkey.toBuffer()); ret.buildxpubkey(); ret.buildxprivkey(); diff --git a/test/bip32.js b/test/bip32.js index 3349627..a0faac0 100644 --- a/test/bip32.js +++ b/test/bip32.js @@ -287,12 +287,12 @@ describe('BIP32', function() { var bip322 = BIP32().set({ version: bip32.version, depth: bip32.depth, - parentFingerprint: bip32.parentFingerprint, - childIndex: bip32.childIndex, - chainCode: bip32.chainCode, + parentfingerprint: bip32.parentfingerprint, + childindex: bip32.childindex, + chaincode: bip32.chaincode, key: bip32.key, - hasPrivateKey: bip32.hasPrivateKey, - pubKeyHash: bip32.pubKeyhash, + hasprivkey: bip32.hasprivkey, + pubkeyhash: bip32.pubKeyhash, xpubkey: bip32.xpubkey, xprivkey: bip32.xprivkey }); From 0eeba8eadb84fa8a035b423269b57cbb536b8b3c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:22:52 -0700 Subject: [PATCH 225/280] canonical BN notation --- lib/bip32.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/bip32.js b/lib/bip32.js index 621f65f..2ee5d6e 100644 --- a/lib/bip32.js +++ b/lib/bip32.js @@ -5,7 +5,7 @@ var Pubkey = require('./pubkey'); var Privkey = require('./privkey'); var Point = require('./point'); var Random = require('./random'); -var bn = require('./bn'); +var BN = require('./bn'); var constants = require('./constants'); var BIP32 = function BIP32(obj) { @@ -72,7 +72,7 @@ BIP32.prototype.fromSeed = function(bytes, networkstr) { this.chaincode = hash.slice(32, 64); this.version = constants[networkstr].bip32privkey; this.keypair = new Keypair(); - this.keypair.privkey = new Privkey({bn: bn.fromBuffer(hash.slice(0, 32))}); + this.keypair.privkey = new Privkey({bn: BN().fromBuffer(hash.slice(0, 32))}); this.keypair.privkey2pubkey(); this.hasprivkey = true; this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); @@ -106,7 +106,7 @@ BIP32.prototype.initFromBytes = function(bytes) { if (isPrivate && keyBytes[0] == 0) { this.keypair = new Keypair(); - this.keypair.privkey = new Privkey({bn: bn.fromBuffer(keyBytes.slice(1, 33))}); + this.keypair.privkey = new Privkey({bn: BN().fromBuffer(keyBytes.slice(1, 33))}); this.keypair.privkey2pubkey(); this.pubkeyhash = Hash.sha256ripemd160(this.keypair.pubkey.toBuffer()); this.hasprivkey = true; @@ -263,7 +263,7 @@ BIP32.prototype.deriveChild = function(i) { } var hash = Hash.sha512hmac(data, this.chaincode); - var il = bn.fromBuffer(hash.slice(0, 32), {size: 32}); + var il = BN().fromBuffer(hash.slice(0, 32), {size: 32}); var ir = hash.slice(32, 64); // ki = IL + kpar (mod n). @@ -280,7 +280,7 @@ BIP32.prototype.deriveChild = function(i) { } else { var data = Buffer.concat([this.keypair.pubkey.toBuffer(), ib]); var hash = Hash.sha512hmac(data, this.chaincode); - var il = bn(hash.slice(0, 32)); + var il = BN().fromBuffer(hash.slice(0, 32)); var ir = hash.slice(32, 64); // Ki = (IL + kpar)*G = IL*G + Kpar From 6494ca507682cd0d80b57d8ca8be4e47d164f843 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:27:12 -0700 Subject: [PATCH 226/280] convenience: varint -> vi --- lib/block.js | 16 ++++++++-------- test/block.js | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/block.js b/lib/block.js index 35fc2d7..50249b3 100644 --- a/lib/block.js +++ b/lib/block.js @@ -4,15 +4,15 @@ var BufferWriter = require('./bufferwriter'); var Blockheader = require('./blockheader'); var Varint = require('./varint'); -var Block = function Block(magicnum, blocksize, blockheader, txsvarint, txs) { +var Block = function Block(magicnum, blocksize, blockheader, txsvi, txs) { if (!(this instanceof Block)) - return new Block(magicnum, blocksize, blockheader, txsvarint, txs); + return new Block(magicnum, blocksize, blockheader, txsvi, txs); if (typeof magicnum === 'number') { this.set({ magicnum: magicnum, blocksize: blocksize, blockheader: blockheader, - txsvarint: txsvarint, + txsvi: txsvi, txs: txs }); } else if (magicnum) { @@ -24,7 +24,7 @@ Block.prototype.set = function(obj) { this.magicnum = typeof obj.magicnum !== 'undefined' ? obj.magicnum : this.magicnum; this.blocksize = typeof obj.blocksize !== 'undefined' ? obj.blocksize : this.blocksize; this.blockheader = obj.blockheader || this.blockheader; - this.txsvarint = obj.txsvarint || this.txsvarint; + this.txsvi = obj.txsvi || this.txsvi; this.txs = obj.txs || this.txs; return this; }; @@ -37,8 +37,8 @@ Block.prototype.fromBufferReader = function(br) { this.magicnum = br.readUInt32LE(); this.blocksize = br.readUInt32LE(); this.blockheader = Blockheader().fromBufferReader(br); - this.txsvarint = Varint(br.readVarintBuf()); - var txslen = this.txsvarint.toNumber(); + this.txsvi = Varint(br.readVarintBuf()); + var txslen = this.txsvi.toNumber(); this.txs = []; for (var i = 0; i < txslen; i++) { this.txs.push(Transaction().fromBufferReader(br)); @@ -56,8 +56,8 @@ Block.prototype.toBufferWriter = function(bw) { bw.writeUInt32LE(this.magicnum); bw.writeUInt32LE(this.blocksize); bw.write(this.blockheader.toBuffer()); - bw.write(this.txsvarint.buf); - var txslen = this.txsvarint.toNumber(); + bw.write(this.txsvi.buf); + var txslen = this.txsvi.toNumber(); for (var i = 0; i < txslen; i++) { this.txs[i].toBufferWriter(bw); } diff --git a/test/block.js b/test/block.js index b563254..dd8e907 100644 --- a/test/block.js +++ b/test/block.js @@ -24,13 +24,13 @@ describe('Block', function() { bhhex = '0100000005050505050505050505050505050505050505050505050505050505050505050909090909090909090909090909090909090909090909090909090909090909020000000300000004000000'; bhbuf = new Buffer(bhhex, 'hex'); var bh = Blockheader().fromBuffer(bhbuf); - var txsvarint = Varint(1); + var txsvi = Varint(1); var txs = [Transaction().fromBuffer(txbuf)]; var block = Block().set({ magicnum: magicnum, blocksize: blocksize, blockheader: bh, - txsvarint: txsvarint, + txsvi: txsvi, txs: txs }); var blockhex = 'f9beb4d93200000001000000050505050505050505050505050505050505050505050505050505050505050509090909090909090909090909090909090909090909090909090909090909090200000003000000040000000101000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000'; @@ -43,13 +43,13 @@ describe('Block', function() { magicnum: magicnum, blocksize: blocksize, blockheader: bh, - txsvarint: txsvarint, + txsvi: txsvi, txs: txs }); should.exist(block.magicnum); should.exist(block.blocksize); should.exist(block.blockheader); - should.exist(block.txsvarint); + should.exist(block.txsvi); should.exist(block.txs); }); From aee8547093d926346f738cb11f25d1c6c27c0c7c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:32:14 -0700 Subject: [PATCH 227/280] add fromString method for library consistency --- lib/bn.js | 6 ++++++ test/bn.js | 24 ++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/bn.js b/lib/bn.js index a4eeea0..c70db49 100644 --- a/lib/bn.js +++ b/lib/bn.js @@ -18,6 +18,12 @@ var reversebuf = function(buf, nbuf) { } }; +bnjs.prototype.fromString = function(str) { + var bn = bnjs(str); + bn.copy(this); + return this; +}; + bnjs.fromBuffer = function(buf, opts) { if (typeof opts !== 'undefined' && opts.endian === 'little') { var nbuf = new Buffer(buf.length); diff --git a/test/bn.js b/test/bn.js index 8a7d458..dbf8d5e 100644 --- a/test/bn.js +++ b/test/bn.js @@ -66,7 +66,23 @@ describe('BN', function() { }); - describe('#fromBuffer', function() { + describe('#fromString', function() { + + it('should make BN from a string', function() { + BN().fromString('5').toString().should.equal('5'); + }); + + }); + + describe('#toString', function() { + + it('should make a string', function() { + BN(5).toString().should.equal('5'); + }); + + }); + + describe('@fromBuffer', function() { it('should work with big endian', function() { var bn = BN.fromBuffer(new Buffer('0001', 'hex'), {endian: 'big'}); @@ -83,11 +99,15 @@ describe('BN', function() { bn.toString().should.equal('1'); }); + }); + + describe('#fromBuffer', function() { + it('should work as a prototype method', function() { var bn = BN().fromBuffer(new Buffer('0100', 'hex'), {size: 2, endian: 'little'}); bn.toString().should.equal('1'); }); - + }); describe('#toBuffer', function() { From 7918f53f12d5669fa4897dde7a87c817d3d11d57 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:32:58 -0700 Subject: [PATCH 228/280] bnjs -> BN ... for consistency --- lib/bn.js | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/bn.js b/lib/bn.js index c70db49..b841a51 100644 --- a/lib/bn.js +++ b/lib/bn.js @@ -1,16 +1,16 @@ -var _bnjs = require('bn.js'); +var _BN = require('bn.js'); -var bnjs = function bnjs_extended(n) { - if (!(this instanceof bnjs_extended)) { - return new bnjs(n); +var BN = function BN_extended(n) { + if (!(this instanceof BN_extended)) { + return new BN(n); } arguments[0] = n; - return _bnjs.apply(this, arguments); + return _BN.apply(this, arguments); }; -module.exports = bnjs; +module.exports = BN; -bnjs.prototype = _bnjs.prototype; +BN.prototype = _BN.prototype; var reversebuf = function(buf, nbuf) { for (var i = 0; i < buf.length; i++) { @@ -18,13 +18,13 @@ var reversebuf = function(buf, nbuf) { } }; -bnjs.prototype.fromString = function(str) { - var bn = bnjs(str); +BN.prototype.fromString = function(str) { + var bn = BN(str); bn.copy(this); return this; }; -bnjs.fromBuffer = function(buf, opts) { +BN.fromBuffer = function(buf, opts) { if (typeof opts !== 'undefined' && opts.endian === 'little') { var nbuf = new Buffer(buf.length); reversebuf(buf, nbuf); @@ -33,18 +33,18 @@ bnjs.fromBuffer = function(buf, opts) { var hex = buf.toString('hex'); if (hex.length % 2) hex = "0" + hex; - var bn = new bnjs(hex, 16); + var bn = new BN(hex, 16); return bn; }; -bnjs.prototype.fromBuffer = function(buf, opts) { - var bn = bnjs.fromBuffer(buf, opts); +BN.prototype.fromBuffer = function(buf, opts) { + var bn = BN.fromBuffer(buf, opts); bn.copy(this); return this; }; -bnjs.prototype.toBuffer = function(opts) { +BN.prototype.toBuffer = function(opts) { var buf; if (opts && opts.size) { var hex = this.toString(16); @@ -87,22 +87,22 @@ bnjs.prototype.toBuffer = function(opts) { }; function decorate(name) { - bnjs.prototype['_' + name] = _bnjs.prototype[name]; + BN.prototype['_' + name] = _BN.prototype[name]; var f = function(b) { if (typeof b === 'string') - b = new _bnjs(b); + b = new _BN(b); else if (typeof b === 'number') - b = new _bnjs(b.toString()); + b = new _BN(b.toString()); return this['_' + name](b); }; - bnjs.prototype[name] = f; + BN.prototype[name] = f; }; -_bnjs.prototype.gt = function(b) { +_BN.prototype.gt = function(b) { return this.cmp(b) > 0; }; -_bnjs.prototype.lt = function(b) { +_BN.prototype.lt = function(b) { return this.cmp(b) < 0; }; @@ -115,8 +115,8 @@ decorate('cmp'); decorate('gt'); decorate('lt'); -bnjs.prototype.toNumber = function() { +BN.prototype.toNumber = function() { return parseInt(this['toString'](10), 10); }; -module.exports = bnjs; +module.exports = BN; From 6e1dfd3003ab762a59a70203f45939f9463533d3 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:41:30 -0700 Subject: [PATCH 229/280] allow bufferreader.read to specify length ...to be more symmetric with the write function of bufferwriter --- lib/bufferreader.js | 4 +++- test/bufferreader.js | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index ffac561..375ed3f 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -28,7 +28,9 @@ BufferReader.prototype.buffer = function(len) { return buf; }; -BufferReader.prototype.read = function() { +BufferReader.prototype.read = function(len) { + if (len) + return this.buffer(len); var buf = this.buf.slice(this.pos); this.pos = this.buf.length; return buf; diff --git a/test/bufferreader.js b/test/bufferreader.js index 650389f..57ed76f 100644 --- a/test/bufferreader.js +++ b/test/bufferreader.js @@ -58,6 +58,16 @@ describe('BufferReader', function() { br.read().toString('hex').should.equal(buf.toString('hex')); }); + it('should return a buffer of this length', function() { + var buf = new Buffer(10); + buf.fill(0); + var br = new BufferReader(buf); + var buf2 = br.read(2); + buf2.length.should.equal(2); + br.eof().should.equal(false); + br.pos.should.equal(2); + }); + }); describe('#readUInt8', function() { From 6cee393c5dff217a7df963ec6972b519b961bfcb Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:43:15 -0700 Subject: [PATCH 230/280] put brains in read --- lib/bufferreader.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index 375ed3f..a356999 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -23,14 +23,15 @@ BufferReader.prototype.eof = function() { }; BufferReader.prototype.buffer = function(len) { - var buf = this.buf.slice(this.pos, this.pos + len); - this.pos = this.pos + len; - return buf; + return this.read(len); }; BufferReader.prototype.read = function(len) { - if (len) - return this.buffer(len); + if (len) { + var buf = this.buf.slice(this.pos, this.pos + len); + this.pos = this.pos + len; + return buf; + } var buf = this.buf.slice(this.pos); this.pos = this.buf.length; return buf; From 8e049b7244d91f688e4105da878226f3a1f9dca7 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:44:59 -0700 Subject: [PATCH 231/280] smarter --- lib/bufferreader.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/bufferreader.js b/lib/bufferreader.js index a356999..19e0ee3 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -27,13 +27,10 @@ BufferReader.prototype.buffer = function(len) { }; BufferReader.prototype.read = function(len) { - if (len) { - var buf = this.buf.slice(this.pos, this.pos + len); - this.pos = this.pos + len; - return buf; - } - var buf = this.buf.slice(this.pos); - this.pos = this.buf.length; + if (!len) + var len = this.buf.length; + var buf = this.buf.slice(this.pos, this.pos + len); + this.pos = this.pos + len; return buf; }; From 0212e4bd4c62bcb7f1163b87e567165c96c06c15 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 15:49:45 -0700 Subject: [PATCH 232/280] replace .buffer with .read --- lib/blockheader.js | 4 ++-- lib/bufferreader.js | 12 ++++-------- lib/script.js | 8 ++++---- lib/txin.js | 4 ++-- lib/txout.js | 2 +- test/bufferreader.js | 14 -------------- 6 files changed, 13 insertions(+), 31 deletions(-) diff --git a/lib/blockheader.js b/lib/blockheader.js index cc17ea6..d4ffc89 100644 --- a/lib/blockheader.js +++ b/lib/blockheader.js @@ -35,8 +35,8 @@ Blockheader.prototype.fromBuffer = function(buf) { Blockheader.prototype.fromBufferReader = function(br) { this.version = br.readUInt32LE(); - this.prevblockidbuf = br.buffer(32); - this.merklerootbuf = br.buffer(32); + this.prevblockidbuf = br.read(32); + this.merklerootbuf = br.read(32); this.time = br.readUInt32LE(); this.bits = br.readUInt32LE(); this.nonce = br.readUInt32LE(); diff --git a/lib/bufferreader.js b/lib/bufferreader.js index 19e0ee3..bc2d6c6 100644 --- a/lib/bufferreader.js +++ b/lib/bufferreader.js @@ -22,10 +22,6 @@ BufferReader.prototype.eof = function() { return this.pos >= this.buf.length; }; -BufferReader.prototype.buffer = function(len) { - return this.read(len); -}; - BufferReader.prototype.read = function(len) { if (!len) var len = this.buf.length; @@ -102,13 +98,13 @@ BufferReader.prototype.readVarintBuf = function() { var first = this.buf.readUInt8(this.pos); switch (first) { case 0xFD: - return this.buffer(1 + 2); + return this.read(1 + 2); case 0xFE: - return this.buffer(1 + 4); + return this.read(1 + 4); case 0xFF: - return this.buffer(1 + 8); + return this.read(1 + 8); default: - return this.buffer(1); + return this.read(1); } }; diff --git a/lib/script.js b/lib/script.js index 4b7eb29..affa32a 100644 --- a/lib/script.js +++ b/lib/script.js @@ -37,13 +37,13 @@ Script.prototype.fromBuffer = function(buf) { if (opcodenum > 0 && opcodenum < Opcode.map.OP_PUSHDATA1) { len = opcodenum; this.chunks.push({ - buf: br.buffer(len), + buf: br.read(len), len: len, opcodenum: opcodenum }); } else if (opcodenum === Opcode.map.OP_PUSHDATA1) { len = br.readUInt8(); - var buf = br.buffer(len); + var buf = br.read(len); this.chunks.push({ buf: buf, len: len, @@ -51,7 +51,7 @@ Script.prototype.fromBuffer = function(buf) { }); } else if (opcodenum === Opcode.map.OP_PUSHDATA2) { len = br.readUInt16LE(); - buf = br.buffer(len); + buf = br.read(len); this.chunks.push({ buf: buf, len: len, @@ -59,7 +59,7 @@ Script.prototype.fromBuffer = function(buf) { }); } else if (opcodenum === Opcode.map.OP_PUSHDATA4) { len = br.readUInt32LE(); - buf = br.buffer(len); + buf = br.read(len); this.chunks.push({ buf: buf, len: len, diff --git a/lib/txin.js b/lib/txin.js index 15a715f..4356a44 100644 --- a/lib/txin.js +++ b/lib/txin.js @@ -32,10 +32,10 @@ Txin.prototype.fromBuffer = function(buf) { }; Txin.prototype.fromBufferReader = function(br) { - this.txidbuf = br.buffer(32); + this.txidbuf = br.read(32); this.txoutnum = br.readUInt32LE(); this.varint = Varint(br.readVarintBuf()); - this.script = Script().fromBuffer(br.buffer(this.varint.toNumber())); + this.script = Script().fromBuffer(br.read(this.varint.toNumber())); this.seqnum = br.readUInt32LE(); return this; }; diff --git a/lib/txout.js b/lib/txout.js index 9ccd082..bbe3f1c 100644 --- a/lib/txout.js +++ b/lib/txout.js @@ -33,7 +33,7 @@ Txout.prototype.fromBuffer = function(buf) { Txout.prototype.fromBufferReader = function(br) { this.valuebn = br.readUInt64LEBN(); this.varint = Varint(br.readVarintNum()); - this.script = Script().fromBuffer(br.buffer(this.varint.toNumber())); + this.script = Script().fromBuffer(br.read(this.varint.toNumber())); return this; }; diff --git a/test/bufferreader.js b/test/bufferreader.js index 57ed76f..ac0df71 100644 --- a/test/bufferreader.js +++ b/test/bufferreader.js @@ -36,20 +36,6 @@ describe('BufferReader', function() { }); - describe('#buffer', function() { - - it('should return a buffer of this length', function() { - var buf = new Buffer(10); - buf.fill(0); - var br = new BufferReader(buf); - var buf2 = br.buffer(2); - buf2.length.should.equal(2); - br.eof().should.equal(false); - br.pos.should.equal(2); - }); - - }); - describe('read', function() { it('should return the same buffer', function() { From f727c5ad25908cdf08557151198bfca4e0dd828d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 16:03:57 -0700 Subject: [PATCH 233/280] fix varint test broken in browser --- test/varint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/varint.js b/test/varint.js index fc4ae25..23e1934 100644 --- a/test/varint.js +++ b/test/varint.js @@ -52,7 +52,7 @@ describe('Varint', function() { describe('#fromBN', function() { it('should set a number', function() { - var varint = Varint().fromNumber(BN(5)); + var varint = Varint().fromBN(BN(5)); varint.toNumber().should.equal(5); }); From 2caf7c6471a6aedd05ec9932965378ece27862c3 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 16:08:42 -0700 Subject: [PATCH 234/280] bn -> BN canonical capitalization --- lib/point.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/point.js b/lib/point.js index 25fd5c9..4296705 100644 --- a/lib/point.js +++ b/lib/point.js @@ -1,4 +1,4 @@ -var bn = require('./bn'); +var BN = require('./bn'); var elliptic = require('elliptic'); var ec = elliptic.curves.secp256k1; @@ -20,18 +20,18 @@ Point.getG = function() { }; Point.getN = function() { - return bn(ec.curve.n.toArray()); + return BN(ec.curve.n.toArray()); }; Point.prototype._getX = Point.prototype.getX; Point.prototype.getX = function() { - var n = bn(this._getX().toArray()); - return bn(this._getX().toArray()); + var n = BN(this._getX().toArray()); + return BN(this._getX().toArray()); }; Point.prototype._getY = Point.prototype.getY; Point.prototype.getY = function() { - return bn(this._getY().toArray()); + return BN(this._getY().toArray()); }; //https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf From 4a21a9551c0c9e60519e7f3ce322b9472727e9a1 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 16:32:38 -0700 Subject: [PATCH 235/280] fromX is actually static ...should add an instance method in the future --- test/point.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/point.js b/test/point.js index fe6b644..6c9c818 100644 --- a/test/point.js +++ b/test/point.js @@ -84,7 +84,7 @@ describe('Point', function() { }); - describe('#fromX', function() { + describe('@fromX', function() { it('should return g', function() { var g = point.getG(); From cf5b2f12506c961db3162024b36b9dd803208a66 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 16:48:23 -0700 Subject: [PATCH 236/280] varint -> vi canonical spelling --- lib/transaction.js | 24 ++++++++++++------------ lib/txin.js | 14 +++++++------- lib/txout.js | 14 +++++++------- test/transaction.js | 12 ++++++------ test/txin.js | 12 ++++++------ test/txout.js | 8 ++++---- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/lib/transaction.js b/lib/transaction.js index 51c27a1..55e7af6 100644 --- a/lib/transaction.js +++ b/lib/transaction.js @@ -4,15 +4,15 @@ var BufferWriter = require('./bufferwriter'); var BufferReader = require('./bufferreader'); var Varint = require('./varint'); -var Transaction = function Transaction(version, txinsvarint, txins, txoutsvarint, txouts, nlocktime) { +var Transaction = function Transaction(version, txinsvi, txins, txoutsvi, txouts, nlocktime) { if (!(this instanceof Transaction)) - return new Transaction(version, txinsvarint, txins, txoutsvarint, txouts, nlocktime); + return new Transaction(version, txinsvi, txins, txoutsvi, txouts, nlocktime); if (typeof version === 'number') { this.set({ version: version, - txinsvarint: txinsvarint, + txinsvi: txinsvi, txins: txins, - txoutsvarint: txoutsvarint, + txoutsvi: txoutsvi, txouts: txouts, nlocktime: nlocktime }); @@ -24,9 +24,9 @@ var Transaction = function Transaction(version, txinsvarint, txins, txoutsvarint Transaction.prototype.set = function(obj) { this.version = typeof obj.version !== 'undefined' ? obj.version : this.version; - this.txinsvarint = obj.txinsvarint || this.txinsvarint; + this.txinsvi = obj.txinsvi || this.txinsvi; this.txins = obj.txins || this.txins; - this.txoutsvarint = obj.txoutsvarint || this.txoutsvarint; + this.txoutsvi = obj.txoutsvi || this.txoutsvi; this.txouts = obj.txouts || this.txouts; this.nlocktime = typeof obj.nlocktime !== 'undefined' ? obj.nlocktime : this.nlocktime; return this; @@ -38,14 +38,14 @@ Transaction.prototype.fromBuffer = function(buf) { Transaction.prototype.fromBufferReader = function(br) { this.version = br.readUInt32LE(); - this.txinsvarint = Varint(br.readVarintBuf()); - var txinsnum = this.txinsvarint.toNumber(); + this.txinsvi = Varint(br.readVarintBuf()); + var txinsnum = this.txinsvi.toNumber(); this.txins = []; for (var i = 0; i < txinsnum; i++) { this.txins.push(Txin().fromBufferReader(br)); } - this.txoutsvarint = Varint(br.readVarintBuf()); - var txoutsnum = this.txoutsvarint.toNumber(); + this.txoutsvi = Varint(br.readVarintBuf()); + var txoutsnum = this.txoutsvi.toNumber(); this.txouts = []; for (var i = 0; i < txoutsnum; i++) { this.txouts.push(Txout().fromBufferReader(br)); @@ -62,11 +62,11 @@ Transaction.prototype.toBufferWriter = function(bw) { if (!bw) bw = new BufferWriter(); bw.writeUInt32LE(this.version); - bw.write(this.txinsvarint.buf); + bw.write(this.txinsvi.buf); for (var i = 0; i < this.txins.length; i++) { this.txins[i].toBufferWriter(bw); } - bw.write(this.txoutsvarint.buf) + bw.write(this.txoutsvi.buf) for (var i = 0; i < this.txouts.length; i++) { this.txouts[i].toBufferWriter(bw); } diff --git a/lib/txin.js b/lib/txin.js index 4356a44..bb36f8a 100644 --- a/lib/txin.js +++ b/lib/txin.js @@ -3,13 +3,13 @@ var BufferWriter = require('./bufferwriter'); var Varint = require('./varint'); var Script = require('./script'); -var Txin = function Txin(txidbuf, txoutnum, varint, script, seqnum) { +var Txin = function Txin(txidbuf, txoutnum, scriptvi, script, seqnum) { if (!(this instanceof Txin)) - return new Txin(txidbuf, txoutnum, varint, script, seqnum); + return new Txin(txidbuf, txoutnum, scriptvi, script, seqnum); if (Buffer.isBuffer(txidbuf)) { this.txidbuf = txidbuf; this.txoutnum = txoutnum; - this.varint = varint; + this.scriptvi = scriptvi; this.script = script; this.seqnum = seqnum; } else if (txidbuf) { @@ -21,7 +21,7 @@ var Txin = function Txin(txidbuf, txoutnum, varint, script, seqnum) { Txin.prototype.set = function(obj) { this.txidbuf = obj.txidbuf || this.txidbuf; this.txoutnum = typeof obj.txoutnum !== 'undefined' ? obj.txoutnum : this.txoutnum; - this.varint = typeof obj.varint !== 'undefined' ? obj.varint : this.varint; + this.scriptvi = typeof obj.scriptvi !== 'undefined' ? obj.scriptvi : this.scriptvi; this.script = obj.script || this.script; this.seqnum = typeof obj.seqnum !== 'undefined' ? obj.seqnum : this.seqnum; return this; @@ -34,8 +34,8 @@ Txin.prototype.fromBuffer = function(buf) { Txin.prototype.fromBufferReader = function(br) { this.txidbuf = br.read(32); this.txoutnum = br.readUInt32LE(); - this.varint = Varint(br.readVarintBuf()); - this.script = Script().fromBuffer(br.read(this.varint.toNumber())); + this.scriptvi = Varint(br.readVarintBuf()); + this.script = Script().fromBuffer(br.read(this.scriptvi.toNumber())); this.seqnum = br.readUInt32LE(); return this; }; @@ -49,7 +49,7 @@ Txin.prototype.toBufferWriter = function(bw) { bw = new BufferWriter(); bw.write(this.txidbuf); bw.writeUInt32LE(this.txoutnum); - bw.write(this.varint.buf); + bw.write(this.scriptvi.buf); bw.write(this.script.toBuffer()); bw.writeUInt32LE(this.seqnum); return bw; diff --git a/lib/txout.js b/lib/txout.js index bbe3f1c..ba64457 100644 --- a/lib/txout.js +++ b/lib/txout.js @@ -4,13 +4,13 @@ var BufferWriter = require('./bufferwriter'); var Varint = require('./varint'); var Script = require('./script'); -var Txout = function Txout(valuebn, varint, script) { +var Txout = function Txout(valuebn, scriptvi, script) { if (!(this instanceof Txout)) - return new Txout(valuebn, varint, script); + return new Txout(valuebn, scriptvi, script); if (valuebn instanceof BN) { this.set({ valuebn: valuebn, - varint: varint, + scriptvi: scriptvi, script: script }); } else if (valuebn) { @@ -21,7 +21,7 @@ var Txout = function Txout(valuebn, varint, script) { Txout.prototype.set = function(obj) { this.valuebn = obj.valuebn || this.valuebn; - this.varint = obj.varint || this.varint; + this.scriptvi = obj.scriptvi || this.scriptvi; this.script = obj.script || this.script; return this; }; @@ -32,8 +32,8 @@ Txout.prototype.fromBuffer = function(buf) { Txout.prototype.fromBufferReader = function(br) { this.valuebn = br.readUInt64LEBN(); - this.varint = Varint(br.readVarintNum()); - this.script = Script().fromBuffer(br.read(this.varint.toNumber())); + this.scriptvi = Varint(br.readVarintNum()); + this.script = Script().fromBuffer(br.read(this.scriptvi.toNumber())); return this; }; @@ -46,7 +46,7 @@ Txout.prototype.toBufferWriter = function(bw) { if (!bw) bw = new BufferWriter(); bw.writeUInt64LEBN(this.valuebn); - bw.write(this.varint.buf); + bw.write(this.scriptvi.buf); bw.write(this.script.toBuffer()); return bw; }; diff --git a/test/transaction.js b/test/transaction.js index 685c33f..9a9ef4c 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -19,9 +19,9 @@ describe('Transaction', function() { var txout = Txout().fromBuffer(new Buffer('050000000000000001ae', 'hex')); var tx = Transaction().set({ version: 0, - txinsvarint: Varint(1), + txinsvi: Varint(1), txins: [txin], - txoutsvarint: Varint(1), + txoutsvi: Varint(1), txouts: [txout], nlocktime: 0 }); @@ -37,16 +37,16 @@ describe('Transaction', function() { it('should set all the basic parameters', function() { var tx = Transaction().set({ version: 0, - txinsvarint: Varint(1), + txinsvi: Varint(1), txins: [txin], - txoutsvarint: Varint(1), + txoutsvi: Varint(1), txouts: [txout], nlocktime: 0 }); should.exist(tx.version); - should.exist(tx.txinsvarint); + should.exist(tx.txinsvi); should.exist(tx.txins); - should.exist(tx.txoutsvarint); + should.exist(tx.txoutsvi); should.exist(tx.txouts); should.exist(tx.nlocktime); }); diff --git a/test/txin.js b/test/txin.js index ec3e1e8..f441f63 100644 --- a/test/txin.js +++ b/test/txin.js @@ -17,12 +17,12 @@ describe('Txin', function() { txidbuf.fill(0); var txoutnum = 0; var script = Script().fromString("OP_CHECKMULTISIG"); - var varint = Varint(script.toBuffer().length); + var scriptvi = Varint(script.toBuffer().length); var seqnum = 0; var txin = Txin().set({ txidbuf: txidbuf, txoutnum: txoutnum, - varint: varint, + scriptvi: scriptvi, script: script, seqnum: seqnum }); @@ -33,13 +33,13 @@ describe('Txin', function() { var txin = Txin().set({ txidbuf: txidbuf, txoutnum: txoutnum, - varint: varint, + scriptvi: scriptvi, script: script, seqnum: seqnum }); should.exist(txin.txidbuf); should.exist(txin.txoutnum); - should.exist(txin.varint); + should.exist(txin.scriptvi); should.exist(txin.script); should.exist(txin.seqnum); }); @@ -52,7 +52,7 @@ describe('Txin', function() { var hex = '00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000'; var buf = new Buffer(hex, 'hex'); var txin = Txin().fromBuffer(buf); - txin.varint.toNumber().should.equal(1); + txin.scriptvi.toNumber().should.equal(1); txin.script.toString().should.equal('OP_CHECKMULTISIG'); }); @@ -65,7 +65,7 @@ describe('Txin', function() { var buf = new Buffer(hex, 'hex'); var br = BufferReader(buf); var txin = Txin().fromBufferReader(br); - txin.varint.toNumber().should.equal(1); + txin.scriptvi.toNumber().should.equal(1); txin.script.toString().should.equal('OP_CHECKMULTISIG'); }); diff --git a/test/txout.js b/test/txout.js index cfc05fc..59a9fb9 100644 --- a/test/txout.js +++ b/test/txout.js @@ -17,10 +17,10 @@ describe('Txout', function() { var valuebn = BN(5); var script = Script().fromString("OP_CHECKMULTISIG"); - var varint = Varint(script.toBuffer().length); + var scriptvi = Varint(script.toBuffer().length); var txout = new Txout().set({ valuebn: valuebn, - varint: varint, + scriptvi: scriptvi, script: script }); @@ -29,11 +29,11 @@ describe('Txout', function() { it('should set this object', function() { var txout = new Txout().set({ valuebn: valuebn, - varint: varint, + scriptvi: scriptvi, script: script }); should.exist(txout.valuebn); - should.exist(txout.varint); + should.exist(txout.scriptvi); should.exist(txout.script); }); From 1fa5482518a3a69f480e526ebdf9efda09f354ff Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 16:56:36 -0700 Subject: [PATCH 237/280] add travis file ...for travis automated testing --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..35048b6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: +- '0.10' From 6fe5d416a00d28c87948b3f3d2338a7991aace01 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 17:00:32 -0700 Subject: [PATCH 238/280] fix capitalization error on case-sensitive filesystems --- test/stealthaddress.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/stealthaddress.js b/test/stealthaddress.js index 6fcc6e5..0c8f56d 100644 --- a/test/stealthaddress.js +++ b/test/stealthaddress.js @@ -5,7 +5,7 @@ var Keypair = require('../lib/keypair'); var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); var BN = require('../lib/bn'); -var Hash = require('../lib/Hash'); +var Hash = require('../lib/hash'); var Base58check = require('../lib/base58check'); describe('StealthAddress', function() { From d2bc960744f8308b969455c9f37c00c386f36285 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 17:02:40 -0700 Subject: [PATCH 239/280] fix capitalization error on case-sensitive filesystems --- test/stealthmessage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/stealthmessage.js b/test/stealthmessage.js index 2fd3769..7951081 100644 --- a/test/stealthmessage.js +++ b/test/stealthmessage.js @@ -1,7 +1,7 @@ var Keypair = require('../lib/keypair'); var StealthMessage = require('../lib/expmt/stealthmessage'); var Stealthkey = require('../lib/expmt/stealthkey'); -var StealthAddress = require('../lib/expmt/stealthAddress'); +var StealthAddress = require('../lib/expmt/stealthaddress'); var KDF = require('../lib/kdf'); var Hash = require('../lib/hash'); var should = require('chai').should(); From 60459b6c7aabc0d09eef6bf18ca8c7ce1cc37b79 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 17 Sep 2014 17:36:59 -0700 Subject: [PATCH 240/280] expose Block, Blockheader, Transaction, Txin, Txout --- index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/index.js b/index.js index ee1c702..5f5a29d 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,8 @@ bitcore.Address = require('./lib/address'); bitcore.Base58 = require('./lib/base58'); bitcore.Base58Check = require('./lib/base58check'); bitcore.BIP32 = require('./lib/bip32'); +bitcore.Block = require('./lib/block'); +bitcore.Blockheader = require('./lib/blockheader'); bitcore.BN = require('./lib/bn'); bitcore.BufferReader = require('./lib/bufferreader'); bitcore.BufferWriter = require('./lib/bufferwriter'); @@ -21,6 +23,9 @@ bitcore.Pubkey = require('./lib/pubkey'); bitcore.Random = require('./lib/random'); bitcore.Script = require('./lib/script'); bitcore.Signature = require('./lib/signature'); +bitcore.Transaction = require('./lib/transaction'); +bitcore.Txin = require('./lib/txin'); +bitcore.Txout = require('./lib/txout'); bitcore.Varint = require('./lib/varint'); //experimental, nonstandard, or unstable features From 2db942d65aa6f08aaf57b36618ee16cfddb9e16a Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Thu, 18 Sep 2014 05:58:08 -0400 Subject: [PATCH 241/280] Minor: spelling --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ef74cb..1eb6887 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ fromObject methods. 4) All standard features of the bitcoin protocol are implemented and saved in lib/. All BIPs are correctly implemented and saved as BIPxx.js in lib/ (since that is their standard name). Any non-standard features (such as SINs and -stealtha addresses) are placed in the lib/expmt/ folder and are accessible at +stealth addresses) are placed in the lib/expmt/ folder and are accessible at bitcore.expmt. Once they are standardized and given a BIP, they are renamed and placed in lib/. From f17d604e44fa0a699603662754b1d1d3fd0431fa Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 18 Sep 2014 15:18:53 -0700 Subject: [PATCH 242/280] Address().fromHashbuf() convenience method ...useful for when you have the pubkeyhash, but not the version byte. --- lib/address.js | 9 +++++++++ test/address.js | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/lib/address.js b/lib/address.js index 5a066ee..6103098 100644 --- a/lib/address.js +++ b/lib/address.js @@ -51,6 +51,15 @@ Address.prototype.fromBuffer = function(buf) { return this; }; +Address.prototype.fromHashbuf = function(hashbuf, networkstr, typestr) { + if (hashbuf.length !== 20) + throw new Error('hashbuf must be exactly 20 bytes'); + this.hashbuf = hashbuf; + this.networkstr = networkstr || 'mainnet'; + this.typestr = typestr || 'pubkeyhash'; + return this; +}; + Address.prototype.fromPubkey = function(pubkey, networkstr) { this.hashbuf = Hash.sha256ripemd160(pubkey.toBuffer()); this.networkstr = networkstr || 'mainnet'; diff --git a/test/address.js b/test/address.js index e1bacf0..7a95fc8 100644 --- a/test/address.js +++ b/test/address.js @@ -38,6 +38,23 @@ describe('Address', function() { }); + describe('#fromHashbuf', function() { + + it('should make an address from a hashbuf', function() { + Address().fromHashbuf(pubkeyhash).toString().should.equal(str); + var a = Address().fromHashbuf(pubkeyhash, 'testnet', 'scripthash'); + a.networkstr.should.equal('testnet'); + a.typestr.should.equal('scripthash'); + }); + + it('should throw an error for invalid length hashbuf', function() { + (function() { + Address().fromHashbuf(buf); + }).should.throw('hashbuf must be exactly 20 bytes'); + }); + + }); + describe('#fromPubkey', function() { it('should make this address from a compressed pubkey', function() { From 2ecf1cdcdffe0850fb64201a7452ef913086d101 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 18 Sep 2014 15:20:23 -0700 Subject: [PATCH 243/280] Block parsing example bitcoind saves blocks in files called blk*****.dat. Those files can be piped into this example, which will parse them and spit out a nice looking string of all the blocks, which also includes parsed transactions. --- examples/blockreader.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 examples/blockreader.js diff --git a/examples/blockreader.js b/examples/blockreader.js new file mode 100644 index 0000000..3640ea0 --- /dev/null +++ b/examples/blockreader.js @@ -0,0 +1,20 @@ +var Block = require('../lib/block'); +var BufferReader = require('../lib/bufferreader'); +var BufferWriter = require('../lib/bufferwriter'); + +//This example will parse the blocks in a block file. +//To use, pipe in a blk*****.dat file. e.g.: +//cat blk00000.dat | node blockreader.js + +var bw = new BufferWriter(); + +process.stdin.on('data', function(buf) { + bw.write(buf); +}); + +process.stdin.on('end', function(buf) { + var blocksbuf = bw.concat(); + var br = new BufferReader(blocksbuf); + while (!br.eof()) + console.log(Block().fromBufferReader(br)); +}); From e11019a083648400df0202424f5630858e39e9d8 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 18 Sep 2014 17:52:02 -0700 Subject: [PATCH 244/280] toJSON, fromJSON Every object should have toJSON and fromJSON methods so you can have a reliable way to store and retrieve objects. --- lib/block.js | 29 +++++++++++++++++++++++++++++ lib/blockheader.js | 23 +++++++++++++++++++++++ lib/bn.js | 10 ++++++++++ lib/script.js | 8 ++++++++ lib/transaction.js | 39 +++++++++++++++++++++++++++++++++++++++ lib/txin.js | 21 +++++++++++++++++++++ lib/txout.js | 17 +++++++++++++++++ lib/varint.js | 11 +++++++++++ test/block.js | 32 ++++++++++++++++++++++++++++++++ test/blockheader.js | 35 +++++++++++++++++++++++++++++++++++ test/bn.js | 17 +++++++++++++++++ test/script.js | 16 ++++++++++++++++ test/transaction.js | 35 +++++++++++++++++++++++++++++++++++ test/txin.js | 26 ++++++++++++++++++++++++++ test/txout.js | 31 +++++++++++++++++++++++++++++++ test/varint.js | 20 ++++++++++++++++++++ 16 files changed, 370 insertions(+) diff --git a/lib/block.js b/lib/block.js index 50249b3..3db8f2a 100644 --- a/lib/block.js +++ b/lib/block.js @@ -29,6 +29,35 @@ Block.prototype.set = function(obj) { return this; }; +Block.prototype.fromJSON = function(json) { + var txs = []; + json.txs.forEach(function(tx) { + txs.push(Transaction().fromJSON(tx)); + }); + this.set({ + magicnum: json.magicnum, + blocksize: json.blocksize, + blockheader: Blockheader().fromJSON(json.blockheader), + txsvi: Varint().fromJSON(json.txsvi), + txs: txs + }); + return this; +}; + +Block.prototype.toJSON = function() { + var txs = []; + this.txs.forEach(function(tx) { + txs.push(tx.toJSON()); + }); + return { + magicnum: this.magicnum, + blocksize: this.blocksize, + blockheader: this.blockheader.toJSON(), + txsvi: this.txsvi.toJSON(), + txs: txs + }; +}; + Block.prototype.fromBuffer = function(buf) { return this.fromBufferReader(BufferReader(buf)); }; diff --git a/lib/blockheader.js b/lib/blockheader.js index d4ffc89..0d047a0 100644 --- a/lib/blockheader.js +++ b/lib/blockheader.js @@ -29,6 +29,29 @@ Blockheader.prototype.set = function(obj) { return this; }; +Blockheader.prototype.fromJSON = function(json) { + this.set({ + version: json.version, + prevblockidbuf: new Buffer(json.prevblockidbuf, 'hex'), + merklerootbuf: new Buffer(json.merklerootbuf, 'hex'), + time: json.time, + bits: json.bits, + nonce: json.nonce + }); + return this; +}; + +Blockheader.prototype.toJSON = function() { + return { + version: this.version, + prevblockidbuf: this.prevblockidbuf.toString('hex'), + merklerootbuf: this.merklerootbuf.toString('hex'), + time: this.time, + bits: this.bits, + nonce: this.nonce + }; +}; + Blockheader.prototype.fromBuffer = function(buf) { return this.fromBufferReader(BufferReader(buf)); }; diff --git a/lib/bn.js b/lib/bn.js index b841a51..4754556 100644 --- a/lib/bn.js +++ b/lib/bn.js @@ -18,6 +18,16 @@ var reversebuf = function(buf, nbuf) { } }; +BN.prototype.toJSON = function() { + return this.toString(); +}; + +BN.prototype.fromJSON = function(str) { + var bn = BN(str); + bn.copy(this); + return this; +}; + BN.prototype.fromString = function(str) { var bn = BN(str); bn.copy(this); diff --git a/lib/script.js b/lib/script.js index affa32a..18bd2b8 100644 --- a/lib/script.js +++ b/lib/script.js @@ -26,6 +26,14 @@ Script.prototype.set = function(obj) { return this; }; +Script.prototype.fromJSON = function(json) { + return this.fromString(json); +}; + +Script.prototype.toJSON = function() { + return this.toString(); +}; + Script.prototype.fromBuffer = function(buf) { this.chunks = []; diff --git a/lib/transaction.js b/lib/transaction.js index 55e7af6..e7762a2 100644 --- a/lib/transaction.js +++ b/lib/transaction.js @@ -32,6 +32,45 @@ Transaction.prototype.set = function(obj) { return this; }; +Transaction.prototype.fromJSON = function(json) { + var txins = []; + json.txins.forEach(function(txin) { + txins.push(Txin().fromJSON(txin)); + }); + var txouts = []; + json.txouts.forEach(function(txout) { + txouts.push(Txout().fromJSON(txout)); + }); + this.set({ + version: json.version, + txinsvi: Varint().fromJSON(json.txinsvi), + txins: txins, + txoutsvi: Varint().fromJSON(json.txoutsvi), + txouts: txouts, + nlocktime: json.nlocktime + }); + return this; +}; + +Transaction.prototype.toJSON = function() { + var txins = []; + this.txins.forEach(function(txin) { + txins.push(txin.toJSON()); + }); + var txouts = []; + this.txouts.forEach(function(txout) { + txouts.push(txout.toJSON()); + }); + return { + version: this.version, + txinsvi: this.txinsvi.toJSON(), + txins: txins, + txoutsvi: this.txoutsvi.toJSON(), + txouts: txouts, + nlocktime: this.nlocktime + }; +}; + Transaction.prototype.fromBuffer = function(buf) { return this.fromBufferReader(BufferReader(buf)); }; diff --git a/lib/txin.js b/lib/txin.js index bb36f8a..0441745 100644 --- a/lib/txin.js +++ b/lib/txin.js @@ -27,6 +27,27 @@ Txin.prototype.set = function(obj) { return this; }; +Txin.prototype.fromJSON = function(json) { + this.set({ + txidbuf: new Buffer(json.txidbuf, 'hex'), + txoutnum: json.txoutnum, + scriptvi: Varint().fromJSON(json.scriptvi), + script: Script().fromJSON(json.script), + seqnum: json.seqnum + }); + return this; +}; + +Txin.prototype.toJSON = function() { + return { + txidbuf: this.txidbuf.toString('hex'), + txoutnum: this.txoutnum, + scriptvi: this.scriptvi.toJSON(), + script: this.script.toJSON(), + seqnum: this.seqnum + }; +}; + Txin.prototype.fromBuffer = function(buf) { return this.fromBufferReader(BufferReader(buf)); }; diff --git a/lib/txout.js b/lib/txout.js index ba64457..05a4c73 100644 --- a/lib/txout.js +++ b/lib/txout.js @@ -26,6 +26,23 @@ Txout.prototype.set = function(obj) { return this; }; +Txout.prototype.fromJSON = function(json) { + this.set({ + valuebn: BN().fromJSON(json.valuebn), + scriptvi: Varint().fromJSON(json.scriptvi), + script: Script().fromJSON(json.script) + }); + return this; +}; + +Txout.prototype.toJSON = function() { + return { + valuebn: this.valuebn.toJSON(), + scriptvi: this.scriptvi.toJSON(), + script: this.script.toJSON() + }; +}; + Txout.prototype.fromBuffer = function(buf) { return this.fromBufferReader(BufferReader(buf)); }; diff --git a/lib/varint.js b/lib/varint.js index c114ae2..e959f2f 100644 --- a/lib/varint.js +++ b/lib/varint.js @@ -21,6 +21,17 @@ Varint.prototype.set = function(obj) { return this; }; +Varint.prototype.fromJSON = function(json) { + this.set({ + buf: new Buffer(json, 'hex') + }); + return this; +}; + +Varint.prototype.toJSON = function() { + return this.buf.toString('hex'); +}; + Varint.prototype.fromBuffer = function(buf) { this.buf = buf; return this; diff --git a/test/block.js b/test/block.js index dd8e907..55aa051 100644 --- a/test/block.js +++ b/test/block.js @@ -55,6 +55,38 @@ describe('Block', function() { }); + describe('#fromJSON', function() { + + it('should set these known values', function() { + var block = Block().set({ + magicnum: magicnum, + blocksize: blocksize, + blockheader: bh.toJSON(), + txsvi: txsvi.toJSON(), + txs: [txs[0].toJSON()] + }); + should.exist(block.magicnum); + should.exist(block.blocksize); + should.exist(block.blockheader); + should.exist(block.txsvi); + should.exist(block.txs); + }); + + }); + + describe('#toJSON', function() { + + it('should recover these known values', function() { + var json = block.toJSON(); + should.exist(json.magicnum); + should.exist(json.blocksize); + should.exist(json.blockheader); + should.exist(json.txsvi); + should.exist(json.txs); + }); + + }); + describe('#fromBuffer', function() { it('should make a block from this known buffer', function() { diff --git a/test/blockheader.js b/test/blockheader.js index 03a335f..a4d7c24 100644 --- a/test/blockheader.js +++ b/test/blockheader.js @@ -53,6 +53,41 @@ describe('Blockheader', function() { }); + describe('#fromJSON', function() { + + it('should set all the variables', function() { + var bh = Blockheader().fromJSON({ + version: version, + prevblockidbuf: prevblockidbuf.toString('hex'), + merklerootbuf: merklerootbuf.toString('hex'), + time: time, + bits: bits, + nonce: nonce + }); + should.exist(bh.version); + should.exist(bh.prevblockidbuf); + should.exist(bh.merklerootbuf); + should.exist(bh.time); + should.exist(bh.bits); + should.exist(bh.nonce); + }); + + }); + + describe('#toJSON', function() { + + it('should set all the variables', function() { + var json = bh.toJSON(); + should.exist(json.version); + should.exist(json.prevblockidbuf); + should.exist(json.merklerootbuf); + should.exist(json.time); + should.exist(json.bits); + should.exist(json.nonce); + }); + + }); + describe('#fromBuffer', function() { it('should parse this known buffer', function() { diff --git a/test/bn.js b/test/bn.js index dbf8d5e..03ef279 100644 --- a/test/bn.js +++ b/test/bn.js @@ -66,6 +66,23 @@ describe('BN', function() { }); + describe('#fromJSON', function() { + + it('should make BN from a string', function() { + BN().fromJSON('5').toString().should.equal('5'); + }); + + }); + + describe('#toJSON', function() { + + it('should make string from a BN', function() { + BN(5).toJSON().should.equal('5'); + BN().fromJSON('5').toJSON().should.equal('5'); + }); + + }); + describe('#fromString', function() { it('should make BN from a string', function() { diff --git a/test/script.js b/test/script.js index a8af6ca..739f7dd 100644 --- a/test/script.js +++ b/test/script.js @@ -180,4 +180,20 @@ describe('Script', function() { }); + describe('#fromJSON', function() { + + it('should parse this known script', function() { + Script().fromJSON('OP_0 OP_PUSHDATA4 3 0x010203 OP_0').toString().should.equal('OP_0 OP_PUSHDATA4 3 0x010203 OP_0'); + }); + + }); + + describe('#toJSON', function() { + + it('should output this known script', function() { + Script().fromString('OP_0 OP_PUSHDATA4 3 0x010203 OP_0').toJSON().should.equal('OP_0 OP_PUSHDATA4 3 0x010203 OP_0'); + }); + + }); + }); diff --git a/test/transaction.js b/test/transaction.js index 9a9ef4c..ba6f6fe 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -53,6 +53,41 @@ describe('Transaction', function() { }); + describe('#fromJSON', function() { + + it('should set all the basic parameters', function() { + var tx = Transaction().fromJSON({ + version: 0, + txinsvi: Varint(1).toJSON(), + txins: [txin.toJSON()], + txoutsvi: Varint(1).toJSON(), + txouts: [txout.toJSON()], + nlocktime: 0 + }); + should.exist(tx.version); + should.exist(tx.txinsvi); + should.exist(tx.txins); + should.exist(tx.txoutsvi); + should.exist(tx.txouts); + should.exist(tx.nlocktime); + }); + + }); + + describe('#toJSON', function() { + + it('should recover all the basic parameters', function() { + var json = tx.toJSON(); + should.exist(json.version); + should.exist(json.txinsvi); + should.exist(json.txins); + should.exist(json.txoutsvi); + should.exist(json.txouts); + should.exist(json.nlocktime); + }); + + }); + describe('#fromBuffer', function() { it('should recover from this known tx', function() { diff --git a/test/txin.js b/test/txin.js index f441f63..92d386a 100644 --- a/test/txin.js +++ b/test/txin.js @@ -46,6 +46,32 @@ describe('Txin', function() { }); + describe('#fromJSON', function() { + + it('should set these vars', function() { + var txin2 = Txin().fromJSON(txin.toJSON()); + should.exist(txin2.txidbuf); + should.exist(txin2.txoutnum); + should.exist(txin2.scriptvi); + should.exist(txin2.script); + should.exist(txin2.seqnum); + }); + + }); + + describe('#toJSON', function() { + + it('should set these vars', function() { + var json = txin.toJSON() + should.exist(json.txidbuf); + should.exist(json.txoutnum); + should.exist(json.scriptvi); + should.exist(json.script); + should.exist(json.seqnum); + }); + + }); + describe('#fromBuffer', function() { it('should convert this known buffer', function() { diff --git a/test/txout.js b/test/txout.js index 59a9fb9..fc3cd9a 100644 --- a/test/txout.js +++ b/test/txout.js @@ -39,6 +39,37 @@ describe('Txout', function() { }); + describe('#fromJSON', function() { + + it('should set from this json', function() { + var txout = Txout().fromJSON({ + valuebn: valuebn.toJSON(), + scriptvi: scriptvi.toJSON(), + script: script.toJSON() + }); + should.exist(txout.valuebn); + should.exist(txout.scriptvi); + should.exist(txout.script); + }); + + }); + + describe('#toJSON', function() { + + it('should return this json', function() { + var txout = Txout().fromJSON({ + valuebn: valuebn.toJSON(), + scriptvi: scriptvi.toJSON(), + script: script.toJSON() + }); + var json = txout.toJSON(); + should.exist(json.valuebn); + should.exist(json.scriptvi); + should.exist(json.script); + }); + + }); + describe('#fromBuffer', function() { it('should make this txin from this known buffer', function() { diff --git a/test/varint.js b/test/varint.js index 23e1934..b19ae95 100644 --- a/test/varint.js +++ b/test/varint.js @@ -28,6 +28,26 @@ describe('Varint', function() { }); + describe('#fromJSON', function() { + + it('should set a buffer', function() { + var buf = BufferWriter().writeVarintNum(5).concat(); + var varint = Varint().fromJSON(buf.toString('hex')); + varint.toNumber().should.equal(5); + }); + + }); + + describe('#toJSON', function() { + + it('should return a buffer', function() { + var buf = BufferWriter().writeVarintNum(5).concat(); + var varint = Varint().fromJSON(buf.toString('hex')); + varint.toJSON().should.equal('05'); + }); + + }); + describe('#fromBuffer', function() { it('should set a buffer', function() { From 04887db261efa6cc059d8ec343e42ffc8590d626 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 18 Sep 2014 17:52:43 -0700 Subject: [PATCH 245/280] update blockreader example to use toJSON --- examples/blockreader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/blockreader.js b/examples/blockreader.js index 3640ea0..33d4c5e 100644 --- a/examples/blockreader.js +++ b/examples/blockreader.js @@ -16,5 +16,5 @@ process.stdin.on('end', function(buf) { var blocksbuf = bw.concat(); var br = new BufferReader(blocksbuf); while (!br.eof()) - console.log(Block().fromBufferReader(br)); + console.log(JSON.stringify(Block().fromBufferReader(br).toJSON(), null, 2)); }); From 199ed7c4ba0967f59e6e70d530c6e352629a0c2d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 18 Sep 2014 18:46:31 -0700 Subject: [PATCH 246/280] update dependencies --- npm-shrinkwrap.json | 54 ++++++++------------------------------------- package.json | 4 ++-- 2 files changed, 11 insertions(+), 47 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 6bb724d..2c57469 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -8,9 +8,9 @@ "resolved": "https://registry.npmjs.org/aes/-/aes-0.1.0.tgz" }, "bn.js": { - "version": "0.13.3", - "from": "bn.js@=0.13.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.13.3.tgz" + "version": "0.14.0", + "from": "bn.js@0.14.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.14.0.tgz" }, "bs58": { "version": "1.2.1", @@ -18,65 +18,29 @@ "resolved": "https://registry.npmjs.org/bs58/-/bs58-1.2.1.tgz" }, "elliptic": { - "version": "0.15.7", - "from": "elliptic@=0.15.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-0.15.7.tgz", + "version": "0.15.10", + "from": "elliptic@0.15.10", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-0.15.10.tgz", "dependencies": { "bn.js": { "version": "0.11.7", - "from": "bn.js@^0.11.6", + "from": "bn.js@0.11.7", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-0.11.7.tgz" }, "hash.js": { "version": "0.2.1", - "from": "hash.js@^0.2.0", + "from": "hash.js@0.2.1", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-0.2.1.tgz" }, "inherits": { "version": "2.0.1", "from": "inherits@^2.0.1" - }, - "uglify-js": { - "version": "2.4.15", - "from": "uglify-js@^2.4.13", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.15.tgz", - "dependencies": { - "async": { - "version": "0.2.10", - "from": "async@~0.2.6" - }, - "source-map": { - "version": "0.1.34", - "from": "source-map@0.1.34", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", - "dependencies": { - "amdefine": { - "version": "0.1.0", - "from": "amdefine@>=0.0.4" - } - } - }, - "optimist": { - "version": "0.3.7", - "from": "optimist@~0.3.5", - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "from": "wordwrap@~0.0.2" - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "from": "uglify-to-browserify@~1.0.0" - } - } } } }, "hash.js": { "version": "0.3.1", - "from": "hash.js@=0.3.1", + "from": "hash.js@0.3.1", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-0.3.1.tgz", "dependencies": { "inherits": { diff --git a/package.json b/package.json index 981732f..374bf34 100644 --- a/package.json +++ b/package.json @@ -63,9 +63,9 @@ }, "dependencies": { "aes": "=0.1.0", - "bn.js": "=0.13.3", + "bn.js": "=0.14.0", "bs58": "=1.2.1", - "elliptic": "=0.15.7", + "elliptic": "=0.15.10", "hash.js": "=0.3.1", "sha512": "=0.0.1" }, From 7647663a5ec9e8797cb2793bb090acf81fc967d2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 19 Sep 2014 14:02:44 -0700 Subject: [PATCH 247/280] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ef74cb..1eb6887 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ fromObject methods. 4) All standard features of the bitcoin protocol are implemented and saved in lib/. All BIPs are correctly implemented and saved as BIPxx.js in lib/ (since that is their standard name). Any non-standard features (such as SINs and -stealtha addresses) are placed in the lib/expmt/ folder and are accessible at +stealth addresses) are placed in the lib/expmt/ folder and are accessible at bitcore.expmt. Once they are standardized and given a BIP, they are renamed and placed in lib/. From cfd509f8591fd611d460b6dcb0456eb608b1ff3c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 19 Sep 2014 14:32:32 -0700 Subject: [PATCH 248/280] make receivePubkey compatible with dark wallet I had been using this formula for the receiveKeypair: scanKeypair + payloadKeypair + sharedKeypair However, Dark Wallet uses this formula: payloadKeypair + sharedKeypair It is not actually necessary to add the scanKeypair in order to have all the features of stealth addresses, at least as far as I can tell. So in order to bring my implementation closer to Dark Wallet's, I have removed the scanKeypair from this calculation. --- lib/expmt/stealthaddress.js | 2 +- lib/expmt/stealthkey.js | 4 ++-- test/stealthkey.js | 4 ++-- test/stealthmessage.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/expmt/stealthaddress.js b/lib/expmt/stealthaddress.js index 2c5cb8b..0d82128 100644 --- a/lib/expmt/stealthaddress.js +++ b/lib/expmt/stealthaddress.js @@ -65,7 +65,7 @@ StealthAddress.prototype.getSharedKeypair = function(senderKeypair) { StealthAddress.prototype.getReceivePubkey = function(senderKeypair) { var sharedKeypair = this.getSharedKeypair(senderKeypair); - var pubkey = Pubkey(this.scanPubkey.point.add(sharedKeypair.pubkey.point).add(this.payloadPubkey.point)); + var pubkey = Pubkey(this.payloadPubkey.point.add(sharedKeypair.pubkey.point)); return pubkey; }; diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index 385983b..2570c77 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -46,14 +46,14 @@ Stealthkey.prototype.getSharedKeypair = function(senderPubkey) { Stealthkey.prototype.getReceivePubkey = function(senderPubkey) { var sharedKeypair = this.getSharedKeypair(senderPubkey); - var pubkey = Pubkey({point: this.scanKeypair.pubkey.point.add(sharedKeypair.pubkey.point).add(this.payloadKeypair.pubkey.point)}); + var pubkey = Pubkey({point: this.payloadKeypair.pubkey.point.add(sharedKeypair.pubkey.point)}); return pubkey; }; Stealthkey.prototype.getReceiveKeypair = function(senderPubkey) { var sharedKeypair = this.getSharedKeypair(senderPubkey); - var privkey = Privkey({bn: this.scanKeypair.privkey.bn.add(sharedKeypair.privkey.bn).add(this.payloadKeypair.privkey.bn).mod(Point.getN())}); + var privkey = Privkey({bn: this.payloadKeypair.privkey.bn.add(sharedKeypair.privkey.bn).mod(Point.getN())}); var key = Keypair({privkey: privkey}); key.privkey2pubkey(); diff --git a/test/stealthkey.js b/test/stealthkey.js index 69b8b22..472cd75 100644 --- a/test/stealthkey.js +++ b/test/stealthkey.js @@ -103,12 +103,12 @@ describe('Stealthkey', function() { describe('#isForMe', function() { it('should return true if it (the transaction or message) is for me', function() { - var pubkeyhash = new Buffer('0db7f06cffb913322e211e8b0688efe8ff52aa8d', 'hex'); + var pubkeyhash = new Buffer('3cb64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(true); }); it('should return false if it (the transaction or message) is not for me', function() { - var pubkeyhash = new Buffer('ffb7f06cffb913322e211e8b0688efe8ff52aa8d', 'hex'); + var pubkeyhash = new Buffer('00b64fa6ee9b3e8754e3e2bd033bf61048604a99', 'hex'); stealthkey.isForMe(senderKeypair.pubkey, pubkeyhash).should.equal(false); }); diff --git a/test/stealthmessage.js b/test/stealthmessage.js index 7951081..16b2139 100644 --- a/test/stealthmessage.js +++ b/test/stealthmessage.js @@ -12,7 +12,7 @@ describe('StealthMessage', function() { var payloadKeypair = KDF.buf2keypair(new Buffer('key1')); var scanKeypair = KDF.buf2keypair(new Buffer('key2')); var fromKeypair = KDF.buf2keypair(new Buffer('key3')); - var enchex = '3867dce7be22e8551512b25f329b51fd5fe8ecfe0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec9f86d081884c7d659a2feaa0c55ad015aca97de5af3a34a0f47eee0a1f7774dcb759187555003282bd23b047bceb5b2f2c1ee35b8f0be1fda7a41424f6cc8030559c8c32ea8cc812860f4c8123f1417a'; + var enchex = 'f557994f16d0d628fa4fdb4ab3d7e0bc5f2754f20381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec0381c7831a20c7c9ec88dcf092ea3683261798ccda991ed65a3a54a036d8125dec9f86d081884c7d659a2feaa0c55ad01560ba2904d3bc8395b6c4a6f87648edb33db6a22170e5e26f340c7ba943169210234cd6a753ad13919b0ab7d678b47b5e7d63e452382de2c2590fb57ef048f7b3'; var encbuf = new Buffer(enchex, 'hex'); var ivbuf = Hash.sha256(new Buffer('test')).slice(0, 128 / 8); var sk = Stealthkey().set({payloadKeypair: payloadKeypair, scanKeypair: scanKeypair}); From f191e93711393d83e2e65b5ee2d5e60cc2586e59 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 19 Sep 2014 16:40:29 -0700 Subject: [PATCH 249/280] make block reading more efficient ...by using streams. This way we don't load all the blocks before parsing them. We parse them as we go. --- examples/blockreader.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/examples/blockreader.js b/examples/blockreader.js index 33d4c5e..4c3db6c 100644 --- a/examples/blockreader.js +++ b/examples/blockreader.js @@ -6,15 +6,22 @@ var BufferWriter = require('../lib/bufferwriter'); //To use, pipe in a blk*****.dat file. e.g.: //cat blk00000.dat | node blockreader.js -var bw = new BufferWriter(); +var head = null; +var body = null; -process.stdin.on('data', function(buf) { - bw.write(buf); -}); - -process.stdin.on('end', function(buf) { - var blocksbuf = bw.concat(); - var br = new BufferReader(blocksbuf); - while (!br.eof()) - console.log(JSON.stringify(Block().fromBufferReader(br).toJSON(), null, 2)); +process.stdin.on('readable', function() { + if (!head) { + head = process.stdin.read(8); + if (!head) + return; + } + var body = process.stdin.read(head.slice(4).readUInt32LE(0)); + if (!body) + return; + var blockbuf = BufferWriter().write(head).write(body).concat(); + var block = Block().fromBuffer(blockbuf); + console.log(block.toJSON()); + head = null; + body = null; + process.stdin.unshift(process.stdin.read()); }); From 40d17c5180c563fe2bf930c2a34d6f9f875a68cb Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 19 Sep 2014 17:08:44 -0700 Subject: [PATCH 250/280] block id The block id is the reverse of the double sha256 hash of the blockheader. --- lib/block.js | 5 +++++ test/block.js | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/block.js b/lib/block.js index 3db8f2a..ec0b994 100644 --- a/lib/block.js +++ b/lib/block.js @@ -3,6 +3,7 @@ var BufferReader = require('./bufferreader'); var BufferWriter = require('./bufferwriter'); var Blockheader = require('./blockheader'); var Varint = require('./varint'); +var Hash = require('./hash'); var Block = function Block(magicnum, blocksize, blockheader, txsvi, txs) { if (!(this instanceof Block)) @@ -93,4 +94,8 @@ Block.prototype.toBufferWriter = function(bw) { return bw; }; +Block.prototype.id = function() { + return BufferReader(Hash.sha256sha256(this.blockheader.toBuffer())).reverse().read(); +}; + module.exports = Block; diff --git a/test/block.js b/test/block.js index 55aa051..5079cfa 100644 --- a/test/block.js +++ b/test/block.js @@ -36,6 +36,10 @@ describe('Block', function() { var blockhex = 'f9beb4d93200000001000000050505050505050505050505050505050505050505050505050505050505050509090909090909090909090909090909090909090909090909090909090909090200000003000000040000000101000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000'; var blockbuf = new Buffer(blockhex, 'hex'); + var genesishex = 'f9beb4d91d0100000100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000'; + var genesisbuf = new Buffer(genesishex, 'hex'); + var genesisidhex = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'; + describe('#set', function() { it('should set these known values', function() { @@ -123,4 +127,13 @@ describe('Block', function() { }); + describe('#id', function() { + + it('should return the correct id of the genesis block', function() { + var block = Block().fromBuffer(genesisbuf); + block.id().toString('hex').should.equal(genesisidhex); + }); + + }); + }); From aff3992ffb018e99c1fa60f36830db1509f9e2a2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 19 Sep 2014 17:25:35 -0700 Subject: [PATCH 251/280] transaction hash and id ...the hash is the usual hash, and the id is the reverse of that, which is what is usually graphically displayed by bitcoind. --- lib/transaction.js | 9 +++++++++ test/transaction.js | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/transaction.js b/lib/transaction.js index e7762a2..6b824ba 100644 --- a/lib/transaction.js +++ b/lib/transaction.js @@ -3,6 +3,7 @@ var Txout = require('./txout'); var BufferWriter = require('./bufferwriter'); var BufferReader = require('./bufferreader'); var Varint = require('./varint'); +var Hash = require('./hash'); var Transaction = function Transaction(version, txinsvi, txins, txoutsvi, txouts, nlocktime) { if (!(this instanceof Transaction)) @@ -113,4 +114,12 @@ Transaction.prototype.toBufferWriter = function(bw) { return bw; }; +Transaction.prototype.hash = function() { + return Hash.sha256sha256(this.toBuffer()); +}; + +Transaction.prototype.id = function() { + return BufferReader(this.hash()).reverse().read(); +}; + module.exports = Transaction; diff --git a/test/transaction.js b/test/transaction.js index ba6f6fe..e43f6e7 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -124,4 +124,23 @@ describe('Transaction', function() { }); + describe('#hash', function() { + + it('should correctly calculate the hash of this known transaction', function() { + var tx = Transaction().fromBuffer(tx2buf); + var txhashbuf = new Buffer(Array.apply([], new Buffer(tx2idhex, 'hex')).reverse()); + tx.hash().toString('hex').should.equal(txhashbuf.toString('hex')); + }); + + }); + + describe('#id', function() { + + it('should correctly calculate the id of this known transaction', function() { + var tx = Transaction().fromBuffer(tx2buf); + tx.id().toString('hex').should.equal(tx2idhex); + }); + + }); + }); From cc3196085fc722abc7bea7b2c080d3be404509a2 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 19 Sep 2014 17:29:40 -0700 Subject: [PATCH 252/280] add .hash() function for a block This is the plain old hash, which is a double sha256. The id of a block is the reverse of this. --- lib/block.js | 6 +++++- test/block.js | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/block.js b/lib/block.js index ec0b994..7481c18 100644 --- a/lib/block.js +++ b/lib/block.js @@ -94,8 +94,12 @@ Block.prototype.toBufferWriter = function(bw) { return bw; }; +Block.prototype.hash = function() { + return Hash.sha256sha256(this.blockheader.toBuffer()); +}; + Block.prototype.id = function() { - return BufferReader(Hash.sha256sha256(this.blockheader.toBuffer())).reverse().read(); + return BufferReader(this.hash()).reverse().read(); }; module.exports = Block; diff --git a/test/block.js b/test/block.js index 5079cfa..2e2040e 100644 --- a/test/block.js +++ b/test/block.js @@ -127,6 +127,16 @@ describe('Block', function() { }); + describe('#hash', function() { + + it('should return the correct hash of the genesis block', function() { + var block = Block().fromBuffer(genesisbuf); + var blockhash = new Buffer(Array.apply([], new Buffer(genesisidhex, 'hex')).reverse()); + block.hash().toString('hex').should.equal(blockhash.toString('hex')); + }); + + }); + describe('#id', function() { it('should return the correct id of the genesis block', function() { From d57613bc911216956c78393a088da17db928f7a0 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 19 Sep 2014 17:41:22 -0700 Subject: [PATCH 253/280] body is actually defined in the function --- examples/blockreader.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/blockreader.js b/examples/blockreader.js index 4c3db6c..b4cf61e 100644 --- a/examples/blockreader.js +++ b/examples/blockreader.js @@ -7,7 +7,6 @@ var BufferWriter = require('../lib/bufferwriter'); //cat blk00000.dat | node blockreader.js var head = null; -var body = null; process.stdin.on('readable', function() { if (!head) { @@ -22,6 +21,5 @@ process.stdin.on('readable', function() { var block = Block().fromBuffer(blockbuf); console.log(block.toJSON()); head = null; - body = null; process.stdin.unshift(process.stdin.read()); }); From ffdfe0ce83e68dc3a3ca4a49992f568a528ea677 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 19 Sep 2014 21:02:57 -0700 Subject: [PATCH 254/280] add fromString test for bip32 ...since it was lacking any tests --- test/bip32.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/bip32.js b/test/bip32.js index a0faac0..a843c48 100644 --- a/test/bip32.js +++ b/test/bip32.js @@ -319,6 +319,17 @@ describe('BIP32', function() { }); }); + describe('#fromString', function() { + + it('should make a bip32 from a string', function() { + var str = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'; + var bip32 = new BIP32().fromString(str); + should.exist(bip32); + bip32.toString().should.equal(str); + }); + + }); + describe('#toString', function() { var bip32 = new BIP32(); bip32.fromRandom('mainnet'); From 27fbdb42ad07f9e95d1fc3d5dabfc4daf8b0e32b Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 19 Sep 2014 21:59:19 -0700 Subject: [PATCH 255/280] isOpReturn standard OP_RETURN scripts contain either just an OP_RETURN or an OP_RETURN followed by a single pushdata OP with not more than 40 bytes. --- lib/script.js | 16 ++++++++++++++++ test/script.js | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/script.js b/lib/script.js index 18bd2b8..4b2f2aa 100644 --- a/lib/script.js +++ b/lib/script.js @@ -137,6 +137,8 @@ Script.prototype.fromString = function(str) { throw new Error('Invalid script'); } } else if (opcodenum === Opcode.map.OP_PUSHDATA1 || opcodenum === Opcode.map.OP_PUSHDATA2 || opcodenum === Opcode.map.OP_PUSHDATA4) { + if (tokens[i + 2].slice(0, 2) != '0x') + throw new Error('Pushdata data must start with 0x'); this.chunks.push({ buf: new Buffer(tokens[i + 2].slice(2), 'hex'), len: parseInt(tokens[i + 1]), @@ -171,4 +173,18 @@ Script.prototype.toString = function() { return str.substr(0, str.length - 1); }; +Script.prototype.isOpReturn = function() { + if (this.chunks[0] === Opcode('OP_RETURN').toNumber() + && + (this.chunks.length === 1 + || + (this.chunks.length === 2 + && this.chunks[1].buf + && this.chunks[1].buf.length <= 40 + && this.chunks[1].length === this.chunks.len))) + return true; + else + return false; +}; + module.exports = Script; diff --git a/test/script.js b/test/script.js index 739f7dd..11f98cc 100644 --- a/test/script.js +++ b/test/script.js @@ -196,4 +196,24 @@ describe('Script', function() { }); + describe('#isOpReturn', function() { + + it('should know this is a (blank) OP_RETURN script', function() { + Script('OP_RETURN').isOpReturn().should.equal(true); + }); + + it('should know this is an OP_RETURN script', function() { + var buf = new Buffer(40); + buf.fill(0); + Script('OP_RETURN 40 0x' + buf.toString('hex')).isOpReturn().should.equal(true); + }); + + it('should know this is not an OP_RETURN script', function() { + var buf = new Buffer(40); + buf.fill(0); + Script('OP_CHECKMULTISIG 40 0x' + buf.toString('hex')).isOpReturn().should.equal(false); + }); + + }); + }); From 1bda566679e36f27cd2a839d7ed8030c565a2fac Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 12:48:13 -0700 Subject: [PATCH 256/280] stealth address to/from JSON --- lib/expmt/stealthaddress.js | 9 +++++++++ test/stealthaddress.js | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/expmt/stealthaddress.js b/lib/expmt/stealthaddress.js index 0d82128..66ae9c6 100644 --- a/lib/expmt/stealthaddress.js +++ b/lib/expmt/stealthaddress.js @@ -26,6 +26,15 @@ StealthAddress.prototype.set = function(obj) { return this; }; +StealthAddress.prototype.fromJSON = function(json) { + this.fromString(json); + return this; +}; + +StealthAddress.prototype.toJSON = function() { + return this.toString(); +}; + StealthAddress.prototype.fromStealthkey = function(stealthkey) { this.set({ payloadPubkey: stealthkey.payloadKeypair.pubkey, diff --git a/test/stealthaddress.js b/test/stealthaddress.js index 0c8f56d..8ab30a9 100644 --- a/test/stealthaddress.js +++ b/test/stealthaddress.js @@ -38,6 +38,25 @@ describe('StealthAddress', function() { should.exist(sa); }); + describe('#fromJSON', function() { + + it('should give a stealthkey address with the right pubkeys', function() { + var sa = new StealthAddress(); + sa.fromJSON(addressString); + sa.payloadPubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); + sa.scanPubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); + }); + + }); + + describe('#toJSON', function() { + + it('should return this known address string', function() { + StealthAddress().fromJSON(addressString).toJSON().should.equal(addressString); + }); + + }); + describe('#fromBuffer', function() { it('should give a stealthkey address with the right pubkeys', function() { From e6a424303e5c6ed767a180a84effbcdb983789f1 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 12:51:18 -0700 Subject: [PATCH 257/280] remove unused base58check dependency --- lib/expmt/stealthkey.js | 1 - test/stealthkey.js | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index 2570c77..7cf49bd 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -4,7 +4,6 @@ var Pubkey = require('../pubkey'); var Point = require('../point'); var Hash = require('../hash'); var KDF = require('../kdf'); -var base58check = require('../base58check'); var Stealthkey = function Stealthkey(payloadKeypair, scanKeypair) { if (!(this instanceof Stealthkey)) diff --git a/test/stealthkey.js b/test/stealthkey.js index 472cd75..c785829 100644 --- a/test/stealthkey.js +++ b/test/stealthkey.js @@ -5,7 +5,6 @@ var Privkey = require('../lib/privkey'); var Pubkey = require('../lib/pubkey'); var BN = require('../lib/bn'); var Hash = require('../lib/hash'); -var base58check = require('../lib/base58check'); describe('Stealthkey', function() { From 1516ad3012a027f2346510a3d211488d84efd895 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 12:52:09 -0700 Subject: [PATCH 258/280] remove unused addressString variable --- test/stealthkey.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/stealthkey.js b/test/stealthkey.js index c785829..7f2d8a7 100644 --- a/test/stealthkey.js +++ b/test/stealthkey.js @@ -23,8 +23,6 @@ describe('Stealthkey', function() { senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); senderKeypair.privkey2pubkey(); - var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW'; - it('should create a new stealthkey', function() { var stealthkey = new Stealthkey(); should.exist(stealthkey); From e9847367364afe02576933cf8de276f3f34fe487 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 13:07:39 -0700 Subject: [PATCH 259/280] allow bufs in constructors For Transaction, Block and Blockheader. This is a convenience so if you happen to have the buffer for one of these, you can make a new one like this: Transaction(txbuf); Rather than having to do this: Transaction().fromBuffer(txbuf); --- lib/block.js | 3 +++ lib/blockheader.js | 3 +++ lib/transaction.js | 3 +++ lib/txin.js | 2 ++ test/block.js | 16 +++++++++------- test/blockheader.js | 15 ++++++++------- test/transaction.js | 15 ++++++++------- test/txin.js | 22 +++++++++++++++------- test/txout.js | 15 ++++++++------- 9 files changed, 59 insertions(+), 35 deletions(-) diff --git a/lib/block.js b/lib/block.js index 7481c18..4798945 100644 --- a/lib/block.js +++ b/lib/block.js @@ -16,6 +16,9 @@ var Block = function Block(magicnum, blocksize, blockheader, txsvi, txs) { txsvi: txsvi, txs: txs }); + } else if (Buffer.isBuffer(magicnum)) { + var blockbuf = magicnum; + this.fromBuffer(blockbuf); } else if (magicnum) { var obj = magicnum; } diff --git a/lib/blockheader.js b/lib/blockheader.js index 0d047a0..b24412e 100644 --- a/lib/blockheader.js +++ b/lib/blockheader.js @@ -13,6 +13,9 @@ var Blockheader = function Blockheader(version, prevblockidbuf, merklerootbuf, t bits: bits, nonce: nonce }); + } else if (Buffer.isBuffer(version)) { + var bhbuf = version; + this.fromBuffer(bhbuf); } else if (version) { var obj = version; this.set(obj); diff --git a/lib/transaction.js b/lib/transaction.js index 6b824ba..aad3f45 100644 --- a/lib/transaction.js +++ b/lib/transaction.js @@ -17,6 +17,9 @@ var Transaction = function Transaction(version, txinsvi, txins, txoutsvi, txouts txouts: txouts, nlocktime: nlocktime }); + } else if (Buffer.isBuffer(version)) { + var txbuf = version; + this.fromBuffer(txbuf); } else if (version) { var obj = version; this.set(obj); diff --git a/lib/txin.js b/lib/txin.js index 0441745..4eba87b 100644 --- a/lib/txin.js +++ b/lib/txin.js @@ -7,6 +7,8 @@ var Txin = function Txin(txidbuf, txoutnum, scriptvi, script, seqnum) { if (!(this instanceof Txin)) return new Txin(txidbuf, txoutnum, scriptvi, script, seqnum); if (Buffer.isBuffer(txidbuf)) { + if (txidbuf.length !== 32) + throw new Error('txidbuf must be 32 bytes'); this.txidbuf = txidbuf; this.txoutnum = txoutnum; this.scriptvi = scriptvi; diff --git a/test/block.js b/test/block.js index 2e2040e..85de6e9 100644 --- a/test/block.js +++ b/test/block.js @@ -8,13 +8,6 @@ var Transaction = require('../lib/transaction'); describe('Block', function() { - it('should make a new block', function() { - var block = new Block(); - should.exist(block); - block = Block(); - should.exist(block); - }); - var txidhex = '8c9aa966d35bfeaf031409e0001b90ccdafd8d859799eb945a3c515b8260bcf2'; var txhex = '01000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000'; var txbuf = new Buffer(txhex, 'hex'); @@ -40,6 +33,15 @@ describe('Block', function() { var genesisbuf = new Buffer(genesishex, 'hex'); var genesisidhex = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'; + it('should make a new block', function() { + var block = new Block(); + should.exist(block); + block = Block(); + should.exist(block); + block = Block(blockbuf); + block.toBuffer().toString('hex').should.equal(blockhex); + }); + describe('#set', function() { it('should set these known values', function() { diff --git a/test/blockheader.js b/test/blockheader.js index a4d7c24..6fec516 100644 --- a/test/blockheader.js +++ b/test/blockheader.js @@ -5,13 +5,6 @@ var should = require('chai').should(); describe('Blockheader', function() { - it('should make a new blockheader', function() { - var blockheader = new Blockheader(); - should.exist(blockheader); - blockheader = Blockheader(); - should.exist(blockheader); - }); - var bh = new Blockheader(); var version = 1; var prevblockidbuf = new Buffer(32); @@ -32,6 +25,14 @@ describe('Blockheader', function() { bhhex = '0100000005050505050505050505050505050505050505050505050505050505050505050909090909090909090909090909090909090909090909090909090909090909020000000300000004000000'; bhbuf = new Buffer(bhhex, 'hex'); + it('should make a new blockheader', function() { + var blockheader = new Blockheader(); + should.exist(blockheader); + blockheader = Blockheader(); + should.exist(blockheader); + Blockheader(bhbuf).toBuffer().toString('hex').should.equal(bhhex); + }); + describe('#set', function() { it('should set all the variables', function() { diff --git a/test/transaction.js b/test/transaction.js index e43f6e7..180f10a 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -8,13 +8,6 @@ var BufferWriter = require('../lib/bufferwriter'); describe('Transaction', function() { - it('should make a new transaction', function() { - var tx = new Transaction(); - should.exist(tx); - tx = Transaction(); - should.exist(tx); - }); - var txin = Txin().fromBuffer(new Buffer('00000000000000000000000000000000000000000000000000000000000000000000000001ae00000000', 'hex')); var txout = Txout().fromBuffer(new Buffer('050000000000000001ae', 'hex')); var tx = Transaction().set({ @@ -32,6 +25,14 @@ describe('Transaction', function() { var tx2hex = '01000000029e8d016a7b0dc49a325922d05da1f916d1e4d4f0cb840c9727f3d22ce8d1363f000000008c493046022100e9318720bee5425378b4763b0427158b1051eec8b08442ce3fbfbf7b30202a44022100d4172239ebd701dae2fbaaccd9f038e7ca166707333427e3fb2a2865b19a7f27014104510c67f46d2cbb29476d1f0b794be4cb549ea59ab9cc1e731969a7bf5be95f7ad5e7f904e5ccf50a9dc1714df00fbeb794aa27aaff33260c1032d931a75c56f2ffffffffa3195e7a1ab665473ff717814f6881485dc8759bebe97e31c301ffe7933a656f020000008b48304502201c282f35f3e02a1f32d2089265ad4b561f07ea3c288169dedcf2f785e6065efa022100e8db18aadacb382eed13ee04708f00ba0a9c40e3b21cf91da8859d0f7d99e0c50141042b409e1ebbb43875be5edde9c452c82c01e3903d38fa4fd89f3887a52cb8aea9dc8aec7e2c9d5b3609c03eb16259a2537135a1bf0f9c5fbbcbdbaf83ba402442ffffffff02206b1000000000001976a91420bb5c3bfaef0231dc05190e7f1c8e22e098991e88acf0ca0100000000001976a9149e3e2d23973a04ec1b02be97c30ab9f2f27c3b2c88ac00000000'; var tx2buf = new Buffer(tx2hex, 'hex'); + it('should make a new transaction', function() { + var tx = new Transaction(); + should.exist(tx); + tx = Transaction(); + should.exist(tx); + Transaction(txbuf).toBuffer().toString('hex').should.equal(txhex); + }); + describe('#set', function() { it('should set all the basic parameters', function() { diff --git a/test/txin.js b/test/txin.js index 92d386a..6a4a5b9 100644 --- a/test/txin.js +++ b/test/txin.js @@ -6,13 +6,6 @@ var BufferReader = require('../lib/bufferreader'); describe('Txin', function() { - it('should make a new txin', function() { - var txin = new Txin(); - should.exist(txin); - txin = Txin(); - should.exist(txin); - }); - var txidbuf = new Buffer(32); txidbuf.fill(0); var txoutnum = 0; @@ -27,6 +20,21 @@ describe('Txin', function() { seqnum: seqnum }); + it('should make a new txin', function() { + var txin = new Txin(); + should.exist(txin); + txin = Txin(); + should.exist(txin); + var txidbuf = new Buffer(32); + txidbuf.fill(0); + Txin(txidbuf).txidbuf.length.should.equal(32); + (function() { + var txidbuf2 = new Buffer(33); + txidbuf2.fill(0); + Txin(txidbuf2); + }).should.throw('txidbuf must be 32 bytes'); + }); + describe('#set', function() { it('should set these vars', function() { diff --git a/test/txout.js b/test/txout.js index fc3cd9a..2f18630 100644 --- a/test/txout.js +++ b/test/txout.js @@ -8,13 +8,6 @@ var BufferWriter = require('../lib/bufferwriter'); describe('Txout', function() { - it('should make a new txout', function() { - var txout = new Txout(); - should.exist(txout); - txout = Txout(); - should.exist(txout); - }); - var valuebn = BN(5); var script = Script().fromString("OP_CHECKMULTISIG"); var scriptvi = Varint(script.toBuffer().length); @@ -24,6 +17,14 @@ describe('Txout', function() { script: script }); + it('should make a new txout', function() { + var txout = new Txout(); + should.exist(txout); + txout = Txout(); + should.exist(txout); + Txout(valuebn, scriptvi, script).valuebn.toString().should.equal('5'); + }); + describe('#set', function() { it('should set this object', function() { From f3614e4a9064f8f0cd852996f6172813b7c839f1 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 13:14:39 -0700 Subject: [PATCH 260/280] Key -> Keypair ...that is what is called everywhere else. --- lib/keypair.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/keypair.js b/lib/keypair.js index e47e540..1515769 100644 --- a/lib/keypair.js +++ b/lib/keypair.js @@ -3,32 +3,32 @@ var Pubkey = require('./pubkey'); var BN = require('./bn'); var point = require('./point'); -var Key = function Key(obj) { - if (!(this instanceof Key)) - return new Key(obj); +var Keypair = function Keypair(obj) { + if (!(this instanceof Keypair)) + return new Keypair(obj); if (obj) this.set(obj); }; -Key.prototype.set = function(obj) { +Keypair.prototype.set = function(obj) { this.privkey = obj.privkey || this.privkey || undefined; this.pubkey = obj.pubkey || this.pubkey || undefined; return this; }; -Key.prototype.fromPrivkey = function(privkey) { +Keypair.prototype.fromPrivkey = function(privkey) { this.privkey = privkey; this.privkey2pubkey(); return this; }; -Key.prototype.fromRandom = function() { +Keypair.prototype.fromRandom = function() { this.privkey = Privkey().fromRandom(); this.privkey2pubkey(); return this; }; -Key.prototype.fromString = function(str) { +Keypair.prototype.fromString = function(str) { var obj = JSON.parse(str); if (obj.privkey) { this.privkey = new Privkey(); @@ -40,11 +40,11 @@ Key.prototype.fromString = function(str) { } }; -Key.prototype.privkey2pubkey = function() { +Keypair.prototype.privkey2pubkey = function() { this.pubkey = Pubkey().fromPrivkey(this.privkey); }; -Key.prototype.toString = function() { +Keypair.prototype.toString = function() { var obj = {}; if (this.privkey) obj.privkey = this.privkey.toString(); @@ -53,4 +53,4 @@ Key.prototype.toString = function() { return JSON.stringify(obj); }; -module.exports = Key; +module.exports = Keypair; From d6e3266179c65ad9cbc30c29f24bd8b3115f20bd Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 13:55:40 -0700 Subject: [PATCH 261/280] toJSON/fromJSON for privkey, pubkey, keypair --- lib/keypair.js | 17 +++++++++++++++++ lib/privkey.js | 9 +++++++++ lib/pubkey.js | 9 +++++++++ test/keypair.js | 28 ++++++++++++++++++++++++++++ test/privkey.js | 20 ++++++++++++++++++++ test/pubkey.js | 21 +++++++++++++++++++++ 6 files changed, 104 insertions(+) diff --git a/lib/keypair.js b/lib/keypair.js index 1515769..bc3adf8 100644 --- a/lib/keypair.js +++ b/lib/keypair.js @@ -16,6 +16,23 @@ Keypair.prototype.set = function(obj) { return this; }; +Keypair.prototype.fromJSON = function(json) { + if (json.privkey) + this.set({privkey: Privkey().fromJSON(json.privkey)}); + if (json.pubkey) + this.set({pubkey: Pubkey().fromJSON(json.pubkey)}); + return this; +}; + +Keypair.prototype.toJSON = function() { + var json = {}; + if (this.privkey) + json.privkey = this.privkey.toJSON(); + if (this.pubkey) + json.pubkey = this.pubkey.toJSON(); + return json; +}; + Keypair.prototype.fromPrivkey = function(privkey) { this.privkey = privkey; this.privkey2pubkey(); diff --git a/lib/privkey.js b/lib/privkey.js index aa107a1..12f0b7c 100644 --- a/lib/privkey.js +++ b/lib/privkey.js @@ -22,6 +22,15 @@ Privkey.prototype.set = function(obj) { return this; }; +Privkey.prototype.fromJSON = function(json) { + this.fromString(json); + return this; +}; + +Privkey.prototype.toJSON = function() { + return this.toString(); +}; + Privkey.prototype.fromRandom = function() { do { var privbuf = Random.getRandomBuffer(32); diff --git a/lib/pubkey.js b/lib/pubkey.js index 4996247..a2db155 100644 --- a/lib/pubkey.js +++ b/lib/pubkey.js @@ -21,6 +21,15 @@ Pubkey.prototype.set = function(obj) { return this; }; +Pubkey.prototype.fromJSON = function(json) { + this.fromBuffer(new Buffer(json, 'hex')); + return this; +}; + +Pubkey.prototype.toJSON = function() { + return this.toBuffer().toString('hex'); +}; + Pubkey.prototype.fromPrivkey = function(privkey) { this.set({ point: Point.getG().mul(privkey.bn), diff --git a/test/keypair.js b/test/keypair.js index 5e9dd01..f6dbe61 100644 --- a/test/keypair.js +++ b/test/keypair.js @@ -29,6 +29,34 @@ describe('Keypair', function() { }); + describe('#fromJSON', function() { + + it('should make a keypair from this json', function() { + var privkey = Privkey().fromRandom(); + var pubkey = Pubkey().fromPrivkey(privkey); + var keypair = Keypair().fromJSON({ + privkey: privkey.toJSON(), + pubkey: pubkey.toJSON() + }) + keypair.privkey.toString().should.equal(privkey.toString()); + keypair.pubkey.toString().should.equal(pubkey.toString()); + }); + + }); + + describe('#toJSON', function() { + + it('should make json from this keypair', function() { + var json = Keypair().fromRandom().toJSON(); + should.exist(json.privkey); + should.exist(json.pubkey); + var keypair = Keypair().fromJSON(json); + keypair.toJSON().privkey.toString().should.equal(json.privkey.toString()); + keypair.toJSON().pubkey.toString().should.equal(json.pubkey.toString()); + }); + + }); + describe("#fromPrivkey", function() { it('should make a new key from a privkey', function() { diff --git a/test/privkey.js b/test/privkey.js index f9cb48e..745e72b 100644 --- a/test/privkey.js +++ b/test/privkey.js @@ -46,6 +46,26 @@ describe('Privkey', function() { }); + describe('#fromJSON', function() { + + it('should input this address correctly', function() { + var privkey = new Privkey(); + privkey.fromJSON(encmu); + privkey.toWIF().should.equal(encmu); + }); + + }); + + describe('#toString', function() { + + it('should output this address correctly', function() { + var privkey = new Privkey(); + privkey.fromJSON(encmu); + privkey.toJSON().should.equal(encmu); + }); + + }); + describe('#fromRandom', function() { it('should set bn gt 0 and lt n, and should be compressed', function() { diff --git a/test/pubkey.js b/test/pubkey.js index f3e3c1f..bc4f167 100644 --- a/test/pubkey.js +++ b/test/pubkey.js @@ -32,6 +32,27 @@ describe('Pubkey', function() { }); + describe('#fromJSON', function() { + + it('should input this public key', function() { + var pk = new Pubkey(); + pk.fromJSON('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + pk.point.getX().toString(16).should.equal('1ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a'); + pk.point.getY().toString(16).should.equal('7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'); + }); + + }); + + describe('#toJSON', function() { + + it('should output this pubkey', function() { + var pk = new Pubkey(); + var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341'; + pk.fromJSON(hex).toJSON().should.equal(hex); + }); + + }); + describe('#fromPrivkey', function() { it('should make a public key from a privkey', function() { From 5c7149aeab60c5b5103e5521a99ac6be02c8d550 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 14:45:41 -0700 Subject: [PATCH 262/280] Stealthkey toJSON/fromJSON --- lib/expmt/stealthkey.js | 15 +++++++++++++++ test/stealthkey.js | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/expmt/stealthkey.js b/lib/expmt/stealthkey.js index 7cf49bd..7cf4d0f 100644 --- a/lib/expmt/stealthkey.js +++ b/lib/expmt/stealthkey.js @@ -27,6 +27,21 @@ Stealthkey.prototype.set = function(obj) { return this; }; +Stealthkey.prototype.fromJSON = function(json) { + this.set({ + payloadKeypair: Keypair().fromJSON(json.payloadKeypair), + scanKeypair: Keypair().fromJSON(json.scanKeypair) + }); + return this; +}; + +Stealthkey.prototype.toJSON = function() { + return { + payloadKeypair: this.payloadKeypair.toJSON(), + scanKeypair: this.scanKeypair.toJSON() + }; +}; + Stealthkey.prototype.fromRandom = function() { this.payloadKeypair = Keypair().fromRandom(); this.scanKeypair = Keypair().fromRandom(); diff --git a/test/stealthkey.js b/test/stealthkey.js index 7f2d8a7..17eed96 100644 --- a/test/stealthkey.js +++ b/test/stealthkey.js @@ -49,6 +49,30 @@ describe('Stealthkey', function() { }); + describe('#fromJSON', function() { + + it('should make a stealthkey from this JSON', function() { + var sk = Stealthkey().fromJSON({ + payloadKeypair: stealthkey.payloadKeypair.toJSON(), + scanKeypair: stealthkey.scanKeypair.toJSON() + }); + sk.payloadKeypair.toString().should.equal(stealthkey.payloadKeypair.toString()); + sk.scanKeypair.toString().should.equal(stealthkey.scanKeypair.toString()); + }); + + }); + + describe('#toJSON', function() { + + it('should convert this stealthkey to json', function() { + var json = stealthkey.toJSON() + var json2 = Stealthkey().fromJSON(json).toJSON(); + json.payloadKeypair.privkey.should.equal(json2.payloadKeypair.privkey); + json.scanKeypair.privkey.should.equal(json2.scanKeypair.privkey); + }); + + }); + describe('#fromRandom', function() { it('should create a new stealthkey from random', function() { From 6f92775b2cacb3798f578dbc38753a734e89d5ad Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 14:56:07 -0700 Subject: [PATCH 263/280] extra curly braces for code readability --- lib/script.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/script.js b/lib/script.js index 4b2f2aa..46a251d 100644 --- a/lib/script.js +++ b/lib/script.js @@ -181,10 +181,12 @@ Script.prototype.isOpReturn = function() { (this.chunks.length === 2 && this.chunks[1].buf && this.chunks[1].buf.length <= 40 - && this.chunks[1].length === this.chunks.len))) + && this.chunks[1].length === this.chunks.len))) { return true; - else + } else { return false; + } }; + module.exports = Script; From 792e8080c8e092ea160568734a3f645fb316f8bd Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 16:04:06 -0700 Subject: [PATCH 264/280] classify pubkeyhash and scripthash scripts ...both the "in" (ScriptSig) and "out" (ScriptPubkey) --- lib/script.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ test/script.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/lib/script.js b/lib/script.js index 46a251d..4611093 100644 --- a/lib/script.js +++ b/lib/script.js @@ -188,5 +188,50 @@ Script.prototype.isOpReturn = function() { } }; +Script.prototype.isPubkeyhashOut = function() { + if (this.chunks[0] === Opcode('OP_DUP').toNumber() + && this.chunks[1] === Opcode('OP_HASH160').toNumber() + && this.chunks[2].buf + && this.chunks[3] === Opcode('OP_EQUALVERIFY').toNumber() + && this.chunks[4] === Opcode('OP_CHECKSIG').toNumber()) { + return true; + } else { + return false; + } +}; + +Script.prototype.isPubkeyhashIn = function() { + if (this.chunks.length === 2 + && this.chunks[0].buf + && this.chunks[1].buf) { + return true; + } else { + return false; + } +}; + +Script.prototype.isScripthashOut = function() { + if (this.chunks.length === 3 + && this.chunks[0] === Opcode('OP_HASH160').toNumber() + && this.chunks[1].buf + && this.chunks[1].buf.length === 20 + && this.chunks[2] === Opcode('OP_EQUAL').toNumber()) { + return true; + } else { + return false; + } +}; + +//note that these are frequently indistinguishable from pubkeyhashin +Script.prototype.isScripthashIn = function() { + var allpush = this.chunks.every(function(chunk) { + return Buffer.isBuffer(chunk.buf); + }); + if (allpush) { + return true; + } else { + return false; + } +}; module.exports = Script; diff --git a/test/script.js b/test/script.js index 11f98cc..4aed785 100644 --- a/test/script.js +++ b/test/script.js @@ -216,4 +216,53 @@ describe('Script', function() { }); + describe('#isPubkeyhashIn', function() { + + it('should classify this known pubkeyhashin', function() { + Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6').isPubkeyhashIn().should.equal(true); + }); + + it('should classify this known non-pubkeyhashin', function() { + Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6 OP_CHECKSIG').isPubkeyhashIn().should.equal(false); + }); + + }); + + describe('#isPubkeyhashOut', function() { + + it('should classify this known pubkeyhashout as pubkeyhashout', function() { + Script('OP_DUP OP_HASH160 20 0000000000000000000000000000000000000000 OP_EQUALVERIFY OP_CHECKSIG').isPubkeyhashOut().should.equal(true); + }); + + it('should classify this known non-pubkeyhashout as not pubkeyhashout', function() { + Script('OP_DUP OP_HASH160 20 0000000000000000000000000000000000000000').isPubkeyhashOut().should.equal(false) + }); + + }); + + describe('#isScripthashIn', function() { + + it('should classify this known scripthashin', function() { + Script('20 0000000000000000000000000000000000000000').isScripthashIn().should.equal(true); + }); + + it('should classify this known non-scripthashin', function() { + Script('20 0000000000000000000000000000000000000000 OP_CHECKSIG').isScripthashIn().should.equal(false); + }); + + }); + + describe('#isScripthashOut', function() { + + it('should classify this known pubkeyhashout as pubkeyhashout', function() { + Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL').isScripthashOut().should.equal(true); + }); + + it('should classify these known non-pubkeyhashout as not pubkeyhashout', function() { + Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL OP_EQUAL').isScripthashOut().should.equal(false); + Script('OP_HASH160 21 0x000000000000000000000000000000000000000000 OP_EQUAL').isScripthashOut().should.equal(false); + }); + + }); + }); From 9b8ce05b151f691c619c46505653c08acd3341bc Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 17:09:53 -0700 Subject: [PATCH 265/280] BufferWriter().toBuffer convenience method It does the same thing as .concat(), but may be easier to remember, since the rest of the library uses the ".toBuffer()" convention --- lib/bufferwriter.js | 4 ++++ test/bufferwriter.js | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/lib/bufferwriter.js b/lib/bufferwriter.js index fa8e8cd..a61ff90 100644 --- a/lib/bufferwriter.js +++ b/lib/bufferwriter.js @@ -14,6 +14,10 @@ BufferWriter.prototype.set = function(obj) { return this; }; +BufferWriter.prototype.toBuffer = function() { + return this.concat(); +}; + BufferWriter.prototype.concat = function() { return Buffer.concat(this.bufs); }; diff --git a/test/bufferwriter.js b/test/bufferwriter.js index 7042533..fa4b2b9 100644 --- a/test/bufferwriter.js +++ b/test/bufferwriter.js @@ -22,6 +22,17 @@ describe('BufferWriter', function() { }); + describe('#toBuffer', function() { + + it('should concat these two bufs', function() { + var buf1 = new Buffer([0]); + var buf2 = new Buffer([1]); + var bw = new BufferWriter({bufs: [buf1, buf2]}); + bw.toBuffer().toString('hex').should.equal('0001'); + }); + + }); + describe('#concat', function() { it('should concat these two bufs', function() { From 96df77429f8d07030c020ff8fca46f381e8e6561 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 18:23:10 -0700 Subject: [PATCH 266/280] add support for Dark Wallet-style addresses These functions are prefixed DW which stands for Dark Wallet. The code for the Dark Wallet address format can be found here: https://github.com/darkwallet/darkwallet/blob/develop/js/util/stealth.js Note that I deliberately support only the simplest possible format, which is where there is only one payload pubkey and the prefix is blank. I should now go back and replace my old toString, fromString, toBuffer, fromBuffer functions with these Dark Wallet versions, since they are much more well-thought out than mine. --- lib/expmt/stealthaddress.js | 65 +++++++++++++++++++++++++++++++++++++ test/stealthaddress.js | 51 +++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/lib/expmt/stealthaddress.js b/lib/expmt/stealthaddress.js index 66ae9c6..1551488 100644 --- a/lib/expmt/stealthaddress.js +++ b/lib/expmt/stealthaddress.js @@ -2,6 +2,8 @@ var Stealthkey = require('./stealthkey'); var Base58check = require('../base58check'); var Pubkey = require('../pubkey'); var KDF = require('../kdf'); +var BufferWriter = require('../bufferwriter'); +var BufferReader = require('../bufferreader'); var StealthAddress = function StealthAddress(addrstr) { if (!(this instanceof StealthAddress)) @@ -20,6 +22,9 @@ var StealthAddress = function StealthAddress(addrstr) { } }; +StealthAddress.mainver = 42; +StealthAddress.testver = 43; + StealthAddress.prototype.set = function(obj) { this.payloadPubkey = obj.payloadPubkey || this.payloadPubkey; this.scanPubkey = obj.scanPubkey || this.scanPubkey; @@ -56,6 +61,29 @@ StealthAddress.prototype.fromBuffer = function(buf) { return this; }; +StealthAddress.prototype.fromDWBuffer = function(buf) { + var parsed = StealthAddress.parseDWBuffer(buf); + if ((parsed.version !== StealthAddress.mainver) && (parsed.version !== StealthAddress.testver)) + throw new Error('Invalid version'); + if (parsed.options !== 0) + throw new Error('Invalid options'); + if (!parsed.scanPubkey) + throw new Error('Invalid scanPubkey'); + if (parsed.payloadPubkeys.length !== 1) + throw new Error('Must have exactly one payloadPubkey'); + if (parsed.nSigs !== 1) + throw new Error('Must require exactly one signature'); + if (parsed.prefix.toString() !== "") + throw new Error('Only blank prefixes supported'); + this.scanPubkey = parsed.scanPubkey; + this.payloadPubkey = parsed.payloadPubkeys[0]; + return this; +}; + +StealthAddress.prototype.fromDWString = function(str) { + return this.fromDWBuffer(Base58check(str).toBuffer()); +}; + StealthAddress.prototype.fromString = function(str) { var buf = Base58check.decode(str); this.fromBuffer(buf); @@ -86,6 +114,27 @@ StealthAddress.prototype.toBuffer = function() { return Buffer.concat([pBuf, sBuf]); }; +StealthAddress.prototype.toDWBuffer = function(networkstr) { + if (networkstr === 'testnet') + var version = StealthAddress.testver; + else + var version = StealthAddress.mainver; + var bw = new BufferWriter(); + bw.writeUInt8(version); + bw.writeUInt8(0); //options + bw.write(this.scanPubkey.toDER(true)); + bw.writeUInt8(1); //number of payload keys - we only support 1 (not multisig) + bw.write(this.payloadPubkey.toDER(true)); + bw.writeUInt8(1); //number of signatures - we only support 1 (not multisig) + bw.writeUInt8(0); //prefix length - we do not support prefix yet + var buf = bw.concat(); + return buf; +}; + +StealthAddress.prototype.toDWString = function(networkstr) { + return Base58check(this.toDWBuffer(networkstr)).toString(); +}; + StealthAddress.prototype.toString = function() { var buf = this.toBuffer(); var b58 = Base58check.encode(buf); @@ -93,4 +142,20 @@ StealthAddress.prototype.toString = function() { return b58; }; +StealthAddress.parseDWBuffer = function(buf) { + var br = new BufferReader(buf); + var parsed = {}; + parsed.version = br.readUInt8(); + parsed.options = br.readUInt8(); + parsed.scanPubkey = Pubkey().fromBuffer(br.read(33)); + parsed.nPayloadPubkeys = br.readUInt8(); + parsed.payloadPubkeys = []; + for (var i = 0; i < parsed.nPayloadPubkeys; i++) + parsed.payloadPubkeys.push(Pubkey().fromBuffer(br.read(33))); + parsed.nSigs = br.readUInt8(); + parsed.nPrefix = br.readUInt8(); + parsed.prefix = br.read(parsed.nPrefix / 8); + return parsed; +}; + module.exports = StealthAddress; diff --git a/test/stealthaddress.js b/test/stealthaddress.js index 8ab30a9..8d32289 100644 --- a/test/stealthaddress.js +++ b/test/stealthaddress.js @@ -26,6 +26,7 @@ describe('StealthAddress', function() { senderKeypair.privkey2pubkey(); var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW'; + var dwhex = '2a0002697763d7e9becb0c180083738c32c05b0e2fee26d6278020c06bbb04d5f66b32010362408459041e0473298af3824dbabe4d2b7f846825ed4d1c2e2c670c07cb275d0100'; it('should make a new stealth address', function() { var sa = new StealthAddress(); @@ -69,6 +70,22 @@ describe('StealthAddress', function() { }); + describe('#fromDWBuffer', function() { + + it('should parse this DW buffer', function() { + StealthAddress().fromDWBuffer(new Buffer(dwhex, 'hex')).toDWBuffer().toString('hex').should.equal(dwhex); + }); + + }); + + describe('#fromDWString', function() { + + it('should parse this DW buffer', function() { + StealthAddress().fromDWString(Base58check(new Buffer(dwhex, 'hex')).toString()).toDWBuffer().toString('hex').should.equal(dwhex); + }); + + }); + describe('#fromString', function() { it('should give a stealthkey address with the right pubkeys', function() { @@ -126,6 +143,24 @@ describe('StealthAddress', function() { }); + describe('#toDWBuffer', function() { + + it('should return this known address buffer', function() { + var buf = Base58check.decode(addressString); + StealthAddress().fromBuffer(buf).toDWBuffer().toString('hex').should.equal(dwhex); + }); + + }); + + describe('#toDWString', function() { + + it('should return this known address buffer', function() { + var buf = Base58check.decode(addressString); + StealthAddress().fromBuffer(buf).toDWString().should.equal(Base58check(new Buffer(dwhex, 'hex')).toString()); + }); + + }); + describe('#toString', function() { it('should return this known address string', function() { @@ -134,4 +169,20 @@ describe('StealthAddress', function() { }); + describe('@parseDWBuffer', function() { + + it('should parse this known DW buffer', function() { + var buf = new Buffer(dwhex, 'hex'); + var parsed = StealthAddress.parseDWBuffer(buf); + parsed.version.should.equal(42); + parsed.options.should.equal(0); + parsed.scanPubkey.toString().should.equal('02697763d7e9becb0c180083738c32c05b0e2fee26d6278020c06bbb04d5f66b32'); + parsed.nPayloadPubkeys.should.equal(1); + parsed.payloadPubkeys[0].toString().should.equal('0362408459041e0473298af3824dbabe4d2b7f846825ed4d1c2e2c670c07cb275d'); + parsed.nSigs.should.equal(1); + parsed.prefix.toString().should.equal(''); + }); + + }); + }); From 22b87325d7f5b50a3ce2522b168b1d8114814508 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 21:27:31 -0700 Subject: [PATCH 267/280] make Dark Wallet buffer/string formats the default and rename the old functions to "bitcore buffer" and "bitcore string" --- lib/expmt/stealthaddress.js | 32 +++++++++--------- test/stealthaddress.js | 66 ++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/lib/expmt/stealthaddress.js b/lib/expmt/stealthaddress.js index 1551488..ced8957 100644 --- a/lib/expmt/stealthaddress.js +++ b/lib/expmt/stealthaddress.js @@ -10,11 +10,11 @@ var StealthAddress = function StealthAddress(addrstr) { return new StealthAddress(addrstr); if (typeof addrstr === 'string') { - this.fromString(addrstr) + this.fromBitcoreString(addrstr) } else if (Buffer.isBuffer(addrstr)) { var buf = addrstr; - this.fromBuffer(buf); + this.fromBitcoreBuffer(buf); } else if (addrstr) { var obj = addrstr; @@ -32,12 +32,12 @@ StealthAddress.prototype.set = function(obj) { }; StealthAddress.prototype.fromJSON = function(json) { - this.fromString(json); + this.fromBitcoreString(json); return this; }; StealthAddress.prototype.toJSON = function() { - return this.toString(); + return this.toBitcoreString(); }; StealthAddress.prototype.fromStealthkey = function(stealthkey) { @@ -48,7 +48,7 @@ StealthAddress.prototype.fromStealthkey = function(stealthkey) { return this; }; -StealthAddress.prototype.fromBuffer = function(buf) { +StealthAddress.prototype.fromBitcoreBuffer = function(buf) { if (!Buffer.isBuffer(buf) || buf.length !== 66) throw new Error('stealthkey: A stealth address must have length 66'); @@ -61,7 +61,7 @@ StealthAddress.prototype.fromBuffer = function(buf) { return this; }; -StealthAddress.prototype.fromDWBuffer = function(buf) { +StealthAddress.prototype.fromBuffer = function(buf) { var parsed = StealthAddress.parseDWBuffer(buf); if ((parsed.version !== StealthAddress.mainver) && (parsed.version !== StealthAddress.testver)) throw new Error('Invalid version'); @@ -80,13 +80,13 @@ StealthAddress.prototype.fromDWBuffer = function(buf) { return this; }; -StealthAddress.prototype.fromDWString = function(str) { - return this.fromDWBuffer(Base58check(str).toBuffer()); +StealthAddress.prototype.fromString = function(str) { + return this.fromBuffer(Base58check(str).toBuffer()); }; -StealthAddress.prototype.fromString = function(str) { +StealthAddress.prototype.fromBitcoreString = function(str) { var buf = Base58check.decode(str); - this.fromBuffer(buf); + this.fromBitcoreBuffer(buf); return this; }; @@ -107,14 +107,14 @@ StealthAddress.prototype.getReceivePubkey = function(senderKeypair) { return pubkey; }; -StealthAddress.prototype.toBuffer = function() { +StealthAddress.prototype.toBitcoreBuffer = function() { var pBuf = this.payloadPubkey.toDER(true); var sBuf = this.scanPubkey.toDER(true); return Buffer.concat([pBuf, sBuf]); }; -StealthAddress.prototype.toDWBuffer = function(networkstr) { +StealthAddress.prototype.toBuffer = function(networkstr) { if (networkstr === 'testnet') var version = StealthAddress.testver; else @@ -131,12 +131,12 @@ StealthAddress.prototype.toDWBuffer = function(networkstr) { return buf; }; -StealthAddress.prototype.toDWString = function(networkstr) { - return Base58check(this.toDWBuffer(networkstr)).toString(); +StealthAddress.prototype.toString = function(networkstr) { + return Base58check(this.toBuffer(networkstr)).toString(); }; -StealthAddress.prototype.toString = function() { - var buf = this.toBuffer(); +StealthAddress.prototype.toBitcoreString = function() { + var buf = this.toBitcoreBuffer(); var b58 = Base58check.encode(buf); return b58; diff --git a/test/stealthaddress.js b/test/stealthaddress.js index 8d32289..a4a59cf 100644 --- a/test/stealthaddress.js +++ b/test/stealthaddress.js @@ -58,39 +58,39 @@ describe('StealthAddress', function() { }); - describe('#fromBuffer', function() { + describe('#fromBitcoreBuffer', function() { it('should give a stealthkey address with the right pubkeys', function() { var sa = new StealthAddress(); var buf = Base58check.decode(addressString); - sa.fromBuffer(buf); + sa.fromBitcoreBuffer(buf); sa.payloadPubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); sa.scanPubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); }); }); - describe('#fromDWBuffer', function() { + describe('#fromBuffer', function() { it('should parse this DW buffer', function() { - StealthAddress().fromDWBuffer(new Buffer(dwhex, 'hex')).toDWBuffer().toString('hex').should.equal(dwhex); - }); - - }); - - describe('#fromDWString', function() { - - it('should parse this DW buffer', function() { - StealthAddress().fromDWString(Base58check(new Buffer(dwhex, 'hex')).toString()).toDWBuffer().toString('hex').should.equal(dwhex); + StealthAddress().fromBuffer(new Buffer(dwhex, 'hex')).toBuffer().toString('hex').should.equal(dwhex); }); }); describe('#fromString', function() { + it('should parse this DW buffer', function() { + StealthAddress().fromString(Base58check(new Buffer(dwhex, 'hex')).toString()).toBuffer().toString('hex').should.equal(dwhex); + }); + + }); + + describe('#fromBitcoreString', function() { + it('should give a stealthkey address with the right pubkeys', function() { var sa = new StealthAddress(); - sa.fromString(addressString); + sa.fromBitcoreString(addressString); sa.payloadPubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); sa.scanPubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); }); @@ -134,37 +134,37 @@ describe('StealthAddress', function() { }); + describe('#toBitcoreBuffer', function() { + + it('should return this known address buffer', function() { + var buf = Base58check.decode(addressString); + StealthAddress().fromBitcoreBuffer(buf).toBitcoreBuffer().toString('hex').should.equal(buf.toString('hex')); + }); + + }); + describe('#toBuffer', function() { it('should return this known address buffer', function() { var buf = Base58check.decode(addressString); - StealthAddress().fromBuffer(buf).toBuffer().toString('hex').should.equal(buf.toString('hex')); - }); - - }); - - describe('#toDWBuffer', function() { - - it('should return this known address buffer', function() { - var buf = Base58check.decode(addressString); - StealthAddress().fromBuffer(buf).toDWBuffer().toString('hex').should.equal(dwhex); - }); - - }); - - describe('#toDWString', function() { - - it('should return this known address buffer', function() { - var buf = Base58check.decode(addressString); - StealthAddress().fromBuffer(buf).toDWString().should.equal(Base58check(new Buffer(dwhex, 'hex')).toString()); + StealthAddress().fromBitcoreBuffer(buf).toBuffer().toString('hex').should.equal(dwhex); }); }); describe('#toString', function() { + it('should return this known address buffer', function() { + var buf = Base58check.decode(addressString); + StealthAddress().fromBitcoreBuffer(buf).toString().should.equal(Base58check(new Buffer(dwhex, 'hex')).toString()); + }); + + }); + + describe('#toBitcoreString', function() { + it('should return this known address string', function() { - StealthAddress().fromString(addressString).toString().should.equal(addressString); + StealthAddress().fromBitcoreString(addressString).toBitcoreString().should.equal(addressString); }); }); From fd499089bb84d1b3b2b13ff6b5231dc0d93ceda4 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 21:30:43 -0700 Subject: [PATCH 268/280] update readme to reflect toJSON/fromJSON ...rather than toObject/fromObject, which I had considered doing before, but decided toJSON/fromJSON was more explicit. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1eb6887..a32d8bf 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,9 @@ type of object always and everywhere. 2) Have 100% test coverage so that the library is known to be reliable. 3) Library objects have an interface suitable for use with a command-line -interface and API, in particular having toString, fromString, toObject, -fromObject methods. +interface and API, in particular having toString, fromString, toJSON, fromJSON, +methods. Other common methods are toBuffer, fromBuffer relevant for binary +formats such as transactions and blocks. 4) All standard features of the bitcoin protocol are implemented and saved in lib/. All BIPs are correctly implemented and saved as BIPxx.js in lib/ (since From 4fabad21a11a777cfbd3224abad3b0e755363a9a Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 22 Sep 2014 21:51:19 -0700 Subject: [PATCH 269/280] get rid of obsolete 'bitcore' stealth addr format --- lib/expmt/stealthaddress.js | 42 ++++----------------------------- test/stealthaddress.js | 47 ++++--------------------------------- 2 files changed, 8 insertions(+), 81 deletions(-) diff --git a/lib/expmt/stealthaddress.js b/lib/expmt/stealthaddress.js index ced8957..6635167 100644 --- a/lib/expmt/stealthaddress.js +++ b/lib/expmt/stealthaddress.js @@ -10,11 +10,11 @@ var StealthAddress = function StealthAddress(addrstr) { return new StealthAddress(addrstr); if (typeof addrstr === 'string') { - this.fromBitcoreString(addrstr) + this.fromString(addrstr) } else if (Buffer.isBuffer(addrstr)) { var buf = addrstr; - this.fromBitcoreBuffer(buf); + this.fromBuffer(buf); } else if (addrstr) { var obj = addrstr; @@ -32,12 +32,12 @@ StealthAddress.prototype.set = function(obj) { }; StealthAddress.prototype.fromJSON = function(json) { - this.fromBitcoreString(json); + this.fromString(json); return this; }; StealthAddress.prototype.toJSON = function() { - return this.toBitcoreString(); + return this.toString(); }; StealthAddress.prototype.fromStealthkey = function(stealthkey) { @@ -48,19 +48,6 @@ StealthAddress.prototype.fromStealthkey = function(stealthkey) { return this; }; -StealthAddress.prototype.fromBitcoreBuffer = function(buf) { - if (!Buffer.isBuffer(buf) || buf.length !== 66) - throw new Error('stealthkey: A stealth address must have length 66'); - - var pPubBuf = buf.slice(0, 33); - var sPubBuf = buf.slice(33, 66); - - this.payloadPubkey = Pubkey().fromDER(pPubBuf); - this.scanPubkey = Pubkey().fromDER(sPubBuf); - - return this; -}; - StealthAddress.prototype.fromBuffer = function(buf) { var parsed = StealthAddress.parseDWBuffer(buf); if ((parsed.version !== StealthAddress.mainver) && (parsed.version !== StealthAddress.testver)) @@ -84,13 +71,6 @@ StealthAddress.prototype.fromString = function(str) { return this.fromBuffer(Base58check(str).toBuffer()); }; -StealthAddress.prototype.fromBitcoreString = function(str) { - var buf = Base58check.decode(str); - this.fromBitcoreBuffer(buf); - - return this; -}; - StealthAddress.prototype.getSharedKeypair = function(senderKeypair) { var sharedSecretPoint = this.scanPubkey.point.mul(senderKeypair.privkey.bn); var sharedSecretPubkey = Pubkey(sharedSecretPoint); @@ -107,13 +87,6 @@ StealthAddress.prototype.getReceivePubkey = function(senderKeypair) { return pubkey; }; -StealthAddress.prototype.toBitcoreBuffer = function() { - var pBuf = this.payloadPubkey.toDER(true); - var sBuf = this.scanPubkey.toDER(true); - - return Buffer.concat([pBuf, sBuf]); -}; - StealthAddress.prototype.toBuffer = function(networkstr) { if (networkstr === 'testnet') var version = StealthAddress.testver; @@ -135,13 +108,6 @@ StealthAddress.prototype.toString = function(networkstr) { return Base58check(this.toBuffer(networkstr)).toString(); }; -StealthAddress.prototype.toBitcoreString = function() { - var buf = this.toBitcoreBuffer(); - var b58 = Base58check.encode(buf); - - return b58; -}; - StealthAddress.parseDWBuffer = function(buf) { var br = new BufferReader(buf); var parsed = {}; diff --git a/test/stealthaddress.js b/test/stealthaddress.js index a4a59cf..3b8e3e8 100644 --- a/test/stealthaddress.js +++ b/test/stealthaddress.js @@ -25,8 +25,9 @@ describe('StealthAddress', function() { senderKeypair.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test 3'))); senderKeypair.privkey2pubkey(); - var addressString = '9dDbC9FzZ74r8njQkXD6W27gtrxLiWaeFPHxeo1fynQRXPicqxVt7u95ozbwoVVMXyrzaHKN9owsteg63FgwDfrxWx82SAW'; + var addressString = 'vJmtuUb8ysKiM1HtHQF23FGfjGAKu5sM94UyyjknqhJHNdj5CZzwtpGzeyaATQ2HvuzomNVtiwsTJSWzzCBgCTtUZbRFpzKVq9MAUr'; var dwhex = '2a0002697763d7e9becb0c180083738c32c05b0e2fee26d6278020c06bbb04d5f66b32010362408459041e0473298af3824dbabe4d2b7f846825ed4d1c2e2c670c07cb275d0100'; + var dwbuf = new Buffer(dwhex, 'hex'); it('should make a new stealth address', function() { var sa = new StealthAddress(); @@ -58,18 +59,6 @@ describe('StealthAddress', function() { }); - describe('#fromBitcoreBuffer', function() { - - it('should give a stealthkey address with the right pubkeys', function() { - var sa = new StealthAddress(); - var buf = Base58check.decode(addressString); - sa.fromBitcoreBuffer(buf); - sa.payloadPubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); - sa.scanPubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); - }); - - }); - describe('#fromBuffer', function() { it('should parse this DW buffer', function() { @@ -86,17 +75,6 @@ describe('StealthAddress', function() { }); - describe('#fromBitcoreString', function() { - - it('should give a stealthkey address with the right pubkeys', function() { - var sa = new StealthAddress(); - sa.fromBitcoreString(addressString); - sa.payloadPubkey.toString().should.equal(stealthkey.payloadKeypair.pubkey.toString()); - sa.scanPubkey.toString().should.equal(stealthkey.scanKeypair.pubkey.toString()); - }); - - }); - describe('#getSharedKeypair', function() { it('should return a key', function() { @@ -134,20 +112,11 @@ describe('StealthAddress', function() { }); - describe('#toBitcoreBuffer', function() { - - it('should return this known address buffer', function() { - var buf = Base58check.decode(addressString); - StealthAddress().fromBitcoreBuffer(buf).toBitcoreBuffer().toString('hex').should.equal(buf.toString('hex')); - }); - - }); - describe('#toBuffer', function() { it('should return this known address buffer', function() { var buf = Base58check.decode(addressString); - StealthAddress().fromBitcoreBuffer(buf).toBuffer().toString('hex').should.equal(dwhex); + StealthAddress().fromBuffer(dwbuf).toBuffer().toString('hex').should.equal(dwhex); }); }); @@ -156,15 +125,7 @@ describe('StealthAddress', function() { it('should return this known address buffer', function() { var buf = Base58check.decode(addressString); - StealthAddress().fromBitcoreBuffer(buf).toString().should.equal(Base58check(new Buffer(dwhex, 'hex')).toString()); - }); - - }); - - describe('#toBitcoreString', function() { - - it('should return this known address string', function() { - StealthAddress().fromBitcoreString(addressString).toBitcoreString().should.equal(addressString); + StealthAddress().fromBuffer(buf).toString().should.equal(Base58check(new Buffer(dwhex, 'hex')).toString()); }); }); From 4f7153586989d3a347d381db5cfad6ff36a896ea Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 23 Sep 2014 16:54:52 -0700 Subject: [PATCH 270/280] StealthTx For spotting transactions to which you have the stealth key (or at least the scan key) and creating transactions to a stealth address. So far it is only partially working - you can see if a transaction is a stealth transaction (or at least one of a limited kind of stealth transactions), and you can see that you do not have the stealth key to spend one of these transactions. However, I have not yet tested whether you can see a stealth transaction that you actually have the key to. Also, it is not yet easy to spend to a stealth address. --- lib/expmt/stealthtx.js | 69 ++++++++++++++++++++++++++++++++++++++++++ test/stealthtx.js | 68 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 lib/expmt/stealthtx.js create mode 100644 test/stealthtx.js diff --git a/lib/expmt/stealthtx.js b/lib/expmt/stealthtx.js new file mode 100644 index 0000000..0133166 --- /dev/null +++ b/lib/expmt/stealthtx.js @@ -0,0 +1,69 @@ +var StealthAddress = require('./stealthaddress'); +var StealthKey = require('./stealthkey'); +var Transaction = require('../transaction'); +var Pubkey = require('../pubkey'); + +var StealthTx = function StealthTx(tx, sa, sk) { + if (!(this instanceof StealthTx)) + return new StealthTx(tx, sa, sk); + if (tx instanceof Transaction) { + this.tx = tx; + this.sa = sa; + this.sk = sk; + } else if (tx) { + var obj = tx; + this.set(obj); + } +}; + +StealthTx.prototype.set = function(obj) { + this.sk = obj.sk || this.sk; + this.sa = obj.sa || this.sa; + this.tx = obj.tx || this.tx; + return this; +}; + +StealthTx.prototype.isForMe = function() { + if (!this.notMine()) + return true; + else + return false; +}; + +StealthTx.prototype.notMine = function() { + var err; + if (err = this.notStealth()) + return "Not stealth: " + err; + var txopbuf = this.tx.txouts[0].script.chunks[1].buf; + var parsed = StealthTx.parseOpReturnData(txopbuf); + var pubkey = parsed.pubkey; + var pubkeyhashbuf = this.tx.txouts[1].script.chunks[2].buf; + var sk = this.sk; + if (sk.isForMe(pubkey, pubkeyhashbuf)) { + return false; + } else { + return "StealthTx not mine"; + } +}; + +//For now, we only support a very limited variety of stealth tx +StealthTx.prototype.notStealth = function() { + var txouts = this.tx.txouts; + if (!(txouts.length >= 2)) + return "Not enough txouts"; + if (!txouts[0].script.isOpReturn()) + return "First txout is not OP_RETURN"; + if (!txouts[1].script.isPubkeyhashOut()) + return "Second txout is not pubkeyhash"; + return false; +}; + +StealthTx.parseOpReturnData = function(buf) { + var parsed = {}; + parsed.version = buf[0]; + parsed.noncebuf = buf.slice(1, 5); + parsed.pubkey = Pubkey().fromBuffer(buf.slice(5, 5 + 33)); + return parsed; +}; + +module.exports = StealthTx; diff --git a/test/stealthtx.js b/test/stealthtx.js new file mode 100644 index 0000000..f00d73e --- /dev/null +++ b/test/stealthtx.js @@ -0,0 +1,68 @@ +var should = require('chai').should(); +var Txout = require('../lib/txout'); +var Stealthkey = require('../lib/expmt/stealthkey'); +var StealthTx = require('../lib/expmt/stealthtx'); +var Transaction = require('../lib/transaction'); +var Varint = require('../lib/varint'); + +describe('StealthTx', function() { + + var txhex = '0100000001c828ccce36eca04f96321ad488528af86c7598e67157c4f8e2f462a9e0e3af5f010000006a47304402204525eef6a56cc57fb184e53efdfdc1086d5265da21480d55c2184536440a64f70220349cdc6c66a8507dde0d172fe64aeb57ae56e014b50315f615086a6b85c5424e012102c0633ddb6bf2a8686e2ba4ce8026c94e1e27ef12e73f8fed6d6d2b97199f9b74ffffffff020000000000000000286a2606deadbeef0365b5a5b0ba059666e907b0b5e07b37fdb162d1399ed829315491fe1f30c87b3f905f0100000000001976a9142042d5e7ef9e82346419fbfe7df5ae52fe4bea3c88ac00000000'; + var txbuf = new Buffer(txhex, 'hex'); + var txidhex = '66da969fff214c329e27062beaf3baf20ed035801559b31f3e868c2de4cdfc5b'; + var tx = Transaction(txbuf); + + it('should make a new StealthTx', function() { + var stx = new StealthTx(); + should.exist(stx); + stx = StealthTx(); + should.exist(stx); + }); + + describe('#isForMe', function() { + + it('should return false for this known tx and random stealthkey', function() { + var sk = Stealthkey().fromRandom(); + var stx = StealthTx().set({sk: sk, tx: tx}); + stx.isForMe().should.equal(false); + }); + + }); + + describe('#notMine', function() { + + it('should return true for this known tx and random stealthkey', function() { + var sk = Stealthkey().fromRandom(); + var stx = StealthTx().set({sk: sk, tx: tx}); + stx.notMine().should.equal("StealthTx not mine"); + }); + + }); + + describe('#notStealth', function() { + + it('should know this is a stealth tx', function() { + var stx = StealthTx().set({tx: tx}); + stx.notStealth().should.equal(false); + }); + + it('should know this is not a stealth tx', function() { + var tx2 = Transaction(tx); + tx2.txouts.pop(); + tx2.txoutsvi = Varint(1); + var stx = StealthTx().set({tx: tx2}); + stx.notStealth().should.equal("Not enough txouts"); + }); + + }); + + describe('@parseOpReturnData', function() { + var txout = tx.txouts[0]; + var buf = txout.script.chunks[1].buf; + var parsed = StealthTx.parseOpReturnData(buf); + (typeof parsed.version).should.equal('number'); + parsed.noncebuf.length.should.be.above(0); + parsed.pubkey.toBuffer().length.should.equal(33); + }); + +}); From d689dbb77e2a3c7043e14c272b8a3eb8535454d1 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 23 Sep 2014 20:28:16 -0700 Subject: [PATCH 271/280] speed up random test slightly ...by caching hexes. Also, correct the "1000" typo - it's actually 100. --- test/random.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/random.js b/test/random.js index 4b6d026..be715ba 100644 --- a/test/random.js +++ b/test/random.js @@ -17,13 +17,13 @@ describe('Random', function() { bytes1.toString('hex').should.not.equal(bytes2.toString('hex')); }); - it('should generate 1000 8 byte buffers in a row that are not equal', function() { - var bufs = []; + it('should generate 100 8 byte buffers in a row that are not equal', function() { + var hexs = []; for (var i = 0; i < 100; i++) - bufs[i] = Random.getRandomBuffer(8); + hexs[i] = Random.getRandomBuffer(8).toString('hex'); for (var i = 0; i < 100; i++) for (var j = i + 1; j < 100; j++) - bufs[i].toString('hex').should.not.equal(bufs[j].toString('hex')); + hexs[i].should.not.equal(hexs[j]); }); }); From 54818c0bd88c4bb78481af20cb530231f473270a Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 23 Sep 2014 21:08:00 -0700 Subject: [PATCH 272/280] expose stealthtx --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 5f5a29d..7df3dda 100644 --- a/index.js +++ b/index.js @@ -37,6 +37,7 @@ bitcore.expmt.ECIES = require('./lib/expmt/ecies'); bitcore.expmt.StealthAddress = require('./lib/expmt/stealthaddress'); bitcore.expmt.Stealthkey = require('./lib/expmt/stealthkey'); bitcore.expmt.StealthMessage = require('./lib/expmt/stealthmessage'); +bitcore.expmt.StealthTx = require('./lib/expmt/stealthtx'); //dependencies, subject to change bitcore.deps = {}; From b37e39abca9feab06989d13b35f3eb1d72268392 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 23 Sep 2014 21:28:03 -0700 Subject: [PATCH 273/280] Script().writeXX convenience methods Script().writeOp('OP_CHECKMULTISIG'), or... Script().writeOp(174), or... Script().writeBuffer([push data buffer]), or... Script().write([op string, number, or push data buffer]) These convenience methods let you easily write a script. --- lib/script.js | 42 ++++++++++++++++++++++++++++++++++++++++++ test/script.js | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/lib/script.js b/lib/script.js index 4611093..63376b2 100644 --- a/lib/script.js +++ b/lib/script.js @@ -234,4 +234,46 @@ Script.prototype.isScripthashIn = function() { } }; +Script.prototype.write = function(obj) { + if (typeof obj === 'string') + this.writeOp(obj); + else if (typeof obj === 'number') + this.writeOp(obj); + else if (Buffer.isBuffer(obj)) + this.writeBuffer(obj); + else if (typeof obj === 'object') + this.chunks.push(obj); + else + throw new Error('Invalid script chunk'); + return this; +}; + +Script.prototype.writeOp = function(str) { + if (typeof str === 'number') + this.chunks.push(str); + else + this.chunks.push(Opcode(str).toNumber()); + return this; +}; + +Script.prototype.writeBuffer = function(buf) { + var opcodenum; + var len = buf.length; + if (buf.length > 0 && buf.length < Opcode.map.OP_PUSHDATA1) { + opcodenum = buf.length; + } else if (buf.length < Math.pow(2, 8)) { + opcodenum = Opcode.map.OP_PUSHDATA1; + } else if (buf.length < Math.pow(2, 16)) { + opcodenum = Opcode.map.OP_PUSHDATA2; + } else if (buf.length < Math.pow(2, 32)) { + opcodenum = Opcode.map.OP_PUSHDATA4; + } + this.chunks.push({ + buf: buf, + len: len, + opcodenum: opcodenum + }); + return this; +}; + module.exports = Script; diff --git a/test/script.js b/test/script.js index 4aed785..83f1b14 100644 --- a/test/script.js +++ b/test/script.js @@ -265,4 +265,44 @@ describe('Script', function() { }); + describe('#writeOp', function() { + + it('should write these ops', function() { + Script().writeOp('OP_CHECKMULTISIG').toString().should.equal('OP_CHECKMULTISIG'); + Script().writeOp(Opcode.map.OP_CHECKMULTISIG).toString().should.equal('OP_CHECKMULTISIG'); + }); + + }); + + describe('#writeBuffer', function() { + + it('should write these push data', function() { + var buf = new Buffer(1); + buf.fill(0); + Script().writeBuffer(buf).toString().should.equal('1 0x00'); + buf = new Buffer(255); + buf.fill(0); + Script().writeBuffer(buf).toString().should.equal('OP_PUSHDATA1 255 0x' + buf.toString('hex')); + buf = new Buffer(256); + buf.fill(0); + Script().writeBuffer(buf).toString().should.equal('OP_PUSHDATA2 256 0x' + buf.toString('hex')); + buf = new Buffer(Math.pow(2, 16)); + buf.fill(0); + Script().writeBuffer(buf).toString().should.equal('OP_PUSHDATA4 ' + Math.pow(2, 16) + ' 0x' + buf.toString('hex')); + }); + + }); + + describe('#write', function() { + + it('should write both pushdata and non-pushdata chunks', function() { + Script().write('OP_CHECKMULTISIG').toString().should.equal('OP_CHECKMULTISIG'); + Script().write(Opcode.map.OP_CHECKMULTISIG).toString().should.equal('OP_CHECKMULTISIG'); + var buf = new Buffer(1); + buf.fill(0); + Script().write(buf).toString().should.equal('1 0x00'); + }); + + }); + }); From 729049a7dae88daa8565f4991a9471a7a2ea677d Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 23 Sep 2014 21:37:18 -0700 Subject: [PATCH 274/280] Add error for when pushdata is exceptionally large --- lib/script.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/script.js b/lib/script.js index 63376b2..149946c 100644 --- a/lib/script.js +++ b/lib/script.js @@ -267,6 +267,8 @@ Script.prototype.writeBuffer = function(buf) { opcodenum = Opcode.map.OP_PUSHDATA2; } else if (buf.length < Math.pow(2, 32)) { opcodenum = Opcode.map.OP_PUSHDATA4; + } else { + throw new Error("You can't push that much data"); } this.chunks.push({ buf: buf, From c07d5096232be393f62ea4b0e170116ee60049a4 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 23 Sep 2014 22:01:30 -0700 Subject: [PATCH 275/280] initialize transaction In order to add convenience methods to a transaction, such as pushing new inputs and outputs, we need to first have the notion of an initialized transaction, which is actually not blank. An initialized transaction just has default values for everything, such as no inputs and no outputs, and default version and nlocktime. --- lib/transaction.js | 15 +++++++++++++++ test/transaction.js | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/transaction.js b/lib/transaction.js index aad3f45..4deb23b 100644 --- a/lib/transaction.js +++ b/lib/transaction.js @@ -9,6 +9,7 @@ var Transaction = function Transaction(version, txinsvi, txins, txoutsvi, txouts if (!(this instanceof Transaction)) return new Transaction(version, txinsvi, txins, txoutsvi, txouts, nlocktime); if (typeof version === 'number') { + this.initialize(); this.set({ version: version, txinsvi: txinsvi, @@ -18,14 +19,28 @@ var Transaction = function Transaction(version, txinsvi, txins, txoutsvi, txouts nlocktime: nlocktime }); } else if (Buffer.isBuffer(version)) { + //not necessary to initialize, since everything should be overwritten var txbuf = version; this.fromBuffer(txbuf); } else if (version) { + this.initialize(); var obj = version; this.set(obj); + } else { + this.initialize(); } }; +Transaction.prototype.initialize = function() { + this.version = 1; + this.txinsvi = Varint(0); + this.txins = []; + this.txoutsvi = Varint(0); + this.txouts = []; + this.nlocktime = 0xffffffff; + return this; +}; + Transaction.prototype.set = function(obj) { this.version = typeof obj.version !== 'undefined' ? obj.version : this.version; this.txinsvi = obj.txinsvi || this.txinsvi; diff --git a/test/transaction.js b/test/transaction.js index 180f10a..9083b56 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -30,7 +30,31 @@ describe('Transaction', function() { should.exist(tx); tx = Transaction(); should.exist(tx); + Transaction(txbuf).toBuffer().toString('hex').should.equal(txhex); + + //should set known defaults + tx.version.should.equal(1); + tx.txinsvi.toNumber().should.equal(0); + tx.txins.length.should.equal(0); + tx.txoutsvi.toNumber().should.equal(0); + tx.txouts.length.should.equal(0); + tx.nlocktime.should.equal(0xffffffff); + }); + + describe('#initialize', function() { + + it('should set these known defaults', function() { + var tx = new Transaction(); + tx.initialize(); + tx.version.should.equal(1); + tx.txinsvi.toNumber().should.equal(0); + tx.txins.length.should.equal(0); + tx.txoutsvi.toNumber().should.equal(0); + tx.txouts.length.should.equal(0); + tx.nlocktime.should.equal(0xffffffff); + }); + }); describe('#set', function() { From 8e85eba08b0c421804352fdbe15838aaa8c0bf22 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 23 Sep 2014 22:09:41 -0700 Subject: [PATCH 276/280] pushin, pushout Add convenience methods for adding new inputs and outputs. --- lib/transaction.js | 12 ++++++++++++ test/transaction.js | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/transaction.js b/lib/transaction.js index 4deb23b..ffa3ffa 100644 --- a/lib/transaction.js +++ b/lib/transaction.js @@ -140,4 +140,16 @@ Transaction.prototype.id = function() { return BufferReader(this.hash()).reverse().read(); }; +Transaction.prototype.pushin = function(txin) { + this.txins.push(txin); + this.txinsvi = Varint(this.txinsvi.toNumber() + 1); + return this; +}; + +Transaction.prototype.pushout = function(txout) { + this.txouts.push(txout); + this.txoutsvi = Varint(this.txoutsvi.toNumber() + 1); + return this; +}; + module.exports = Transaction; diff --git a/test/transaction.js b/test/transaction.js index 9083b56..c5e6a9d 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -168,4 +168,28 @@ describe('Transaction', function() { }); + describe('#pushin', function() { + + it('should add an input', function() { + var txin = Txin(); + var tx = Transaction(); + tx.pushin(txin); + tx.txinsvi.toNumber().should.equal(1); + tx.txins.length.should.equal(1); + }); + + }); + + describe('#pushout', function() { + + it('should add an output', function() { + var txout = Txout(); + var tx = Transaction(); + tx.pushout(txout); + tx.txoutsvi.toNumber().should.equal(1); + tx.txouts.length.should.equal(1); + }); + + }); + }); From f54edfb6188a7870a06e27b5c8b7961d2a657f18 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 23 Sep 2014 22:19:01 -0700 Subject: [PATCH 277/280] Varint(BN()) convenience ...and add some tests for the various constructor conveniences --- lib/varint.js | 3 +++ test/varint.js | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/lib/varint.js b/lib/varint.js index e959f2f..5fc367a 100644 --- a/lib/varint.js +++ b/lib/varint.js @@ -10,6 +10,9 @@ var Varint = function Varint(buf) { } else if (typeof buf === 'number') { var num = buf; this.fromNumber(num); + } else if (buf instanceof BN) { + var bn = buf; + this.fromBN(bn); } else if (buf) { var obj = buf; this.set(obj); diff --git a/test/varint.js b/test/varint.js index b19ae95..85a9644 100644 --- a/test/varint.js +++ b/test/varint.js @@ -14,6 +14,11 @@ describe('Varint', function() { varint = Varint(buf); should.exist(varint); varint.buf.toString('hex').should.equal('00'); + + //various ways to use the constructor + Varint(Varint(0).toBuffer()).toNumber().should.equal(0); + Varint(0).toNumber().should.equal(0); + Varint(BN(0)).toNumber().should.equal(0); }); describe('#set', function() { From 8b0b30e226307a733f717e2f0320fef5e5611bfb Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 28 Sep 2014 18:33:54 -0700 Subject: [PATCH 278/280] Initial commit of Full Node all current code is forked from bitcore2 --- .gitignore | 2 +- LICENSE.md | 6 ++-- README.md | 18 +++++----- browser/build | 2 +- index.js | 96 +++++++++++++++++++++++++-------------------------- package.json | 8 ++--- 6 files changed, 65 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index 77a88c2..94e0bc0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ *.swp coverage node_modules -browser/bitcore.js +browser/fullnode.js browser/tests.js diff --git a/LICENSE.md b/LICENSE.md index 19004df..6fdf61a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,10 +1,10 @@ This software is licensed under the MIT License. -Copyright (c) 2014 BitPay Inc. - -Parts of this software are based on privsec Copyright (c) 2014 Ryan X. Charles +Parts of this software are based on bitcore +Copyright (c) 2014 BitPay Inc. + Parts of this software are based on BitcoinJS Copyright (c) 2011 Stefan Thomas diff --git a/README.md b/README.md index a32d8bf..c374bef 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,13 @@ -bitcore2 -======== +Full Node +========= -Bitcore 2 is a rewrite of bitcore. It is alpha quality. It will ultimately be -merged into upstream bitcore. - -Our goals: +Full Node is a javascript implementation of bitcoin, forked from bitcore, +intended to satisfy certain goals: 1) Support ease-of-use by being internally consistent. It should not be necessary to read the source code of a class or function to know how to use it. -I.e., where in old bitcore a "privkey" might be anything from a buffer to a hex -string to a key to a private key object, in bitcore 2 "privkey" is the same +I.e., where in bitcore a "privkey" might be anything from a buffer to a hex +string to a key to a private key object, in fullnode "privkey" is the same type of object always and everywhere. 2) Have 100% test coverage so that the library is known to be reliable. @@ -23,12 +21,12 @@ formats such as transactions and blocks. lib/. All BIPs are correctly implemented and saved as BIPxx.js in lib/ (since that is their standard name). Any non-standard features (such as SINs and stealth addresses) are placed in the lib/expmt/ folder and are accessible at -bitcore.expmt. Once they are standardized and given a BIP, they are renamed and +fullnode.expmt. Once they are standardized and given a BIP, they are renamed and placed in lib/. 5) It is always possible to create a new object without using "new". -6) Compatible with browserify (i.e., using require('bitcore/lib/message') +6) Compatible with browserify (i.e., using require('fullnode/lib/message') should work both in node, and be automatically work in the browser with used in conjunction with browserify). diff --git a/browser/build b/browser/build index fca4b11..a2deef5 100755 --- a/browser/build +++ b/browser/build @@ -1,4 +1,4 @@ #!/bin/bash -browserify index.js -o browser/bitcore.js +browserify index.js -o browser/fullnode.js ls test/*.js | xargs browserify -o browser/tests.js diff --git a/index.js b/index.js index 7df3dda..d4bae10 100644 --- a/index.js +++ b/index.js @@ -1,56 +1,56 @@ -var bitcore = module.exports; +var fullnode = module.exports; //main bitcoin library -bitcore.Address = require('./lib/address'); -bitcore.Base58 = require('./lib/base58'); -bitcore.Base58Check = require('./lib/base58check'); -bitcore.BIP32 = require('./lib/bip32'); -bitcore.Block = require('./lib/block'); -bitcore.Blockheader = require('./lib/blockheader'); -bitcore.BN = require('./lib/bn'); -bitcore.BufferReader = require('./lib/bufferreader'); -bitcore.BufferWriter = require('./lib/bufferwriter'); -bitcore.Constants = require('./lib/constants'); -bitcore.ECDSA = require('./lib/ecdsa'); -bitcore.Hash = require('./lib/hash'); -bitcore.KDF = require('./lib/kdf'); -bitcore.Keypair = require('./lib/keypair'); -bitcore.Message = require('./lib/message'); -bitcore.Opcode = require('./lib/opcode'); -bitcore.Point = require('./lib/point'); -bitcore.Privkey = require('./lib/privkey'); -bitcore.Pubkey = require('./lib/pubkey'); -bitcore.Random = require('./lib/random'); -bitcore.Script = require('./lib/script'); -bitcore.Signature = require('./lib/signature'); -bitcore.Transaction = require('./lib/transaction'); -bitcore.Txin = require('./lib/txin'); -bitcore.Txout = require('./lib/txout'); -bitcore.Varint = require('./lib/varint'); +fullnode.Address = require('./lib/address'); +fullnode.Base58 = require('./lib/base58'); +fullnode.Base58Check = require('./lib/base58check'); +fullnode.BIP32 = require('./lib/bip32'); +fullnode.Block = require('./lib/block'); +fullnode.Blockheader = require('./lib/blockheader'); +fullnode.BN = require('./lib/bn'); +fullnode.BufferReader = require('./lib/bufferreader'); +fullnode.BufferWriter = require('./lib/bufferwriter'); +fullnode.Constants = require('./lib/constants'); +fullnode.ECDSA = require('./lib/ecdsa'); +fullnode.Hash = require('./lib/hash'); +fullnode.KDF = require('./lib/kdf'); +fullnode.Keypair = require('./lib/keypair'); +fullnode.Message = require('./lib/message'); +fullnode.Opcode = require('./lib/opcode'); +fullnode.Point = require('./lib/point'); +fullnode.Privkey = require('./lib/privkey'); +fullnode.Pubkey = require('./lib/pubkey'); +fullnode.Random = require('./lib/random'); +fullnode.Script = require('./lib/script'); +fullnode.Signature = require('./lib/signature'); +fullnode.Transaction = require('./lib/transaction'); +fullnode.Txin = require('./lib/txin'); +fullnode.Txout = require('./lib/txout'); +fullnode.Varint = require('./lib/varint'); //experimental, nonstandard, or unstable features -bitcore.expmt = {}; -bitcore.expmt.AES = require('./lib/expmt/aes'); -bitcore.expmt.AESCBC = require('./lib/expmt/aescbc'); -bitcore.expmt.CBC = require('./lib/expmt/cbc'); -bitcore.expmt.ECIES = require('./lib/expmt/ecies'); -bitcore.expmt.StealthAddress = require('./lib/expmt/stealthaddress'); -bitcore.expmt.Stealthkey = require('./lib/expmt/stealthkey'); -bitcore.expmt.StealthMessage = require('./lib/expmt/stealthmessage'); -bitcore.expmt.StealthTx = require('./lib/expmt/stealthtx'); +fullnode.expmt = {}; +fullnode.expmt.AES = require('./lib/expmt/aes'); +fullnode.expmt.AESCBC = require('./lib/expmt/aescbc'); +fullnode.expmt.CBC = require('./lib/expmt/cbc'); +fullnode.expmt.ECIES = require('./lib/expmt/ecies'); +fullnode.expmt.StealthAddress = require('./lib/expmt/stealthaddress'); +fullnode.expmt.Stealthkey = require('./lib/expmt/stealthkey'); +fullnode.expmt.StealthMessage = require('./lib/expmt/stealthmessage'); +fullnode.expmt.StealthTx = require('./lib/expmt/stealthtx'); //dependencies, subject to change -bitcore.deps = {}; -bitcore.deps.aes = require('aes'); -bitcore.deps.bnjs = require('bn.js'); -bitcore.deps.bs58 = require('bs58'); -bitcore.deps.Buffer = Buffer; -bitcore.deps.elliptic = require('elliptic'); -bitcore.deps.hashjs = require('hash.js'); -bitcore.deps.sha512 = require('sha512'); +fullnode.deps = {}; +fullnode.deps.aes = require('aes'); +fullnode.deps.bnjs = require('bn.js'); +fullnode.deps.bs58 = require('bs58'); +fullnode.deps.Buffer = Buffer; +fullnode.deps.elliptic = require('elliptic'); +fullnode.deps.hashjs = require('hash.js'); +fullnode.deps.sha512 = require('sha512'); -//bitcore.scriptexec = require('lib/scriptexec'); -//bitcore.tx = require('lib/tx'); -//bitcore.txpartial = require('lib/txpartial'); +//fullnode.scriptexec = require('lib/scriptexec'); +//fullnode.tx = require('lib/tx'); +//fullnode.txpartial = require('lib/txpartial'); -//bitcore.bip70 = require('lib/bip70'); +//fullnode.bip70 = require('lib/bip70'); diff --git a/package.json b/package.json index 374bf34..2970276 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { - "name": "bitcore2", + "name": "fullnode", "version": "0.0.0", - "description": "Bitcoin library.", + "description": "Bitcoin library, CLI and API.", + "author": "Ryan X. Charles ", "main": "index.js", "scripts": { "test": "mocha" @@ -59,7 +60,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/ryanxcharles/bitcore2.git" + "url": "https://github.com/ryanxcharles/fullnode.git" }, "dependencies": { "aes": "=0.1.0", @@ -74,6 +75,5 @@ "mocha": "~1.21.0", "browserify": "~5.9.1" }, - "author": "Ryan X. Charles ", "license": "MIT" } From 11919a5fee3e2b94ab54bcbd5dd60adf653ca513 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 28 Sep 2014 18:38:31 -0700 Subject: [PATCH 279/280] clarify README --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index c374bef..d2f45b6 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,6 @@ intended to satisfy certain goals: 1) Support ease-of-use by being internally consistent. It should not be necessary to read the source code of a class or function to know how to use it. -I.e., where in bitcore a "privkey" might be anything from a buffer to a hex -string to a key to a private key object, in fullnode "privkey" is the same -type of object always and everywhere. 2) Have 100% test coverage so that the library is known to be reliable. @@ -37,7 +34,7 @@ To access the result of an instance method, you must access the object property(s) that it modifies. ------------------------- -Features over bitcore: +Key features: * Stealth keys, addresses, message * Proper handling of reading and writing big varInts * Browserifiable From b38a6671f7f2185aaf314730d230412b8fafcfde Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Sun, 28 Sep 2014 18:39:19 -0700 Subject: [PATCH 280/280] clarify README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d2f45b6..0518cb1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Full Node ========= -Full Node is a javascript implementation of bitcoin, forked from bitcore, +Full Node is a javascript implementation of bitcoin, forked from bitcore2, intended to satisfy certain goals: 1) Support ease-of-use by being internally consistent. It should not be