Browse Source

Merge remote-tracking branch 'matiu/feature/support-soop-browser'

Conflicts:
	Script.js
	ScriptInterpreter.js
	Transaction.js
	test/testdata.js

...conflicts resolved by taking Manuel's changes, and then manually including
Matias's changes on those same files. The conflicts resulted from differences
in indentation, which is because Matias' changes unindendented all the code
that had been but is not now inside a function.
patch-2
Ryan X. Charles 11 years ago
parent
commit
c19fb7a3ce
  1. 33
      Address.js
  2. 993
      Block.js
  3. 213
      Bloom.js
  4. 945
      Connection.js
  5. 28
      Gruntfile.js
  6. 315
      Opcode.js
  7. 105
      Peer.js
  8. 371
      PeerManager.js
  9. 115
      PrivateKey.js
  10. 30
      README.md
  11. 372
      RpcClient.js
  12. 103
      SIN.js
  13. 62
      SINKey.js
  14. 943
      Script.js
  15. 1921
      ScriptInterpreter.js
  16. 1401
      Transaction.js
  17. 274
      Wallet.js
  18. 91
      WalletKey.js
  19. 14
      bitcore.js
  20. 6
      browser/bignum_config.js
  21. 88
      browserify.js
  22. 2
      examples/Address.js
  23. 7
      examples/PeerManager.js
  24. 2
      examples/Rpc.js
  25. 13
      examples/SendTx.js
  26. 37
      examples/example.html
  27. 5
      package.json
  28. 9
      test/index.html
  29. 6
      test/test.Address.js
  30. 6
      test/test.Block.js
  31. 6
      test/test.Bloom.js
  32. 6
      test/test.Connection.js
  33. 6
      test/test.EncodedData.js
  34. 4
      test/test.Key.js
  35. 6
      test/test.Opcode.js
  36. 6
      test/test.Peer.js
  37. 9
      test/test.PeerManager.js
  38. 6
      test/test.PrivateKey.js
  39. 6
      test/test.RpcClient.js
  40. 6
      test/test.SIN.js
  41. 9
      test/test.SINKey.js
  42. 8
      test/test.Script.js
  43. 6
      test/test.ScriptInterpreter.js
  44. 8
      test/test.Transaction.js
  45. 6
      test/test.VersionedData.js
  46. 6
      test/test.Wallet.js
  47. 6
      test/test.WalletKey.js
  48. 22
      test/test.basic.js
  49. 4
      test/test.main.js
  50. 4
      test/test.misc.js
  51. 5
      test/test.util.js
  52. 2
      test/testdata.js
  53. 252
      util/BinaryParser.js
  54. 285
      util/EncodedData.js
  55. 63
      util/VersionedData.js

33
Address.js

@ -1,22 +1,19 @@
require('classtool'); 'use strict';
var imports = require('soop').imports();
var parent = imports.parent || require('./util/VersionedData');
function ClassSpec(b) { function Address() {
var superclass = b.superclass || require('./util/VersionedData').class(); Address.super(this, arguments);
}
function Address() {
Address.super(this, arguments);
}
Address.superclass = superclass; Address.parent = parent;
superclass.applyEncodingsTo(Address); parent.applyEncodingsTo(Address);
Address.prototype.validate = function() { Address.prototype.validate = function() {
this.doAsBinary(function() { this.doAsBinary(function() {
Address.super(this, 'validate', arguments); Address.super(this, 'validate', arguments);
if(this.data.length !== 21) throw new Error('invalid data length'); if(this.data.length !== 21) throw new Error('invalid data length');
}); });
}; };
return Address; module.exports = require('soop')(Address);
}
module.defineClass(ClassSpec);

993
Block.js

File diff suppressed because it is too large

213
Bloom.js

@ -1,116 +1,111 @@
require('classtool'); var MAX_BLOOM_FILTER_SIZE = 36000; // bytes
var MAX_HASH_FUNCS = 50;
function ClassSpec(b) { var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
var MAX_BLOOM_FILTER_SIZE = 36000; // bytes var LN2 = 0.6931471805599453094172321214581765680755001343602552;
var MAX_HASH_FUNCS = 50; var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80];
var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
var LN2 = 0.6931471805599453094172321214581765680755001343602552; function Bloom() {
var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]; this.data = '';
this.hashFuncs = 0;
function Bloom() { };
this.data = '';
this.hashFuncs = 0; function ROTL32(x, r) {
}; return (x << r) | (x >> (32 - r));
};
function ROTL32(x, r) {
return (x << r) | (x >> (32 - r)); function getBlockU32(blockIdx, data) {
}; var idx = blockIdx * 4;
var v = (data[idx + 0] << (0 * 8)) |
function getBlockU32(blockIdx, data) { (data[idx + 1] << (1 * 8)) |
var idx = blockIdx * 4; (data[idx + 2] << (2 * 8)) |
var v = (data[idx + 0] << (0 * 8)) | (data[idx + 3] << (3 * 8));
(data[idx + 1] << (1 * 8)) | return v;
(data[idx + 2] << (2 * 8)) | };
(data[idx + 3] << (3 * 8));
return v; Bloom.prototype.hash = function(hashNum, data) {
}; var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1));
var c1 = 0xcc9e2d51;
Bloom.prototype.hash = function(hashNum, data) { var c2 = 0x1b873593;
var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1)); var nBlocks = data.length / 4;
var c1 = 0xcc9e2d51;
var c2 = 0x1b873593; // data body
var nBlocks = data.length / 4; for (var i = -nBlocks; i; i++) {
var k1 = getBlockU32(i);
// data body
for (var i = -nBlocks; i; i++) { k1 *= c1;
var k1 = getBlockU32(i); k1 = ROTLF32(k1, 15);
k1 *= c2;
k1 *= c1;
k1 = ROTLF32(k1, 15); h1 ^= k1;
k1 *= c2; h1 = ROTFL(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
h1 ^= k1;
h1 = ROTFL(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
}
// tail (trailing 1-3 bytes)
var tail = data.slice(nBlocks * 4);
var k1 = 0;
switch (data.length & 3) {
case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0];
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
}
// finalize
h1 ^= data.length;
h1 ^= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >> 16;
return h1 % (this.data.length * 8);
};
Bloom.prototype.insert = function(data) {
for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data);
this.data[index >> 3] |= bit_mask[7 & index];
}
};
Bloom.prototype.contains = function(data) {
for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data);
if (!(this.data[index >> 3] & bit_mask[7 & index]))
return false;
}
return true;
};
Bloom.prototype.sizeOk = function() {
return this.data.length <= MAX_BLOOM_FILTER_SIZE &&
this.hashFuncs <= MAX_HASH_FUNCS;
};
function toInt(v) {
return ~~v;
} }
function min(a, b) { // tail (trailing 1-3 bytes)
if (a < b) var tail = data.slice(nBlocks * 4);
return a;
return b; var k1 = 0;
switch (data.length & 3) {
case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0];
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
} }
Bloom.prototype.init = function(elements, FPRate) { // finalize
var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)), h1 ^= data.length;
MAX_BLOOM_FILTER_SIZE * 8) / 8; h1 ^= h1 >> 16;
this.data[filterSize] = 0; h1 *= 0x85ebca6b;
this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2), h1 ^= h1 >> 13;
MAX_HASH_FUNCS); h1 *= 0xc2b2ae35;
}; h1 ^= h1 >> 16;
return Bloom; return h1 % (this.data.length * 8);
}; };
module.defineClass(ClassSpec);
Bloom.prototype.insert = function(data) {
for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data);
this.data[index >> 3] |= bit_mask[7 & index];
}
};
Bloom.prototype.contains = function(data) {
for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data);
if (!(this.data[index >> 3] & bit_mask[7 & index]))
return false;
}
return true;
};
Bloom.prototype.sizeOk = function() {
return this.data.length <= MAX_BLOOM_FILTER_SIZE &&
this.hashFuncs <= MAX_HASH_FUNCS;
};
function toInt(v) {
return ~~v;
}
function min(a, b) {
if (a < b)
return a;
return b;
}
Bloom.prototype.init = function(elements, FPRate) {
var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)),
MAX_BLOOM_FILTER_SIZE * 8) / 8;
this.data[filterSize] = 0;
this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2),
MAX_HASH_FUNCS);
};
module.exports = require('soop')(Bloom);

945
Connection.js

File diff suppressed because it is too large

28
Gruntfile.js

@ -3,26 +3,28 @@
module.exports = function(grunt) { module.exports = function(grunt) {
//Load NPM tasks //Load NPM tasks
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-mocha-test'); grunt.loadNpmTasks('grunt-mocha-test');
grunt.loadNpmTasks('grunt-markdown'); grunt.loadNpmTasks('grunt-markdown');
grunt.loadNpmTasks('grunt-shell');
// Project Configuration // Project Configuration
grunt.initConfig({ grunt.initConfig({
browserify: { shell: {
client: { browserify: {
src: ['bitcore.js'],
dest: 'browser/bundle.js',
options: { options: {
debug: true, stdout: true
alias: [ },
'browserify-bignum/bignumber.js:bignum', command: 'node ./browserify.js > browser/bundle.js',
'browserify-buffertools/buffertools.js:buffertools'
],
standalone: 'bitcore',
}
}, },
browserifyData: {
options: {
stdout: true
},
command: 'browserify -t brfs test/testdata.js > browser/testdata.js'
},
},
browserify: {
test_data: { test_data: {
src: ['test/testdata.js'], src: ['test/testdata.js'],
dest: 'browser/testdata.js', dest: 'browser/testdata.js',
@ -40,7 +42,7 @@ module.exports = function(grunt) {
}, },
scripts: { scripts: {
files: ['**/*.js', '**/*.html', '!**/node_modules/**', '!browser/bundle.js', '!browser/testdata.js'], files: ['**/*.js', '**/*.html', '!**/node_modules/**', '!browser/bundle.js', '!browser/testdata.js'],
tasks: ['browserify' /*, 'mochaTest'*/ ], tasks: ['shell' /*, 'mochaTest'*/ ],
}, },
}, },
mochaTest: { mochaTest: {

315
Opcode.js

@ -1,161 +1,158 @@
require('classtool'); var imports = require('soop').imports();
function spec(b) { function Opcode(num) {
function Opcode(num) { this.code = num;
this.code = num; };
};
Opcode.prototype.toString = function () {
Opcode.prototype.toString = function () { return Opcode.reverseMap[this.code];
return Opcode.reverseMap[this.code]; };
};
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.substr(3);
}
}
return Opcode; 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
}; };
module.defineClass(spec);
Opcode.reverseMap = [];
for (var k in Opcode.map) {
if(Opcode.map.hasOwnProperty(k)) {
Opcode.reverseMap[Opcode.map[k]] = k.substr(3);
}
}
module.exports = require('soop')(Opcode);

105
Peer.js

@ -1,61 +1,58 @@
require('classtool'); var imports = require('soop').imports();
function spec(b) { var Net = imports.Net || require('net');
var Net = b.Net || require('net'); var Binary = imports.Binary || require('binary');
var Binary = b.Binary || require('binary'); var buffertools = imports.buffertools || require('buffertools');
var buffertools = b.buffertools || require('buffertools');
function Peer(host, port, services) {
function Peer(host, port, services) { if ("string" === typeof host) {
if ("string" === typeof host) { if (host.indexOf(':') && !port) {
if (host.indexOf(':') && !port) { var parts = host.split(':');
var parts = host.split(':'); host = parts[0];
host = parts[0]; port = parts[1];
port = parts[1];
}
this.host = host;
this.port = +port || 8333;
} else if (host instanceof Peer) {
this.host = host.host;
this.port = host.port;
} else if (Buffer.isBuffer(host)) {
if (buffertools.compare(Peer.IPV6_IPV4_PADDING, host.slice(0, 12)) != 0) {
throw new Error('IPV6 not supported yet! Cannot instantiate host.');
}
this.host = Array.prototype.slice.apply(host.slice(12)).join('.');
this.port = +port || 8333;
} else {
throw new Error('Could not instantiate peer, invalid parameter type: ' +
typeof host);
} }
this.host = host;
this.port = +port || 8333;
} else if (host instanceof Peer) {
this.host = host.host;
this.port = host.port;
} else if (Buffer.isBuffer(host)) {
if (buffertools.compare(Peer.IPV6_IPV4_PADDING, host.slice(0, 12)) != 0) {
throw new Error('IPV6 not supported yet! Cannot instantiate host.');
}
this.host = Array.prototype.slice.apply(host.slice(12)).join('.');
this.port = +port || 8333;
} else {
throw new Error('Could not instantiate peer, invalid parameter type: ' +
typeof host);
}
this.services = (services) ? services : null;
this.lastSeen = 0;
};
this.services = (services) ? services : null; Peer.IPV6_IPV4_PADDING = new Buffer([0,0,0,0,0,0,0,0,0,0,255,255]);
this.lastSeen = 0;
};
Peer.IPV6_IPV4_PADDING = new Buffer([0,0,0,0,0,0,0,0,0,0,255,255]);
Peer.prototype.createConnection = function () {
var c = Net.createConnection(this.port, this.host);
return c;
};
Peer.prototype.getHostAsBuffer = function () { Peer.prototype.createConnection = function () {
return new Buffer(this.host.split('.')); var c = Net.createConnection(this.port, this.host);
}; return c;
};
Peer.prototype.toString = function () { Peer.prototype.getHostAsBuffer = function () {
return this.host + ":" + this.port; return new Buffer(this.host.split('.'));
}; };
Peer.prototype.toBuffer = function () { Peer.prototype.toString = function () {
var put = Binary.put(); return this.host + ":" + this.port;
put.word32le(this.lastSeen); };
put.word64le(this.services);
put.put(this.getHostAsBuffer());
put.word16be(this.port);
return put.buffer();
};
return Peer; Peer.prototype.toBuffer = function () {
var put = Binary.put();
put.word32le(this.lastSeen);
put.word64le(this.services);
put.put(this.getHostAsBuffer());
put.word16be(this.port);
return put.buffer();
}; };
module.defineClass(spec);
module.exports = require('soop')(Peer);

371
PeerManager.js

@ -1,215 +1,214 @@
require('classtool');
function spec(b) {
var config = b.config || require('./config');
var log = b.log || require('./util/log');
var network = b.network || require('./networks')[config.network];
var Connection = b.Connection || require('./Connection').createClass(
{config: config, network: network});
var Peer = b.Peer || require('./Peer').class();
var noop = function() {};
GetAdjustedTime = b.GetAdjustedTime || function () {
// TODO: Implement actual adjustment
return Math.floor(new Date().getTime() / 1000);
};
function PeerManager() { var imports = require('soop').imports();
this.active = false; var config = imports.config || require('./config');
this.timer = null; var log = imports.log || require('./util/log');
var network = imports.network || require('./networks')[config.network];
this.peers = []; var Connection = imports.Connection ||
this.connections = []; require('soop').load('Connection', {config: config, network: network}) ||
this.isConnected = false; require ('./Connection');
this.peerDiscovery = false;
// Move these to the Node's settings object var Peer = imports.Peer || require('./Peer');
this.interval = 5000;
this.minConnections = 8;
this.minKnownPeers = 10;
};
PeerManager.superclass = b.superclass || require('events').EventEmitter;
PeerManager.Connection = Connection; GetAdjustedTime = imports.GetAdjustedTime || function () {
// TODO: Implement actual adjustment
return Math.floor(new Date().getTime() / 1000);
};
PeerManager.prototype.start = function() { function PeerManager() {
this.active = true; this.active = false;
if(!this.timer) { this.timer = null;
this.timer = setInterval(this.checkStatus.bind(this), this.interval);
}
};
PeerManager.prototype.stop = function() { this.peers = [];
this.active = false; this.connections = [];
if(this.timer) { this.isConnected = false;
clearInterval(this.timer); this.peerDiscovery = false;
this.timer = null;
}
for(var i=0; i<this.connections.length; i++) {
this.connections[i].socket.end();
};
};
PeerManager.prototype.addPeer = function(peer, port) { // Move these to the Node's settings object
if(peer instanceof Peer) { this.interval = 5000;
this.peers.push(peer); this.minConnections = 8;
} else if ("string" == typeof peer) { this.minKnownPeers = 10;
this.addPeer(new Peer(peer, port)); };
} else {
log.err('Node.addPeer(): Invalid value provided for peer',
{val: peer});
throw 'Node.addPeer(): Invalid value provided for peer.';
}
};
PeerManager.prototype.checkStatus = function checkStatus() { PeerManager.parent = imports.parent || require('events').EventEmitter;
// Make sure we are connected to all forcePeers PeerManager.Connection = Connection;
if(this.peers.length) {
var peerIndex = {};
this.peers.forEach(function(peer) {
peerIndex[peer.toString()] = peer;
});
// Ignore the ones we're already connected to
this.connections.forEach(function(conn) {
var peerName = conn.peer.toString();
if("undefined" !== peerIndex[peerName]) {
delete peerIndex[peerName];
}
});
Object.keys(peerIndex).forEach(function(i) {
this.connectTo(peerIndex[i]);
}.bind(this));
}
};
PeerManager.prototype.connectTo = function(peer) { PeerManager.prototype.start = function() {
log.info('connecting to '+peer); this.active = true;
try { if(!this.timer) {
return this.addConnection(peer.createConnection(), peer); this.timer = setInterval(this.checkStatus.bind(this), this.interval);
} catch (e) { }
log.err('creating connection',e); };
return null;
} PeerManager.prototype.stop = function() {
this.active = false;
if(this.timer) {
clearInterval(this.timer);
this.timer = null;
}
for(var i=0; i<this.connections.length; i++) {
this.connections[i].socket.end();
}; };
};
PeerManager.prototype.addConnection = function(socketConn, peer) { PeerManager.prototype.addPeer = function(peer, port) {
var conn = new Connection(socketConn, peer); if(peer instanceof Peer) {
this.connections.push(conn); this.peers.push(peer);
this.emit('connection', conn); } else if ("string" == typeof peer) {
this.addPeer(new Peer(peer, port));
} else {
log.err('Node.addPeer(): Invalid value provided for peer',
{val: peer});
throw 'Node.addPeer(): Invalid value provided for peer.';
}
};
conn.addListener('version', this.handleVersion.bind(this)); PeerManager.prototype.checkStatus = function checkStatus() {
conn.addListener('verack', this.handleReady.bind(this)); // Make sure we are connected to all forcePeers
conn.addListener('addr', this.handleAddr.bind(this)); if(this.peers.length) {
conn.addListener('getaddr', this.handleGetAddr.bind(this)); var peerIndex = {};
conn.addListener('error', this.handleError.bind(this)); this.peers.forEach(function(peer) {
conn.addListener('disconnect', this.handleDisconnect.bind(this)); peerIndex[peer.toString()] = peer;
});
return conn; // Ignore the ones we're already connected to
}; this.connections.forEach(function(conn) {
var peerName = conn.peer.toString();
if("undefined" !== peerIndex[peerName]) {
delete peerIndex[peerName];
}
});
PeerManager.prototype.handleVersion = function(e) { Object.keys(peerIndex).forEach(function(i) {
if (!e.conn.inbound) { this.connectTo(peerIndex[i]);
// TODO: Advertise our address (if listening) }.bind(this));
} }
// Get recent addresses };
if(this.peerDiscovery &&
(e.message.version >= 31402 || this.peers.length < 1000)) {
e.conn.sendGetAddr();
e.conn.getaddr = true;
}
};
PeerManager.prototype.handleReady = function (e) { PeerManager.prototype.connectTo = function(peer) {
log.info('connected to '+e.conn.peer.host+':'+e.conn.peer.port); log.info('connecting to '+peer);
this.emit('connect', { try {
pm: this, return this.addConnection(peer.createConnection(), peer);
conn: e.conn, } catch (e) {
socket: e.socket, log.err('creating connection',e);
peer: e.peer return null;
}); }
};
if(this.isConnected == false) { PeerManager.prototype.addConnection = function(socketConn, peer) {
this.emit('netConnected'); var conn = new Connection(socketConn, peer);
this.isConnected = true; this.connections.push(conn);
} this.emit('connection', conn);
};
conn.addListener('version', this.handleVersion.bind(this));
conn.addListener('verack', this.handleReady.bind(this));
conn.addListener('addr', this.handleAddr.bind(this));
conn.addListener('getaddr', this.handleGetAddr.bind(this));
conn.addListener('error', this.handleError.bind(this));
conn.addListener('disconnect', this.handleDisconnect.bind(this));
return conn;
};
PeerManager.prototype.handleVersion = function(e) {
if (!e.conn.inbound) {
// TODO: Advertise our address (if listening)
}
// Get recent addresses
if(this.peerDiscovery &&
(e.message.version >= 31402 || this.peers.length < 1000)) {
e.conn.sendGetAddr();
e.conn.getaddr = true;
}
};
PeerManager.prototype.handleReady = function (e) {
log.info('connected to '+e.conn.peer.host+':'+e.conn.peer.port);
this.emit('connect', {
pm: this,
conn: e.conn,
socket: e.socket,
peer: e.peer
});
if(this.isConnected == false) {
this.emit('netConnected');
this.isConnected = true;
}
};
PeerManager.prototype.handleAddr = function (e) { PeerManager.prototype.handleAddr = function (e) {
if(!this.peerDiscovery) return; if(!this.peerDiscovery) return;
var now = GetAdjustedTime(); var now = GetAdjustedTime();
e.message.addrs.forEach(function (addr) { e.message.addrs.forEach(function (addr) {
try { try {
// In case of an invalid time, assume "5 days ago" // In case of an invalid time, assume "5 days ago"
if (addr.time <= 100000000 || addr.time > (now + 10 * 60)) { if (addr.time <= 100000000 || addr.time > (now + 10 * 60)) {
addr.time = now - 5 * 24 * 60 * 60; addr.time = now - 5 * 24 * 60 * 60;
}
var peer = new Peer(addr.ip, addr.port, addr.services);
peer.lastSeen = addr.time;
// TODO: Handle duplicate peers
this.peers.push(peer);
// TODO: Handle addr relay
} catch(e) {
log.warn("Invalid addr received: "+e.message);
} }
}.bind(this)); var peer = new Peer(addr.ip, addr.port, addr.services);
if (e.message.addrs.length < 1000 ) { peer.lastSeen = addr.time;
e.conn.getaddr = false;
// TODO: Handle duplicate peers
this.peers.push(peer);
// TODO: Handle addr relay
} catch(e) {
log.warn("Invalid addr received: "+e.message);
} }
}; }.bind(this));
if (e.message.addrs.length < 1000 ) {
e.conn.getaddr = false;
}
};
PeerManager.prototype.handleGetAddr = function(e) { PeerManager.prototype.handleGetAddr = function(e) {
// TODO: Reply with addr message. // TODO: Reply with addr message.
}; };
PeerManager.prototype.handleError = function(e) { PeerManager.prototype.handleError = function(e) {
log.err('unkown error with peer '+e.peer+' (disconnecting): '+e.err); log.err('unkown error with peer '+e.peer+' (disconnecting): '+e.err);
this.handleDisconnect.apply(this, [].slice.call(arguments)); this.handleDisconnect.apply(this, [].slice.call(arguments));
}; };
PeerManager.prototype.handleDisconnect = function(e) { PeerManager.prototype.handleDisconnect = function(e) {
log.info('disconnected from peer '+e.peer); log.info('disconnected from peer '+e.peer);
var i = this.connections.indexOf(e.conn); var i = this.connections.indexOf(e.conn);
if(i != -1) this.connections.splice(i, 1); if(i != -1) this.connections.splice(i, 1);
if(!this.connections.length) { if(!this.connections.length) {
this.emit('netDisconnected'); this.emit('netDisconnected');
this.isConnected = false; this.isConnected = false;
} }
}; };
PeerManager.prototype.getActiveConnection = function () { PeerManager.prototype.getActiveConnection = function () {
var activeConnections = this.connections.filter(function (conn) { var activeConnections = this.connections.filter(function (conn) {
return conn.active; return conn.active;
}); });
if (activeConnections.length) { if (activeConnections.length) {
var randomIndex = Math.floor(Math.random()*activeConnections.length); var randomIndex = Math.floor(Math.random()*activeConnections.length);
var candidate = activeConnections[randomIndex]; var candidate = activeConnections[randomIndex];
if (candidate.socket.writable) { if (candidate.socket.writable) {
return candidate; return candidate;
} else {
// Socket is not writable, remove it from active connections
activeConnections.splice(randomIndex, 1);
// Then try again
// TODO: This causes an infinite recursion when all connections are dead,
// although it shouldn't.
return this.getActiveConnection();
}
} else { } else {
return null; // Socket is not writable, remove it from active connections
} activeConnections.splice(randomIndex, 1);
};
PeerManager.prototype.getActiveConnections = function () { // Then try again
return this.connections.slice(0); // TODO: This causes an infinite recursion when all connections are dead,
}; // although it shouldn't.
return this.getActiveConnection();
}
} else {
return null;
}
};
return PeerManager; PeerManager.prototype.getActiveConnections = function () {
return this.connections.slice(0);
}; };
module.defineClass(spec);
module.exports = require('soop')(PeerManager);

115
PrivateKey.js

@ -1,67 +1,64 @@
require('classtool'); var imports = require('soop').imports();
function ClassSpec(b) { var parent = imports.parent || require('./util/VersionedData');
var superclass = b.superclass || require('./util/VersionedData').class();
//compressed is true if public key is compressed; false otherwise //compressed is true if public key is compressed; false otherwise
function PrivateKey(version, buf, compressed) { function PrivateKey(version, buf, compressed) {
PrivateKey.super(this, arguments); PrivateKey.super(this, arguments);
if (compressed !== undefined) if (compressed !== undefined)
this.compressed(compressed); this.compressed(compressed);
}; };
PrivateKey.superclass = superclass; PrivateKey.parent = parent;
superclass.applyEncodingsTo(PrivateKey); parent.applyEncodingsTo(PrivateKey);
PrivateKey.prototype.validate = function() { PrivateKey.prototype.validate = function() {
this.doAsBinary(function() { this.doAsBinary(function() {
PrivateKey.super(this, 'validate', arguments); PrivateKey.super(this, 'validate', arguments);
if (this.data.length < 32 || (this.data.length > 1+32 && !this.compressed()) || (this.data.length==1+32+1 && this.data[1+32+1-1]!=1) || this.data.length>1+32+1) if (this.data.length < 32 || (this.data.length > 1+32 && !this.compressed()) || (this.data.length==1+32+1 && this.data[1+32+1-1]!=1) || this.data.length>1+32+1)
throw new Error('invalid data length'); throw new Error('invalid data length');
}); });
}; };
// get or set the payload data (as a Buffer object) // get or set the payload data (as a Buffer object)
// overloaded from VersionedData // overloaded from VersionedData
PrivateKey.prototype.payload = function(data) { PrivateKey.prototype.payload = function(data) {
if(data) { if(data) {
this.doAsBinary(function() {data.copy(this.data,1);}); this.doAsBinary(function() {data.copy(this.data,1);});
return data; return data;
} }
var buf=this.as('binary'); var buf=this.as('binary');
if (buf.length==1+32+1) if (buf.length==1+32+1)
return buf.slice(1,1+32); return buf.slice(1,1+32);
else if (buf.length==1+32) else if (buf.length==1+32)
return buf.slice(1); return buf.slice(1);
}; };
// get or set whether the corresponding public key is compressed // get or set whether the corresponding public key is compressed
PrivateKey.prototype.compressed = function(compressed) { PrivateKey.prototype.compressed = function(compressed) {
if (compressed !== undefined) { if (compressed !== undefined) {
this.doAsBinary(function(){ this.doAsBinary(function(){
var len=1+32+1;
if (compressed) {
var data=new Buffer(len);
this.data.copy(data);
this.data=data;
this.data[len-1]=1;
} else {
this.data=this.data.slice(0,len-1);
}
});
}
else {
var len=1+32+1; var len=1+32+1;
var data=this.as('binary'); if (compressed) {
if (data.length==len && data[len-1]==1) var data=new Buffer(len);
return true; this.data.copy(data);
else if (data.length==len-1) this.data=data;
return false; this.data[len-1]=1;
else } else {
throw new Error('invalid private key'); this.data=this.data.slice(0,len-1);
} }
}; });
}
return PrivateKey; else {
var len=1+32+1;
var data=this.as('binary');
if (data.length==len && data[len-1]==1)
return true;
else if (data.length==len-1)
return false;
else
throw new Error('invalid private key');
}
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(PrivateKey);

30
README.md

@ -17,9 +17,9 @@ Bitcore runs on [node](http://nodejs.org/), and can be installed via [npm](https
npm install bitcore npm install bitcore
``` ```
It is a collection of objects useful to bitcoin applications; class-like idioms are enabled via [Classtool](https://github.com/gasteve/classtool). In most cases, a developer will require the object's class directly: It is a collection of objects useful to bitcoin applications; class-like idioms are enabled via [Soop](https://github.com/gasteve/soop). In most cases, a developer will require the object's class directly:
``` ```
var Address = require('bitcore/Address').class(); var Address = require('bitcore/Address');
``` ```
#Examples #Examples
@ -29,7 +29,7 @@ Some examples are provided at the [examples](/examples) path. Here are some snip
## Validating an address ## Validating an address
Validating a Bitcoin address: Validating a Bitcoin address:
```js ```js
var Address = require('bitcore/Address').class(); var Address = require('bitcore/Address');
var addrStrings = [ var addrStrings = [
"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
@ -57,10 +57,9 @@ For this example you need a running bitcoind instance with RPC enabled.
```js ```js
var util = require('util'); var util = require('util');
var networks = require('bitcore/networks'); var networks = require('bitcore/networks');
var Peer = require('bitcore/Peer').class(); var Peer = require('bitcore/Peer');
var PeerManager = require('bitcore/PeerManager').createClass({ var PeerManager = require('soop').load('bitcore/PeerManager',
network: networks.testnet {network: networks.testnet});
});
var handleBlock = function(info) { var handleBlock = function(info) {
@ -109,14 +108,13 @@ PeerManager will emit the following events: 'version', 'verack', 'addr', 'getadd
For this example you need a running bitcoind instance with RPC enabled. For this example you need a running bitcoind instance with RPC enabled.
```js ```js
var networks = require('bitcore/networks'); var networks = require('bitcore/networks');
var Peer = require('bitcore/Peer').class(); var Peer = require('bitcore/Peer');
var Transaction = require('bitcore/Transaction').class(); var Transaction = require('bitcore/Transaction');
var Address = require('bitcore/Address').class(); var Address = require('bitcore/Address');
var Script = require('bitcore/Script').class(); var Script = require('bitcore/Script');
var coinUtil = require('bitcore/util/util'); var coinUtil = require('bitcore/util/util');
var PeerManager = require('bitcore/PeerManager').createClass({ var PeerManager = require('soop').load('bitcore/PeerManager',
network: networks.testnet {network: networks.testnet});
});
var createTx = function() { var createTx = function() {
@ -185,7 +183,7 @@ peerman.start();
For this example you need a running bitcoind instance with RPC enabled. For this example you need a running bitcoind instance with RPC enabled.
```js ```js
var util = require('util'); var util = require('util');
var RpcClient = require('bitcore/RpcClient').class(); var RpcClient = require('bitcore/RpcClient');
var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4';
var config = { var config = {
@ -217,7 +215,7 @@ Check the list of all supported RPC call at [RpcClient.js](RpcClient.js)
Gets an address strings from a ScriptPubKey Buffer Gets an address strings from a ScriptPubKey Buffer
``` ```
var Address = require('bitcore/Address').class(); var Address = require('bitcore/Address');
var coinUtil= require('bitcore/util/util'); var coinUtil= require('bitcore/util/util');
var getAddrStr = function(s) { var getAddrStr = function(s) {

372
RpcClient.js

@ -1,210 +1,208 @@
// RpcClient.js // RpcClient.js
// MIT/X11-like license. See LICENSE.txt. // MIT/X11-like license. See LICENSE.txt.
// Copyright 2013 BitPay, Inc. // Copyright 2013 BitPay, Inc.
require('classtool'); //
var imports = require('soop').imports();
var http = imports.http || require('http');
var https = imports.https || require('https');
var log = imports.log || require('./util/log');
function ClassSpec(b) { function RpcClient(opts) {
var http = b.http || require('http'); opts = opts || {};
var https = b.https || require('https'); this.host = opts.host || '127.0.0.1';
var log = b.log || require('./util/log'); this.port = opts.port || 8332;
this.user = opts.user || 'user';
this.pass = opts.pass || 'pass';
this.protocol = (opts.protocol == 'http') ? http : https;
this.batchedCalls = null;
this.disableAgent = opts.disableAgent || false;
}
RpcClient.prototype.batch = function(batchCallback, resultCallback) {
this.batchedCalls = [];
batchCallback();
rpc.call(this, this.batchedCalls, resultCallback);
this.batchedCalls = null;
}
function RpcClient(opts) { var callspec = {
opts = opts || {}; addMultiSigAddress: '',
this.host = opts.host || '127.0.0.1'; addNode: '',
this.port = opts.port || 8332; backupWallet: '',
this.user = opts.user || 'user'; createMultiSig: '',
this.pass = opts.pass || 'pass'; createRawTransaction: '',
this.protocol = (opts.protocol == 'http') ? http : https; decodeRawTransaction: '',
this.batchedCalls = null; dumpPrivKey: '',
this.disableAgent = opts.disableAgent || false; encryptWallet: '',
} getAccount: '',
getAccountAddress: 'str',
RpcClient.prototype.batch = function(batchCallback, resultCallback) { getAddedNodeInfo: '',
this.batchedCalls = []; getAddressesByAccount: '',
batchCallback(); getBalance: 'str int',
rpc.call(this, this.batchedCalls, resultCallback); getBestBlockHash: '',
this.batchedCalls = null; getBlock: '',
} getBlockCount: '',
getBlockHash: 'int',
var callspec = { getBlockNumber: '',
addMultiSigAddress: '', getBlockTemplate: '',
addNode: '', getConnectionCount: '',
backupWallet: '', getDifficulty: '',
createMultiSig: '', getGenerate: '',
createRawTransaction: '', getHashesPerSec: '',
decodeRawTransaction: '', getInfo: '',
dumpPrivKey: '', getMemoryPool: '',
encryptWallet: '', getMiningInfo: '',
getAccount: '', getNewAddress: '',
getAccountAddress: 'str', getPeerInfo: '',
getAddedNodeInfo: '', getRawMemPool: '',
getAddressesByAccount: '', getRawTransaction: 'str int',
getBalance: 'str int', getReceivedByAccount: 'str int',
getBestBlockHash: '', getReceivedByAddress: 'str int',
getBlock: '', getTransaction: '',
getBlockCount: '', getTxOut: 'str int bool',
getBlockHash: 'int', getTxOutSetInfo: '',
getBlockNumber: '', getWork: '',
getBlockTemplate: '', help: '',
getConnectionCount: '', importAddress: 'str str bool',
getDifficulty: '', importPrivKey: 'str str bool',
getGenerate: '', keyPoolRefill: '',
getHashesPerSec: '', listAccounts: 'int',
getInfo: '', listAddressGroupings: '',
getMemoryPool: '', listReceivedByAccount: 'int bool',
getMiningInfo: '', listReceivedByAddress: 'int bool',
getNewAddress: '', listSinceBlock: 'str int',
getPeerInfo: '', listTransactions: 'str int int',
getRawMemPool: '', listUnspent: 'int int',
getRawTransaction: 'str int', listLockUnspent: 'bool',
getReceivedByAccount: 'str int', lockUnspent: '',
getReceivedByAddress: 'str int', move: 'str str float int str',
getTransaction: '', sendFrom: 'str str float int str str',
getTxOut: 'str int bool', sendMany: 'str str int str', //not sure this is will work
getTxOutSetInfo: '', sendRawTransaction: '',
getWork: '', sendToAddress: 'str float str str',
help: '', setAccount: '',
importAddress: 'str str bool', setGenerate: 'bool int',
importPrivKey: 'str str bool', setTxFee: 'float',
keyPoolRefill: '', signMessage: '',
listAccounts: 'int', signRawTransaction: '',
listAddressGroupings: '', stop: '',
listReceivedByAccount: 'int bool', submitBlock: '',
listReceivedByAddress: 'int bool', validateAddress: '',
listSinceBlock: 'str int', verifyMessage: '',
listTransactions: 'str int int', walletLock: '',
listUnspent: 'int int', walletPassPhrase: 'string int',
listLockUnspent: 'bool', walletPassphraseChange: '',
lockUnspent: '', };
move: 'str str float int str',
sendFrom: 'str str float int str str',
sendMany: 'str str int str', //not sure this is will work
sendRawTransaction: '',
sendToAddress: 'str float str str',
setAccount: '',
setGenerate: 'bool int',
setTxFee: 'float',
signMessage: '',
signRawTransaction: '',
stop: '',
submitBlock: '',
validateAddress: '',
verifyMessage: '',
walletLock: '',
walletPassPhrase: 'string int',
walletPassphraseChange: '',
};
var slice = function(arr, start, end) { var slice = function(arr, start, end) {
return Array.prototype.slice.call(arr, start, end); return Array.prototype.slice.call(arr, start, end);
}; };
function generateRPCMethods(constructor, apiCalls, rpc) { function generateRPCMethods(constructor, apiCalls, rpc) {
function createRPCMethod(methodName, argMap) { function createRPCMethod(methodName, argMap) {
return function() { return function() {
var limit = arguments.length - 1; var limit = arguments.length - 1;
if(this.batchedCalls) var limit = arguments.length; if(this.batchedCalls) var limit = arguments.length;
for (var i=0; i<limit; i++) { for (var i=0; i<limit; i++) {
if(argMap[i]) arguments[i] = argMap[i](arguments[i]); if(argMap[i]) arguments[i] = argMap[i](arguments[i]);
};
if(this.batchedCalls) {
this.batchedCalls.push({jsonrpc: '2.0', method: methodName, params: slice(arguments)});
} else {
rpc.call(this, {method: methodName, params: slice(arguments, 0, arguments.length - 1)}, arguments[arguments.length - 1]);
}
}; };
if(this.batchedCalls) {
this.batchedCalls.push({jsonrpc: '2.0', method: methodName, params: slice(arguments)});
} else {
rpc.call(this, {method: methodName, params: slice(arguments, 0, arguments.length - 1)}, arguments[arguments.length - 1]);
}
}; };
};
var types = { var types = {
str: function(arg) {return arg.toString();}, str: function(arg) {return arg.toString();},
int: function(arg) {return parseFloat(arg);}, int: function(arg) {return parseFloat(arg);},
float: function(arg) {return parseFloat(arg);}, float: function(arg) {return parseFloat(arg);},
bool: function(arg) {return (arg === true || arg == '1' || arg == 'true' || arg.toString().toLowerCase() == 'true');}, bool: function(arg) {return (arg === true || arg == '1' || arg == 'true' || arg.toString().toLowerCase() == 'true');},
}; };
for(var k in apiCalls) { for(var k in apiCalls) {
if (apiCalls.hasOwnProperty(k)) { if (apiCalls.hasOwnProperty(k)) {
var spec = apiCalls[k].split(' '); var spec = apiCalls[k].split(' ');
for (var i = 0; i < spec.length; i++) { for (var i = 0; i < spec.length; i++) {
if(types[spec[i]]) { if(types[spec[i]]) {
spec[i] = types[spec[i]]; spec[i] = types[spec[i]];
} else { } else {
spec[i] = types.string; spec[i] = types.string;
}
} }
var methodName = k.toLowerCase();
constructor.prototype[k] = createRPCMethod(methodName, spec);
constructor.prototype[methodName] = constructor.prototype[k];
} }
var methodName = k.toLowerCase();
constructor.prototype[k] = createRPCMethod(methodName, spec);
constructor.prototype[methodName] = constructor.prototype[k];
} }
} }
}
function rpc(request, callback) { function rpc(request, callback) {
var self = this; var self = this;
var request; var request;
request = JSON.stringify(request); request = JSON.stringify(request);
var auth = Buffer(self.user + ':' + self.pass).toString('base64'); var auth = Buffer(self.user + ':' + self.pass).toString('base64');
var options = { var options = {
host: self.host, host: self.host,
path: '/', path: '/',
method: 'POST', method: 'POST',
port: self.port, port: self.port,
agent: self.disableAgent ? false : undefined, agent: self.disableAgent ? false : undefined,
}; };
if(self.httpOptions) { if(self.httpOptions) {
for(var k in self.httpOptions) { for(var k in self.httpOptions) {
options[k] = self.httpOptions[k]; options[k] = self.httpOptions[k];
}
} }
var err = null; }
var req = this.protocol.request(options, function(res) { var err = null;
var req = this.protocol.request(options, function(res) {
var buf = ''; var buf = '';
res.on('data', function(data) { res.on('data', function(data) {
buf += data; buf += data;
});
res.on('end', function() {
if(res.statusCode == 401) {
callback(new Error('bitcoin JSON-RPC connection rejected: 401 unauthorized'));
return;
}
if(res.statusCode == 403) {
callback(new Error('bitcoin JSON-RPC connection rejected: 403 forbidden'));
return;
}
if(err) {
callback(err);
return;
}
try {
var parsedBuf = JSON.parse(buf);
} catch(e) {
log.err(e.stack);
log.err(buf);
log.err('HTTP Status code:' + res.statusCode);
callback(e);
return;
}
callback(parsedBuf.error, parsedBuf);
});
}); });
req.on('error', function(e) { res.on('end', function() {
var err = new Error('Could not connect to bitcoin via RPC: '+e.message); if(res.statusCode == 401) {
log.err(err); callback(new Error('bitcoin JSON-RPC connection rejected: 401 unauthorized'));
callback(err); return;
}); }
if(res.statusCode == 403) {
req.setHeader('Content-Length', request.length); callback(new Error('bitcoin JSON-RPC connection rejected: 403 forbidden'));
req.setHeader('Content-Type', 'application/json'); return;
req.setHeader('Authorization', 'Basic ' + auth); }
req.write(request);
req.end();
};
generateRPCMethods(RpcClient, callspec, rpc); if(err) {
return RpcClient; callback(err);
return;
}
try {
var parsedBuf = JSON.parse(buf);
} catch(e) {
log.err(e.stack);
log.err(buf);
log.err('HTTP Status code:' + res.statusCode);
callback(e);
return;
}
callback(parsedBuf.error, parsedBuf);
});
});
req.on('error', function(e) {
var err = new Error('Could not connect to bitcoin via RPC: '+e.message);
log.err(err);
callback(err);
});
req.setHeader('Content-Length', request.length);
req.setHeader('Content-Type', 'application/json');
req.setHeader('Authorization', 'Basic ' + auth);
req.write(request);
req.end();
}; };
module.defineClass(ClassSpec);
generateRPCMethods(RpcClient, callspec, rpc);
module.exports = require('soop')(RpcClient);

103
SIN.js

@ -1,60 +1,55 @@
require('classtool'); var imports = require('soop').imports();
var parent = imports.parent || require('./util/VersionedData');
function ClassSpec(b) { function SIN(type, payload) {
var superclass = b.superclass || require('./util/VersionedData').class(); if (typeof type != 'number') {
SIN.super(this, arguments);
return;
function SIN(type, payload) {
if (typeof type != 'number') {
SIN.super(this, arguments);
return;
};
this.data = new Buffer(1 + 1 + payload.length);
this.__proto__ = this.encodings['binary'];
this.prefix(0x0F); // SIN magic number, in numberspace
this.type(type);
this.payload(payload);
};
SIN.superclass = superclass;
superclass.applyEncodingsTo(SIN);
SIN.SIN_PERSIST_MAINNET = 0x01; // associated with sacrifice TX
SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX
SIN.SIN_EPHEM = 0x02; // generate off-net at any time
// get or set the prefix data (the first byte of the address)
SIN.prototype.prefix = function(num) {
if(num || (num === 0)) {
this.doAsBinary(function() {this.data.writeUInt8(num, 0);});
return num;
}
return this.as('binary').readUInt8(0);
}; };
this.data = new Buffer(1 + 1 + payload.length);
this.__proto__ = this.encodings['binary'];
this.prefix(0x0F); // SIN magic number, in numberspace
this.type(type);
this.payload(payload);
};
SIN.parent = parent;
parent.applyEncodingsTo(SIN);
SIN.SIN_PERSIST_MAINNET = 0x01; // associated with sacrifice TX
SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX
SIN.SIN_EPHEM = 0x02; // generate off-net at any time
// get or set the prefix data (the first byte of the address)
SIN.prototype.prefix = function(num) {
if(num || (num === 0)) {
this.doAsBinary(function() {this.data.writeUInt8(num, 0);});
return num;
}
return this.as('binary').readUInt8(0);
};
// get or set the SIN-type data (the second byte of the address) // get or set the SIN-type data (the second byte of the address)
SIN.prototype.type = function(num) { SIN.prototype.type = function(num) {
if(num || (num === 0)) { if(num || (num === 0)) {
this.doAsBinary(function() {this.data.writeUInt8(num, 1);}); this.doAsBinary(function() {this.data.writeUInt8(num, 1);});
return num; return num;
} }
return this.as('binary').readUInt8(1); return this.as('binary').readUInt8(1);
}; };
// get or set the payload data (as a Buffer object) // get or set the payload data (as a Buffer object)
SIN.prototype.payload = function(data) { SIN.prototype.payload = function(data) {
if(data) { if(data) {
this.doAsBinary(function() {data.copy(this.data, 2);}); this.doAsBinary(function() {data.copy(this.data, 2);});
return data; return data;
} }
return this.as('binary').slice(1); return this.as('binary').slice(1);
}; };
SIN.prototype.validate = function() { SIN.prototype.validate = function() {
this.doAsBinary(function() { this.doAsBinary(function() {
SIN.super(this, 'validate', arguments); SIN.super(this, 'validate', arguments);
if (this.data.length != 22) throw new Error('invalid data length'); if (this.data.length != 22) throw new Error('invalid data length');
}); });
};
return SIN;
}; };
module.defineClass(ClassSpec); module.exports = require('soop')(SIN);

62
SINKey.js

@ -1,43 +1,37 @@
require('classtool'); var coinUtil = require('./util/util');
var timeUtil = require('./util/time');
var KeyModule = require('./Key');
var SIN = require('./SIN');
function ClassSpec(b) { function SINKey(cfg) {
var coinUtil = require('./util/util'); if (typeof cfg != 'object')
var timeUtil = require('./util/time'); cfg = {};
var KeyModule = require('./Key');
var SIN = require('./SIN').class();
function SINKey(cfg) { this.created = cfg.created;
if (typeof cfg != 'object') this.privKey = cfg.privKey;
cfg = {}; };
this.created = cfg.created;
this.privKey = cfg.privKey;
};
SINKey.prototype.generate = function() {
this.privKey = KeyModule.Key.generateSync();
this.created = timeUtil.curtime();
};
SINKey.prototype.pubkeyHash = function() { SINKey.prototype.generate = function() {
return coinUtil.sha256ripe160(this.privKey.public); this.privKey = KeyModule.Key.generateSync();
}; this.created = timeUtil.curtime();
};
SINKey.prototype.storeObj = function() { SINKey.prototype.pubkeyHash = function() {
var pubKey = this.privKey.public.toString('hex'); return coinUtil.sha256ripe160(this.privKey.public);
var pubKeyHash = this.pubkeyHash(); };
var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash);
var obj = {
created: this.created,
priv: this.privKey.private.toString('hex'),
pub: pubKey,
sin: sin.toString(),
};
return obj; SINKey.prototype.storeObj = function() {
var pubKey = this.privKey.public.toString('hex');
var pubKeyHash = this.pubkeyHash();
var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash);
var obj = {
created: this.created,
priv: this.privKey.private.toString('hex'),
pub: pubKey,
sin: sin.toString(),
}; };
return SINKey; return obj;
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(SINKey);

943
Script.js

@ -1,517 +1,512 @@
require('classtool'); var imports = require('soop').imports();
var config = imports.config || require('./config');
var log = imports.log || require('./util/log');
var Opcode = imports.Opcode || require('./Opcode');
var buffertools = imports.buffertools || require('buffertools');
// Make opcodes available as pseudo-constants
for (var i in Opcode.map) {
eval(i + " = " + Opcode.map[i] + ";");
}
var util = imports.util || require('./util/util');
var Parser = imports.Parser || require('./util/BinaryParser');
var Put = imports.Put || require('bufferput');
var TX_UNKNOWN = 0;
var TX_PUBKEY = 1;
var TX_PUBKEYHASH = 2;
var TX_MULTISIG = 3;
var TX_SCRIPTHASH = 4;
var TX_TYPES = [
'unknown',
'pubkey',
'pubkeyhash',
'multisig',
'scripthash'
];
function Script(buffer) {
if (buffer) {
this.buffer = buffer;
} else {
this.buffer = util.EMPTY_BUFFER;
}
this.chunks = [];
this.parse();
};
this.class = Script;
Script.TX_UNKNOWN = TX_UNKNOWN;
Script.TX_PUBKEY = TX_PUBKEY;
Script.TX_PUBKEYHASH = TX_PUBKEYHASH;
Script.TX_MULTISIG = TX_MULTISIG;
Script.TX_SCRIPTHASH = TX_SCRIPTHASH;
Script.prototype.parse = function() {
this.chunks = [];
var parser = new Parser(this.buffer);
while (!parser.eof()) {
var opcode = parser.word8();
var len;
if (opcode > 0 && opcode < OP_PUSHDATA1) {
// Read some bytes of data, opcode value is the length of data
this.chunks.push(parser.buffer(opcode));
} else if (opcode == OP_PUSHDATA1) {
len = parser.word8();
this.chunks.push(parser.buffer(len));
} else if (opcode == OP_PUSHDATA2) {
len = parser.word16le();
this.chunks.push(parser.buffer(len));
} else if (opcode == OP_PUSHDATA4) {
len = parser.word32le();
this.chunks.push(parser.buffer(len));
} else {
this.chunks.push(opcode);
}
}
};
function spec(b) { Script.prototype.isPushOnly = function() {
var config = b.config || require('./config'); for (var i = 0; i < this.chunks.length; i++)
var log = b.log || require('./util/log'); if (!Buffer.isBuffer(this.chunks[i]))
return false;
var Opcode = b.Opcode || require('./Opcode').class(); return true;
var buffertools = b.buffertools || require('buffertools'); };
// Make opcodes available as pseudo-constants Script.prototype.isP2SH = function() {
for (var i in Opcode.map) { return (this.chunks.length == 3 &&
eval(i + " = " + Opcode.map[i] + ";"); this.chunks[0] == OP_HASH160 &&
} Buffer.isBuffer(this.chunks[1]) &&
this.chunks[1].length == 20 &&
this.chunks[2] == OP_EQUAL);
};
var util = b.util || require('./util/util'); Script.prototype.isPubkey = function() {
var Parser = b.Parser || require('./util/BinaryParser').class(); return (this.chunks.length == 2 &&
var Put = b.Put || require('bufferput'); Buffer.isBuffer(this.chunks[0]) &&
this.chunks[1] == OP_CHECKSIG);
var TX_UNKNOWN = 0; };
var TX_PUBKEY = 1;
var TX_PUBKEYHASH = 2;
var TX_MULTISIG = 3;
var TX_SCRIPTHASH = 4;
var TX_TYPES = [
'unknown',
'pubkey',
'pubkeyhash',
'multisig',
'scripthash'
];
function Script(buffer) {
if (buffer) {
this.buffer = buffer;
} else {
this.buffer = util.EMPTY_BUFFER;
}
this.chunks = [];
this.parse();
};
this.class = Script;
Script.TX_UNKNOWN = TX_UNKNOWN;
Script.TX_PUBKEY = TX_PUBKEY;
Script.TX_PUBKEYHASH = TX_PUBKEYHASH;
Script.TX_MULTISIG = TX_MULTISIG;
Script.TX_SCRIPTHASH = TX_SCRIPTHASH;
Script.prototype.parse = function() {
this.chunks = [];
var parser = new Parser(this.buffer);
while (!parser.eof()) {
var opcode = parser.word8();
var len;
if (opcode > 0 && opcode < OP_PUSHDATA1) {
// Read some bytes of data, opcode value is the length of data
this.chunks.push(parser.buffer(opcode));
} else if (opcode == OP_PUSHDATA1) {
len = parser.word8();
this.chunks.push(parser.buffer(len));
} else if (opcode == OP_PUSHDATA2) {
len = parser.word16le();
this.chunks.push(parser.buffer(len));
} else if (opcode == OP_PUSHDATA4) {
len = parser.word32le();
this.chunks.push(parser.buffer(len));
} else {
this.chunks.push(opcode);
}
}
};
Script.prototype.isPushOnly = function() { Script.prototype.isPubkeyHash = function() {
for (var i = 0; i < this.chunks.length; i++) return (this.chunks.length == 5 &&
if (!Buffer.isBuffer(this.chunks[i])) this.chunks[0] == OP_DUP &&
return false; this.chunks[1] == OP_HASH160 &&
Buffer.isBuffer(this.chunks[2]) &&
this.chunks[2].length == 20 &&
this.chunks[3] == OP_EQUALVERIFY &&
this.chunks[4] == OP_CHECKSIG);
};
function isSmallIntOp(opcode) {
return ((opcode == OP_0) ||
((opcode >= OP_1) && (opcode <= OP_16)));
};
Script.prototype.isMultiSig = function() {
return (this.chunks.length > 3 &&
isSmallIntOp(this.chunks[0]) &&
isSmallIntOp(this.chunks[this.chunks.length - 2]) &&
this.chunks[this.chunks.length - 1] == OP_CHECKMULTISIG);
};
Script.prototype.finishedMultiSig = function() {
var nsigs = 0;
for (var i = 0; i < this.chunks.length - 1; i++)
if (this.chunks[i] !== 0)
nsigs++;
var serializedScript = this.chunks[this.chunks.length - 1];
var script = new Script(serializedScript);
var nreq = script.chunks[0] - 80; //see OP_2-OP_16
if (nsigs == nreq)
return true; return true;
}; else
return false;
Script.prototype.isP2SH = function() { }
return (this.chunks.length == 3 &&
this.chunks[0] == OP_HASH160 && Script.prototype.removePlaceHolders = function() {
Buffer.isBuffer(this.chunks[1]) && var chunks = [];
this.chunks[1].length == 20 && for (var i in this.chunks) {
this.chunks[2] == OP_EQUAL); if (this.chunks.hasOwnProperty(i)) {
}; var chunk = this.chunks[i];
if (chunk != 0)
Script.prototype.isPubkey = function() { chunks.push(chunk);
return (this.chunks.length == 2 && }
Buffer.isBuffer(this.chunks[0]) &&
this.chunks[1] == OP_CHECKSIG);
};
Script.prototype.isPubkeyHash = function() {
return (this.chunks.length == 5 &&
this.chunks[0] == OP_DUP &&
this.chunks[1] == OP_HASH160 &&
Buffer.isBuffer(this.chunks[2]) &&
this.chunks[2].length == 20 &&
this.chunks[3] == OP_EQUALVERIFY &&
this.chunks[4] == OP_CHECKSIG);
};
function isSmallIntOp(opcode) {
return ((opcode == OP_0) ||
((opcode >= OP_1) && (opcode <= OP_16)));
};
Script.prototype.isMultiSig = function() {
return (this.chunks.length > 3 &&
isSmallIntOp(this.chunks[0]) &&
isSmallIntOp(this.chunks[this.chunks.length - 2]) &&
this.chunks[this.chunks.length - 1] == OP_CHECKMULTISIG);
};
Script.prototype.finishedMultiSig = function() {
var nsigs = 0;
for (var i = 0; i < this.chunks.length - 1; i++)
if (this.chunks[i] !== 0)
nsigs++;
var serializedScript = this.chunks[this.chunks.length - 1];
var script = new Script(serializedScript);
var nreq = script.chunks[0] - 80; //see OP_2-OP_16
if (nsigs == nreq)
return true;
else
return false;
} }
this.chunks = chunks;
Script.prototype.removePlaceHolders = function() { this.updateBuffer();
var chunks = []; return this;
for (var i in this.chunks) { }
if (this.chunks.hasOwnProperty(i)) {
var chunk = this.chunks[i]; Script.prototype.prependOp0 = function() {
if (chunk != 0) var chunks = [0];
chunks.push(chunk); for (i in this.chunks) {
} if (this.chunks.hasOwnProperty(i)) {
chunks.push(this.chunks[i]);
} }
this.chunks = chunks;
this.updateBuffer();
return this;
} }
this.chunks = chunks;
this.updateBuffer();
return this;
}
// is this a script form we know?
Script.prototype.classify = function() {
if (this.isPubkeyHash())
return TX_PUBKEYHASH;
if (this.isP2SH())
return TX_SCRIPTHASH;
if (this.isMultiSig())
return TX_MULTISIG;
if (this.isPubkey())
return TX_PUBKEY;
return TX_UNKNOWN;
};
Script.prototype.prependOp0 = function() { // extract useful data items from known scripts
var chunks = [0]; Script.prototype.capture = function() {
for (i in this.chunks) { var txType = this.classify();
if (this.chunks.hasOwnProperty(i)) { var res = [];
chunks.push(this.chunks[i]); switch (txType) {
} case TX_PUBKEY:
} res.push(this.chunks[0]);
this.chunks = chunks; break;
this.updateBuffer(); case TX_PUBKEYHASH:
return this; res.push(this.chunks[2]);
break;
case TX_MULTISIG:
for (var i = 1; i < (this.chunks.length - 2); i++)
res.push(this.chunks[i]);
break;
case TX_SCRIPTHASH:
res.push(this.chunks[1]);
break;
case TX_UNKNOWN:
default:
// do nothing
break;
} }
// is this a script form we know? return res;
Script.prototype.classify = function() { };
if (this.isPubkeyHash())
return TX_PUBKEYHASH;
if (this.isP2SH())
return TX_SCRIPTHASH;
if (this.isMultiSig())
return TX_MULTISIG;
if (this.isPubkey())
return TX_PUBKEY;
return TX_UNKNOWN;
};
// extract useful data items from known scripts
Script.prototype.capture = function() {
var txType = this.classify();
var res = [];
switch (txType) {
case TX_PUBKEY:
res.push(this.chunks[0]);
break;
case TX_PUBKEYHASH:
res.push(this.chunks[2]);
break;
case TX_MULTISIG:
for (var i = 1; i < (this.chunks.length - 2); i++)
res.push(this.chunks[i]);
break;
case TX_SCRIPTHASH:
res.push(this.chunks[1]);
break;
case TX_UNKNOWN:
default:
// do nothing
break;
}
return res; // return first extracted data item from script
}; Script.prototype.captureOne = function() {
var arr = this.capture();
// return first extracted data item from script return arr[0];
Script.prototype.captureOne = function() { };
var arr = this.capture();
return arr[0];
};
Script.prototype.getOutType = function() {
var txType = this.classify();
switch (txType) {
case TX_PUBKEY:
return 'Pubkey';
case TX_PUBKEYHASH:
return 'Address';
default:
return 'Strange';
}
};
Script.prototype.getRawOutType = function() {
return TX_TYPES[this.classify()];
};
Script.prototype.simpleOutHash = function() {
switch (this.getOutType()) {
case 'Address':
return this.chunks[2];
case 'Pubkey':
return util.sha256ripe160(this.chunks[0]);
default:
log.debug("Encountered non-standard scriptPubKey");
log.debug("Strange script was: " + this.toString());
return null;
}
};
Script.prototype.getInType = function() { Script.prototype.getOutType = function() {
if (this.chunks.length == 1) { var txType = this.classify();
// Direct IP to IP transactions only have the public key in their scriptSig. switch (txType) {
case TX_PUBKEY:
return 'Pubkey'; return 'Pubkey';
} else if (this.chunks.length == 2 && case TX_PUBKEYHASH:
Buffer.isBuffer(this.chunks[0]) &&
Buffer.isBuffer(this.chunks[1])) {
return 'Address'; return 'Address';
} else { default:
return 'Strange'; return 'Strange';
} }
}; };
Script.prototype.simpleInPubKey = function() { Script.prototype.getRawOutType = function() {
switch (this.getInType()) { return TX_TYPES[this.classify()];
case 'Address': };
return this.chunks[1];
case 'Pubkey': Script.prototype.simpleOutHash = function() {
return null; switch (this.getOutType()) {
default: case 'Address':
log.debug("Encountered non-standard scriptSig"); return this.chunks[2];
log.debug("Strange script was: " + this.toString()); case 'Pubkey':
return null; return util.sha256ripe160(this.chunks[0]);
} default:
}; log.debug("Encountered non-standard scriptPubKey");
log.debug("Strange script was: " + this.toString());
Script.prototype.getBuffer = function() { return null;
return this.buffer; }
}; };
Script.fromStringContent = function(s) { Script.prototype.getInType = function() {
var chunks = []; if (this.chunks.length == 1) {
var split = s.split(' '); // Direct IP to IP transactions only have the public key in their scriptSig.
for (var i = 0; i < split.length; i++) { return 'Pubkey';
var word = split[i]; } else if (this.chunks.length == 2 &&
if (word.length > 2 && word.substring(0, 2) === '0x') { Buffer.isBuffer(this.chunks[0]) &&
chunks.push(new Buffer(word.substring(2, word.length), 'hex')); Buffer.isBuffer(this.chunks[1])) {
return 'Address';
} else {
return 'Strange';
}
};
Script.prototype.simpleInPubKey = function() {
switch (this.getInType()) {
case 'Address':
return this.chunks[1];
case 'Pubkey':
return null;
default:
log.debug("Encountered non-standard scriptSig");
log.debug("Strange script was: " + this.toString());
return null;
}
};
Script.prototype.getBuffer = function() {
return this.buffer;
};
Script.fromStringContent = function(s) {
var chunks = [];
var split = s.split(' ');
for (var i = 0; i < split.length; i++) {
var word = split[i];
if (word.length > 2 && word.substring(0, 2) === '0x') {
chunks.push(new Buffer(word.substring(2, word.length), 'hex'));
} else {
var opcode = Opcode.map['OP_' + word];
if (opcode) {
chunks.push(opcode);
} else { } else {
var opcode = Opcode.map['OP_' + word]; var integer = parseInt(word);
if (opcode) { if (!isNaN(integer)) {
chunks.push(opcode); //console.log(integer+' bits=\t'+integer.toString(2).replace('-','').length);
} else { var data = util.intToBuffer(integer);
var integer = parseInt(word); chunks.push(data);
if (!isNaN(integer)) {
//console.log(integer+' bits=\t'+integer.toString(2).replace('-','').length);
var data = util.intToBuffer(integer);
chunks.push(data);
}
} }
} }
} }
return Script.fromChunks(chunks); }
}; return Script.fromChunks(chunks);
};
Script.prototype.getStringContent = function(truncate, maxEl) {
if (truncate === null) {
truncate = true;
}
if ('undefined' === typeof maxEl) {
maxEl = 15;
}
Script.prototype.getStringContent = function(truncate, maxEl) { var s = '';
if (truncate === null) { for (var i = 0, l = this.chunks.length; i < l; i++) {
truncate = true; var chunk = this.chunks[i];
if (i > 0) {
s += ' ';
} }
if ('undefined' === typeof maxEl) { if (Buffer.isBuffer(chunk)) {
maxEl = 15; s += '0x' + util.formatBuffer(chunk, truncate ? null : 0);
} else {
s += Opcode.reverseMap[chunk];
} }
var s = ''; if (maxEl && i > maxEl) {
for (var i = 0, l = this.chunks.length; i < l; i++) { s += ' ...';
var chunk = this.chunks[i]; break;
}
}
return s;
};
if (i > 0) { Script.prototype.toString = function(truncate, maxEl) {
s += ' '; var script = "<Script ";
} script += this.getStringContent(truncate, maxEl);
script += ">";
return script;
};
if (Buffer.isBuffer(chunk)) {
s += '0x' + util.formatBuffer(chunk, truncate ? null : 0);
} else {
s += Opcode.reverseMap[chunk];
}
if (maxEl && i > maxEl) { Script.prototype.writeOp = function(opcode) {
s += ' ...'; var buf = Buffer(this.buffer.length + 1);
break; this.buffer.copy(buf);
buf.writeUInt8(opcode, this.buffer.length);
this.buffer = buf;
this.chunks.push(opcode);
};
Script.prototype.writeN = function(n) {
if (n < 0 || n > 16)
throw new Error("writeN: out of range value " + n);
if (n == 0)
this.writeOp(OP_0);
else
this.writeOp(OP_1 + n - 1);
};
function prefixSize(data_length) {
if (data_length < OP_PUSHDATA1) {
return 1;
} else if (data_length <= 0xff) {
return 1 + 1;
} else if (data_length <= 0xffff) {
return 1 + 2;
} else {
return 1 + 4;
}
};
function encodeLen(data_length) {
var buf = undefined;
if (data_length < OP_PUSHDATA1) {
buf = new Buffer(1);
buf.writeUInt8(data_length, 0);
} else if (data_length <= 0xff) {
buf = new Buffer(1 + 1);
buf.writeUInt8(OP_PUSHDATA1, 0);
buf.writeUInt8(data_length, 1);
} else if (data_length <= 0xffff) {
buf = new Buffer(1 + 2);
buf.writeUInt8(OP_PUSHDATA2, 0);
buf.writeUInt16LE(data_length, 1);
} else {
buf = new Buffer(1 + 4);
buf.writeUInt8(OP_PUSHDATA4, 0);
buf.writeUInt32LE(data_length, 1);
}
return buf;
};
Script.prototype.writeBytes = function(data) {
var newSize = this.buffer.length + prefixSize(data.length) + data.length;
this.buffer = Buffer.concat([this.buffer, encodeLen(data.length), data]);
this.chunks.push(data);
};
Script.prototype.updateBuffer = function() {
this.buffer = Script.chunksToBuffer(this.chunks);
};
Script.prototype.findAndDelete = function(chunk) {
var dirty = false;
if (Buffer.isBuffer(chunk)) {
for (var i = 0, l = this.chunks.length; i < l; i++) {
if (Buffer.isBuffer(this.chunks[i]) &&
buffertools.compare(this.chunks[i], chunk) === 0) {
this.chunks.splice(i, 1);
dirty = true;
} }
} }
return s; } else if ("number" === typeof chunk) {
}; for (var i = 0, l = this.chunks.length; i < l; i++) {
if (this.chunks[i] === chunk) {
Script.prototype.toString = function(truncate, maxEl) { this.chunks.splice(i, 1);
var script = "<Script "; dirty = true;
script += this.getStringContent(truncate, maxEl); }
script += ">";
return script;
};
Script.prototype.writeOp = function(opcode) {
var buf = Buffer(this.buffer.length + 1);
this.buffer.copy(buf);
buf.writeUInt8(opcode, this.buffer.length);
this.buffer = buf;
this.chunks.push(opcode);
};
Script.prototype.writeN = function(n) {
if (n < 0 || n > 16)
throw new Error("writeN: out of range value " + n);
if (n == 0)
this.writeOp(OP_0);
else
this.writeOp(OP_1 + n - 1);
};
function prefixSize(data_length) {
if (data_length < OP_PUSHDATA1) {
return 1;
} else if (data_length <= 0xff) {
return 1 + 1;
} else if (data_length <= 0xffff) {
return 1 + 2;
} else {
return 1 + 4;
}
};
function encodeLen(data_length) {
var buf = undefined;
if (data_length < OP_PUSHDATA1) {
buf = new Buffer(1);
buf.writeUInt8(data_length, 0);
} else if (data_length <= 0xff) {
buf = new Buffer(1 + 1);
buf.writeUInt8(OP_PUSHDATA1, 0);
buf.writeUInt8(data_length, 1);
} else if (data_length <= 0xffff) {
buf = new Buffer(1 + 2);
buf.writeUInt8(OP_PUSHDATA2, 0);
buf.writeUInt16LE(data_length, 1);
} else {
buf = new Buffer(1 + 4);
buf.writeUInt8(OP_PUSHDATA4, 0);
buf.writeUInt32LE(data_length, 1);
} }
} else {
throw new Error("Invalid chunk datatype.");
}
if (dirty) {
this.updateBuffer();
}
};
return buf; /**
}; * Creates a simple OP_CHECKSIG with pubkey output script.
*
* These are used for coinbase transactions and at some point were used for
* IP-based transactions as well.
*/
Script.createPubKeyOut = function(pubkey) {
var script = new Script();
script.writeBytes(pubkey);
script.writeOp(OP_CHECKSIG);
return script;
};
Script.prototype.writeBytes = function(data) { /**
var newSize = this.buffer.length + prefixSize(data.length) + data.length; * Creates a standard txout script.
this.buffer = Buffer.concat([this.buffer, encodeLen(data.length), data]); */
this.chunks.push(data); Script.createPubKeyHashOut = function(pubKeyHash) {
}; var script = new Script();
script.writeOp(OP_DUP);
script.writeOp(OP_HASH160);
script.writeBytes(pubKeyHash);
script.writeOp(OP_EQUALVERIFY);
script.writeOp(OP_CHECKSIG);
return script;
};
Script.prototype.updateBuffer = function() { Script.createMultisig = function(n_required, keys) {
this.buffer = Script.chunksToBuffer(this.chunks); var script = new Script();
}; script.writeN(n_required);
keys.forEach(function(key) {
script.writeBytes(key);
});
script.writeN(keys.length);
script.writeOp(OP_CHECKMULTISIG);
return script;
};
Script.prototype.findAndDelete = function(chunk) { Script.createP2SH = function(scriptHash) {
var dirty = false; var script = new Script();
if (Buffer.isBuffer(chunk)) { script.writeOp(OP_HASH160);
for (var i = 0, l = this.chunks.length; i < l; i++) { script.writeBytes(scriptHash);
if (Buffer.isBuffer(this.chunks[i]) && script.writeOp(OP_EQUAL);
buffertools.compare(this.chunks[i], chunk) === 0) { return script;
this.chunks.splice(i, 1); };
dirty = true;
} Script.fromTestData = function(testData) {
} testData = testData.map(function(chunk) {
} else if ("number" === typeof chunk) { if ("string" === typeof chunk) {
for (var i = 0, l = this.chunks.length; i < l; i++) { return new Buffer(chunk, 'hex');
if (this.chunks[i] === chunk) {
this.chunks.splice(i, 1);
dirty = true;
}
}
} else { } else {
throw new Error("Invalid chunk datatype."); return chunk;
} }
if (dirty) { });
this.updateBuffer();
} var script = new Script();
}; script.chunks = testData;
script.updateBuffer();
/** return script;
* Creates a simple OP_CHECKSIG with pubkey output script. };
*
* These are used for coinbase transactions and at some point were used for Script.fromChunks = function(chunks) {
* IP-based transactions as well. var script = new Script();
*/ script.chunks = chunks;
Script.createPubKeyOut = function(pubkey) { script.updateBuffer();
var script = new Script(); return script;
script.writeBytes(pubkey); };
script.writeOp(OP_CHECKSIG);
return script; Script.chunksToBuffer = function(chunks) {
}; var buf = new Put();
/** for (var i = 0, l = chunks.length; i < l; i++) {
* Creates a standard txout script. var data = chunks[i];
*/ if (Buffer.isBuffer(data)) {
Script.createPubKeyHashOut = function(pubKeyHash) { if (data.length < OP_PUSHDATA1) {
var script = new Script(); buf.word8(data.length);
script.writeOp(OP_DUP); } else if (data.length <= 0xff) {
script.writeOp(OP_HASH160); buf.word8(OP_PUSHDATA1);
script.writeBytes(pubKeyHash); buf.word8(data.length);
script.writeOp(OP_EQUALVERIFY); } else if (data.length <= 0xffff) {
script.writeOp(OP_CHECKSIG); buf.word8(OP_PUSHDATA2);
return script; buf.word16le(data.length);
};
Script.createMultisig = function(n_required, keys) {
var script = new Script();
script.writeN(n_required);
keys.forEach(function(key) {
script.writeBytes(key);
});
script.writeN(keys.length);
script.writeOp(OP_CHECKMULTISIG);
return script;
};
Script.createP2SH = function(scriptHash) {
var script = new Script();
script.writeOp(OP_HASH160);
script.writeBytes(scriptHash);
script.writeOp(OP_EQUAL);
return script;
};
Script.fromTestData = function(testData) {
testData = testData.map(function(chunk) {
if ("string" === typeof chunk) {
return new Buffer(chunk, 'hex');
} else {
return chunk;
}
});
var script = new Script();
script.chunks = testData;
script.updateBuffer();
return script;
};
Script.fromChunks = function(chunks) {
var script = new Script();
script.chunks = chunks;
script.updateBuffer();
return script;
};
Script.chunksToBuffer = function(chunks) {
var buf = new Put();
for (var i = 0, l = chunks.length; i < l; i++) {
var data = chunks[i];
if (Buffer.isBuffer(data)) {
if (data.length < OP_PUSHDATA1) {
buf.word8(data.length);
} else if (data.length <= 0xff) {
buf.word8(OP_PUSHDATA1);
buf.word8(data.length);
} else if (data.length <= 0xffff) {
buf.word8(OP_PUSHDATA2);
buf.word16le(data.length);
} else {
buf.word8(OP_PUSHDATA4);
buf.word32le(data.length);
}
buf.put(data);
} else if ("number" === typeof data) {
buf.word8(data);
} else { } else {
throw new Error("Script.chunksToBuffer(): Invalid chunk datatype"); buf.word8(OP_PUSHDATA4);
buf.word32le(data.length);
} }
buf.put(data);
} else if ("number" === typeof data) {
buf.word8(data);
} else {
throw new Error("Script.chunksToBuffer(): Invalid chunk datatype");
} }
return buf.buffer(); }
}; return buf.buffer();
return Script;
}; };
module.defineClass(spec);
module.exports = require('soop')(Script);

1921
ScriptInterpreter.js

File diff suppressed because it is too large

1401
Transaction.js

File diff suppressed because it is too large

274
Wallet.js

@ -1,142 +1,140 @@
require('classtool'); var imports = require('soop').imports();
var hex = function(hex) {return new Buffer(hex, 'hex');}; var hex = function(hex) {return new Buffer(hex, 'hex');};
function ClassSpec(b) { var fs = require('fs');
var fs = require('fs'); var EncFile = require('./util/EncFile');
var EncFile = require('./util/EncFile'); var Address = require('./Address');
var Address = require('./Address').class(); var networks = require('./networks');
var networks = require('./networks'); var util = imports.util || require('./util/util');
var util = b.util || require('./util/util'); var ENC_METHOD = 'aes-256-cbc';
var ENC_METHOD = 'aes-256-cbc';
var skeleton = {
var skeleton = { client: 'libcoin',
client: 'libcoin', client_version: '0.0.1',
client_version: '0.0.1', network: 'testnet',
network: 'testnet', version: 1,
version: 1, best_hash: null,
best_hash: null, best_height: -1,
best_height: -1, keys: [],
keys: [], sin: {},
sin: {}, scripts: {},
scripts: {}, };
};
function Wallet(cfg) {
function Wallet(cfg) { if (typeof cfg !== 'object')
if (typeof cfg !== 'object') cfg = {};
cfg = {};
// deep copy (no references)
// deep copy (no references) if (cfg.datastore)
if (cfg.datastore) this.datastore = JSON.parse(JSON.stringify(cfg.datastore));
this.datastore = JSON.parse(JSON.stringify(cfg.datastore)); else
else this.datastore = JSON.parse(JSON.stringify(skeleton));
this.datastore = JSON.parse(JSON.stringify(skeleton));
this.network = undefined;
this.network = undefined; this.dirty = cfg.dirty || true;
this.dirty = cfg.dirty || true; };
};
Wallet.prototype.readSync = function(filename, passphrase) {
Wallet.prototype.readSync = function(filename, passphrase) { this.datastore = EncFile.readJFileSync(ENC_METHOD,
this.datastore = EncFile.readJFileSync(ENC_METHOD, passphrase, filename);
passphrase, filename); this.dirty = false;
this.dirty = false; };
};
Wallet.prototype.writeSync = function(filename, passphrase) {
Wallet.prototype.writeSync = function(filename, passphrase) { var tmp_fn = filename + ".tmp";
var tmp_fn = filename + ".tmp";
EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn,
EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn, this.datastore);
this.datastore); fs.renameSync(tmp_fn, filename);
fs.renameSync(tmp_fn, filename);
this.dirty = false;
this.dirty = false; };
};
Wallet.prototype.setNetwork = function(netname) {
Wallet.prototype.setNetwork = function(netname) { if (!netname)
if (!netname) netname = this.datastore.network;
netname = this.datastore.network;
switch (netname) {
switch (netname) { case "mainnet":
case "mainnet": case "livenet":
case "livenet": this.network = networks.livenet;
this.network = networks.livenet; break;
break; case "testnet":
case "testnet": this.network = networks.testnet;
this.network = networks.testnet; break;
break; default:
default: throw new Error("Unsupported network");
throw new Error("Unsupported network"); }
}
// store+canonicalize name
// store+canonicalize name this.datastore['network'] = this.network.name;
this.datastore['network'] = this.network.name; this.dirty = true;
this.dirty = true; };
};
Wallet.prototype.addKey = function(wkey) {
Wallet.prototype.addKey = function(wkey) { this.datastore.keys.push(wkey);
this.datastore.keys.push(wkey); this.dirty = true;
this.dirty = true;
};
Wallet.prototype.addSIN = function(sinObj) {
this.datastore.sin[sinObj.sin] = sinObj;
this.dirty = true;
};
Wallet.prototype.findKeyHash = function(pubKeyHash) {
var pkhStr = pubKeyHash.toString();
for (var i = 0; i < this.datastore.keys.length; i++) {
var obj = this.datastore.keys[i];
var addrStr = obj.addr;
var addr = new Address(addrStr);
if (addr.payload().toString() == pkhStr)
return obj;
}
return undefined;
};
Wallet.prototype.expandKey = function(key) {
var addr = new Address(key);
var isAddr = true;
try {
addr.validate();
var b = addr.payload();
var obj = this.findKeyHash(b);
key = obj.pub;
} catch(e) {
// do nothing
}
var re = /^[a-fA-F0-9]+$/;
if (!key.match(re))
throw new Error("Unknown key type");
return hex(key);
};
Wallet.prototype.expandKeys = function(keys) {
var res = [];
var us = this;
keys.forEach(function(key) {
var expKey = us.expandKey(key);
res.push(expKey);
});
return res;
};
Wallet.prototype.addScript = function(script) {
var buf = script.getBuffer();
var hash = util.sha256ripe160(buf);
var addr = new Address(this.network.addressScript, hash);
var addrStr = addr.as('base58');
this.datastore.scripts[addrStr] = buf.toString('hex');
this.dirty = true;
return addrStr;
};
return Wallet;
}; };
module.defineClass(ClassSpec);
Wallet.prototype.addSIN = function(sinObj) {
this.datastore.sin[sinObj.sin] = sinObj;
this.dirty = true;
};
Wallet.prototype.findKeyHash = function(pubKeyHash) {
var pkhStr = pubKeyHash.toString();
for (var i = 0; i < this.datastore.keys.length; i++) {
var obj = this.datastore.keys[i];
var addrStr = obj.addr;
var addr = new Address(addrStr);
if (addr.payload().toString() == pkhStr)
return obj;
}
return undefined;
};
Wallet.prototype.expandKey = function(key) {
var addr = new Address(key);
var isAddr = true;
try {
addr.validate();
var b = addr.payload();
var obj = this.findKeyHash(b);
key = obj.pub;
} catch(e) {
// do nothing
}
var re = /^[a-fA-F0-9]+$/;
if (!key.match(re))
throw new Error("Unknown key type");
return hex(key);
};
Wallet.prototype.expandKeys = function(keys) {
var res = [];
var us = this;
keys.forEach(function(key) {
var expKey = us.expandKey(key);
res.push(expKey);
});
return res;
};
Wallet.prototype.addScript = function(script) {
var buf = script.getBuffer();
var hash = util.sha256ripe160(buf);
var addr = new Address(this.network.addressScript, hash);
var addrStr = addr.as('base58');
this.datastore.scripts[addrStr] = buf.toString('hex');
this.dirty = true;
return addrStr;
};
module.exports = require('soop')(Wallet);

91
WalletKey.js

@ -1,55 +1,52 @@
require('classtool'); var imports = require('soop').imports();
function ClassSpec(b) { var coinUtil = require('./util/util');
var coinUtil = require('./util/util'); var timeUtil = require('./util/time');
var timeUtil = require('./util/time'); var KeyModule = require('./Key');
var KeyModule = require('./Key'); var PrivateKey = require('./PrivateKey');
var PrivateKey = require('./PrivateKey').class(); var Address = require('./Address');
var Address = require('./Address').class();
function WalletKey(cfg) { function WalletKey(cfg) {
if (!cfg) cfg = {}; if (!cfg) cfg = {};
if (!cfg.network) throw new Error('network parameter is required'); if (!cfg.network) throw new Error('network parameter is required');
this.network = cfg.network; // required this.network = cfg.network; // required
this.created = cfg.created; this.created = cfg.created;
this.privKey = cfg.privKey; this.privKey = cfg.privKey;
}; };
WalletKey.prototype.generate = function() {
this.privKey = KeyModule.Key.generateSync();
this.created = timeUtil.curtime();
};
WalletKey.prototype.storeObj = function() { WalletKey.prototype.generate = function() {
var pubKey = this.privKey.public.toString('hex'); this.privKey = KeyModule.Key.generateSync();
var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public); this.created = timeUtil.curtime();
var addr = new Address(this.network.addressPubkey, pubKeyHash); };
var priv = new PrivateKey(this.network.keySecret, this.privKey.private, this.privKey.compressed);
var obj = {
created: this.created,
priv: priv.toString(),
pub: pubKey,
addr: addr.toString(),
};
return obj; WalletKey.prototype.storeObj = function() {
var pubKey = this.privKey.public.toString('hex');
var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public);
var addr = new Address(this.network.addressPubkey, pubKeyHash);
var priv = new PrivateKey(this.network.keySecret, this.privKey.private, this.privKey.compressed);
var obj = {
created: this.created,
priv: priv.toString(),
pub: pubKey,
addr: addr.toString(),
}; };
WalletKey.prototype.fromObj = function(obj) { return obj;
this.created = obj.created; };
this.privKey = new KeyModule.Key();
if (obj.priv.length==64) {
this.privKey.private = new Buffer(obj.priv,'hex');
this.privKey.compressed = true;
}
else {
var priv = new PrivateKey(obj.priv);
this.privKey.private = new Buffer(priv.payload());
this.privKey.compressed = priv.compressed();
}
this.privKey.regenerateSync();
};
return WalletKey; WalletKey.prototype.fromObj = function(obj) {
this.created = obj.created;
this.privKey = new KeyModule.Key();
if (obj.priv.length==64) {
this.privKey.private = new Buffer(obj.priv,'hex');
this.privKey.compressed = true;
}
else {
var priv = new PrivateKey(obj.priv);
this.privKey.private = new Buffer(priv.payload());
this.privKey.compressed = priv.compressed();
}
this.privKey.regenerateSync();
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(WalletKey);

14
bitcore.js

@ -3,25 +3,27 @@
*/ */
module.exports.bignum = require('bignum'); module.exports.bignum = require('bignum');
module.exports.base58 = require('base58-native'); module.exports.base58 = require('base58-native');
module.exports.buffertools = require('buffertools'); module.exports.buffertools = require('buffertools');
module.exports.config = require('./config'); module.exports.config = require('./config');
module.exports.const = require('./const'); module.exports.const = require('./const');
module.exports.Deserialize = require('./Deserialize'); module.exports.Deserialize = require('./Deserialize');
module.exports.log = require('./util/log'); module.exports.log = require('./util/log');
module.exports.networks = require('./networks'); module.exports.networks = require('./networks');
module.exports.util = require('./util/util'); module.exports.util = require('./util/util');
module.exports.EncodedData = require('./util/EncodedData'); module.exports.EncodedData = require('./util/EncodedData');
module.exports.VersionedData = require('./util/VersionedData'); module.exports.VersionedData = require('./util/VersionedData');
module.exports.Address = require('./Address'); module.exports.Address = require('./Address');
module.exports.Opcode = require('./Opcode'); module.exports.Opcode = require('./Opcode');
module.exports.Script = require('./Script'); module.exports.Script = require('./Script');
module.exports.Transaction = require('./Transaction'); module.exports.Transaction = require('./Transaction');
module.exports.Connection = require('./Connection');
module.exports.Peer = require('./Peer'); module.exports.Peer = require('./Peer');
module.exports.PeerManager = require('./PeerManager');
module.exports.Block = require('./Block'); module.exports.Block = require('./Block');
module.exports.Connection = require('./Connection');
module.exports.ScriptInterpreter = require('./ScriptInterpreter'); module.exports.ScriptInterpreter = require('./ScriptInterpreter');
module.exports.Bloom = require('./Bloom'); module.exports.Bloom = require('./Bloom');
module.exports.KeyModule = require('./Key'); module.exports.KeyModule = require('./Key');
@ -34,6 +36,14 @@ module.exports.WalletKey = require('./WalletKey');
module.exports.Buffer = Buffer; module.exports.Buffer = Buffer;
if (typeof process.versions === 'undefined') { if (typeof process.versions === 'undefined') {
// Browser specific
module.exports.bignum.config({EXPONENTIAL_AT: 9999999, DECIMAL_PLACES: 0, ROUNDING_MODE: 1}); module.exports.bignum.config({EXPONENTIAL_AT: 9999999, DECIMAL_PLACES: 0, ROUNDING_MODE: 1});
// module.exports.PeerManager = function () {
// throw new Error('PeerManager not availabe in browser Bitcore, under .bitcore. Use it with: require(\'PeerManager\');');
// };
}
else {
// Nodejs specific
module.exports.PeerManager = require('./PeerManager');
} }

6
browser/bignum_config.js

@ -0,0 +1,6 @@
require('bignum').config({
EXPONENTIAL_AT: 9999999,
DECIMAL_PLACES: 0,
ROUNDING_MODE: 1,
});

88
browserify.js

@ -0,0 +1,88 @@
'use strict';
/*
* Example for usage of browserify with soop
*
* The key parameter 'pack'
* The supplied 'custom_prelude.js' file is needed for
* .load function of soop.
*/
var fs = require('fs');
var browserify = require('browserify');
var browserPack = require('browser-pack');
var opts = {};
var preludePath = 'node_modules/soop/example/custom_prelude.js';
var pack = function (params) {
params.raw = true;
params.sourceMapPrefix = '//#';
params.prelude= fs.readFileSync(preludePath, 'utf8');
params.preludePath= preludePath;
return browserPack(params);
};
opts.pack = pack;
opts.debug = true;
var modules = [
'Address',
'Block',
'Bloom',
'Buffers.monkey',
'Connection',
'Deserialize',
'Gruntfile',
'Number.monkey',
'Opcode',
'Peer',
'PeerManager',
'PrivateKey',
'RpcClient',
'SIN',
'SINKey',
'Script',
'ScriptInterpreter',
'Sign',
'Transaction',
'Wallet',
'WalletKey',
'config',
'const',
'networks',
'bitcore',
];
var b = browserify(opts);
b.require('browserify-bignum/bignumber.js', {expose: 'bignum'} );
b.require('browserify-buffertools/buffertools.js', {expose:'buffertools'});
b.require('buffer', {expose: 'buffer'});
b.require('base58-native');
b.require('./Key.js', {expose: 'KeyModule'});
b.require('./util/log');
b.require('./util/util');
b.require('./util/EncodedData');
b.require('./util/VersionedData');
b.add('./browser/bignum_config.js');
modules.forEach(function(m) {
b.require('./' + m + '.js' ,{expose:m} );
});
var bopts = {
// detectGlobals: true,
// insertGlobals: 'Buffer',
// insertGlobalVars: {
// Buffer: function () {
// return 'require("buffer").Buffer';
// },
// },
};
b.bundle(bopts).pipe(process.stdout);

2
examples/Address.js

@ -2,7 +2,7 @@
// Replace '..' with 'bitcore' if you plan on using this code elsewhere. // Replace '..' with 'bitcore' if you plan on using this code elsewhere.
var Address = require('../Address').class(); var Address = require('../Address');
var addrStrings = [ var addrStrings = [
"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",

7
examples/PeerManager.js

@ -4,10 +4,9 @@
var util = require('util'); var util = require('util');
var networks = require('../networks'); var networks = require('../networks');
var Peer = require('../Peer').class(); var Peer = require('../Peer');
var PeerManager = require('../PeerManager').createClass({ var PeerManager = require('soop').load('../PeerManager',
network: networks.testnet {network: networks.testnet});
});
var handleBlock = function(info) { var handleBlock = function(info) {

2
examples/Rpc.js

@ -3,7 +3,7 @@
// Replace '..' with 'bitcore' if you plan on using this code elsewhere. // Replace '..' with 'bitcore' if you plan on using this code elsewhere.
var util = require('util'); var util = require('util');
var RpcClient = require('../RpcClient').class(); var RpcClient = require('../RpcClient');
var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4';
var config = { var config = {

13
examples/SendTx.js

@ -2,14 +2,13 @@
// Replace '..' with 'bitcore' if you plan on using this code elsewhere. // Replace '..' with 'bitcore' if you plan on using this code elsewhere.
var networks = require('../networks'); var networks = require('../networks');
var Peer = require('../Peer').class(); var Peer = require('../Peer');
var Transaction = require('../Transaction').class(); var Transaction = require('../Transaction');
var Address = require('../Address').class(); var Address = require('../Address');
var Script = require('../Script').class(); var Script = require('../Script');
var coinUtil = require('../util/util'); var coinUtil = require('../util/util');
var PeerManager = require('../PeerManager').createClass({ var PeerManager = require('soop').load('../PeerManager',
network: networks.testnet {network: networks.testnet});
});
var createTx = function() { var createTx = function() {

37
examples/example.html

@ -6,18 +6,19 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head> </head>
<body> <body>
<div id='content'></div> <pre>
<div id='content'></div>
</pre>
<script src="../browser/bundle.js"></script> <script src="../browser/bundle.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var Address = require('Address');
print = function(s){ print = function(s){
var div = document.getElementById('content'); var div = document.getElementById('content');
div.innerHTML += s + '<br />'; div.innerHTML += s + '<br />';
}; };
var Address = bitcore.Address.class();
var addrStrings = [ var addrStrings = [
"1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"1A1zP1eP5QGefi2DMPTfTL5SLmv7Dixxxx", "1A1zP1eP5QGefi2DMPTfTL5SLmv7Dixxxx",
@ -37,15 +38,33 @@
} }
}); });
print('<hr>');
var Key = require('KeyModule').Key;
var Key = bitcore.KeyModule.Key; var buffertools = require('buffertools');
var k = Key.generateSync(); var k = Key.generateSync();
print ('Generated Key Pair:'); print ('Generate Key Pair:');
print ('Private:' + bitcore.buffertools.toHex(k.private)); print ('Private:' + buffertools.toHex(k.private));
print ('Public:' + bitcore.buffertools.toHex(k.public)); print ('Public:' + buffertools.toHex(k.public));
print('<hr>');
/*
Using bitcore root module
*/
var bitcore = require('bitcore');
var k = bitcore.KeyModule.Key.generateSync();
print ('Generate Key Pair:');
print ('Private:' + buffertools.toHex(k.private));
print ('Public:' + buffertools.toHex(k.public));
print('<hr>');
console.log('[example.html.65:PeerManager:]'); //TODO
var pm = require('PeerManager');
</script> </script>
</body> </body>

5
package.json

@ -44,7 +44,7 @@
"test": "mocha test -R spec" "test": "mocha test -R spec"
}, },
"dependencies": { "dependencies": {
"classtool": "git://github.com/bitpay/classtool.git", "soop": "git://github.com/matiu/node-soop.git#feature/browser-support",
"base58-native": "=0.1.3", "base58-native": "=0.1.3",
"bindings": "=1.1.1", "bindings": "=1.1.1",
"bufferput": "git://github.com/bitpay/node-bufferput.git", "bufferput": "git://github.com/bitpay/node-bufferput.git",
@ -57,7 +57,8 @@
"devDependencies": { "devDependencies": {
"grunt-contrib-watch": "~0.5.3", "grunt-contrib-watch": "~0.5.3",
"grunt-mocha-test": "~0.8.2", "grunt-mocha-test": "~0.8.2",
"grunt-browserify": "~1.3.0", "grunt-shell": "~0.6.4",
"browser-pack": "*",
"grunt-markdown": "~0.5.0", "grunt-markdown": "~0.5.0",
"mocha": ">=1.15.1", "mocha": ">=1.15.1",
"browserify-bignum": "git://github.com/maraoz/browserify-bignum.git", "browserify-bignum": "git://github.com/maraoz/browserify-bignum.git",

9
test/index.html

@ -13,7 +13,14 @@
<script>mocha.setup('bdd')</script> <script>mocha.setup('bdd')</script>
<script src="../browser/bundle.js"></script> <script src="../browser/bundle.js"></script>
<script src="../browser/testdata.js"></script> <script src="../browser/testdata.js"></script>
<script src="adapter.js"></script>
<script>
var bitcore = require('bitcore');
this.Buffer = require('buffer').Buffer;
</script>
<!-- <script src="adapter.js"></script> -->
<script src="test.Address.js"></script> <script src="test.Address.js"></script>
<script src="test.basic.js"></script> <script src="test.basic.js"></script>

6
test/test.Address.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -13,7 +13,7 @@ describe('Address', function() {
should.exist(AddressModule); should.exist(AddressModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Address = AddressModule.class(); Address = AddressModule;
should.exist(Address); should.exist(Address);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

6
test/test.Block.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
var BlockModule = bitcore.Block; var BlockModule = bitcore.Block;
@ -12,7 +12,7 @@ describe('Block', function() {
should.exist(BlockModule); should.exist(BlockModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Block = BlockModule.class(); Block = BlockModule;
should.exist(Block); should.exist(Block);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

6
test/test.Bloom.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -13,7 +13,7 @@ describe('Bloom', function() {
should.exist(BloomModule); should.exist(BloomModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Bloom = BloomModule.class(); Bloom = BloomModule;
should.exist(Bloom); should.exist(Bloom);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

6
test/test.Connection.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -14,7 +14,7 @@ describe('Connection', function() {
should.exist(ConnectionModule); should.exist(ConnectionModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Connection = ConnectionModule.class(); Connection = ConnectionModule;
should.exist(Connection); should.exist(Connection);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

6
test/test.EncodedData.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -13,7 +13,7 @@ describe('EncodedData', function() {
should.exist(EncodedDataModule); should.exist(EncodedDataModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
EncodedData = EncodedDataModule.class(); EncodedData = EncodedDataModule;
should.exist(EncodedData); should.exist(EncodedData);
}); });
it('should be able to create an instance', function() { it('should be able to create an instance', function() {

4
test/test.Key.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var buffertools = require('buffertools'); var buffertools = require('buffertools');

6
test/test.Opcode.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -13,7 +13,7 @@ describe('Opcode', function() {
should.exist(OpcodeModule); should.exist(OpcodeModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Opcode = OpcodeModule.class(); Opcode = OpcodeModule;
should.exist(Opcode); should.exist(Opcode);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

6
test/test.Peer.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -13,7 +13,7 @@ describe('Peer', function() {
should.exist(PeerModule); should.exist(PeerModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Peer = PeerModule.class(); Peer = PeerModule;
should.exist(Peer); should.exist(Peer);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

9
test/test.PeerManager.js

@ -1,11 +1,12 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
var PeerManagerModule = bitcore.PeerManager; var PeerManagerModule = bitcore.PeerManager || require('PeerManager');
var PeerManager; var PeerManager;
describe('PeerManager', function() { describe('PeerManager', function() {
@ -13,7 +14,7 @@ describe('PeerManager', function() {
should.exist(PeerManagerModule); should.exist(PeerManagerModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
PeerManager = PeerManagerModule.class(); PeerManager = PeerManagerModule;
should.exist(PeerManager); should.exist(PeerManager);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

6
test/test.PrivateKey.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -15,7 +15,7 @@ describe('PrivateKey', function() {
should.exist(PrivateKeyModule); should.exist(PrivateKeyModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
PrivateKey = PrivateKeyModule.class(); PrivateKey = PrivateKeyModule;
should.exist(PrivateKey); should.exist(PrivateKey);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

6
test/test.RpcClient.js

@ -1,13 +1,13 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
var RpcClientModule = bitcore.RpcClient; var RpcClientModule = bitcore.RpcClient;
var RpcClient; var RpcClient;
RpcClient = RpcClientModule.class(); RpcClient = RpcClientModule;
describe('RpcClient', function() { describe('RpcClient', function() {
it('should initialze the main object', function() { it('should initialze the main object', function() {

6
test/test.SIN.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -13,7 +13,7 @@ describe('SIN', function() {
should.exist(SINModule); should.exist(SINModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
SIN = SINModule.class(); SIN = SINModule;
should.exist(SIN); should.exist(SIN);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

9
test/test.SINKey.js

@ -1,10 +1,7 @@
'use strict'; 'use strict';
var chai = chai || require('chai');
var bitcore = bitcore || require('../bitcore');
var chai = require('chai');
var bitcore = require('../bitcore');
var should = chai.should(); var should = chai.should();
var SINKeyModule = bitcore.SINKey; var SINKeyModule = bitcore.SINKey;
@ -16,7 +13,7 @@ describe('SINKey', function() {
should.exist(SINKeyModule); should.exist(SINKeyModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
SINKey = SINKeyModule.class(); SINKey = SINKeyModule;
should.exist(SINKey); should.exist(SINKey);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

8
test/test.Script.js

@ -1,12 +1,12 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
var ScriptModule = bitcore.Script; var ScriptModule = bitcore.Script;
var Address = bitcore.Address.class(); var Address = bitcore.Address;
var networks = bitcore.networks; var networks = bitcore.networks;
var Script; var Script;
var test_data = require('./testdata'); var test_data = require('./testdata');
@ -16,7 +16,7 @@ describe('Script', function() {
should.exist(ScriptModule); should.exist(ScriptModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Script = ScriptModule.class(); Script = ScriptModule;
should.exist(Script); should.exist(Script);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

6
test/test.ScriptInterpreter.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -13,7 +13,7 @@ describe('ScriptInterpreter', function() {
should.exist(ScriptInterpreterModule); should.exist(ScriptInterpreterModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
ScriptInterpreter = ScriptInterpreterModule.class(); ScriptInterpreter = ScriptInterpreterModule;
should.exist(ScriptInterpreter); should.exist(ScriptInterpreter);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

8
test/test.Transaction.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -9,7 +9,7 @@ var TransactionModule = bitcore.Transaction;
var Transaction; var Transaction;
var In; var In;
var Out; var Out;
var Script = bitcore.Script.class(); var Script = bitcore.Script;
var buffertools = require('buffertools'); var buffertools = require('buffertools');
var test_data = require('./testdata'); var test_data = require('./testdata');
@ -18,7 +18,7 @@ describe('Transaction', function() {
should.exist(TransactionModule); should.exist(TransactionModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Transaction = TransactionModule.class(); Transaction = TransactionModule;
should.exist(Transaction); should.exist(Transaction);
In = Transaction.In; In = Transaction.In;
Out = Transaction.Out; Out = Transaction.Out;

6
test/test.VersionedData.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -13,7 +13,7 @@ describe('VersionedData', function() {
should.exist(VersionedDataModule); should.exist(VersionedDataModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
VersionedData = VersionedDataModule.class(); VersionedData = VersionedDataModule;
should.exist(VersionedData); should.exist(VersionedData);
}); });
it('should be able to create an instance', function() { it('should be able to create an instance', function() {

6
test/test.Wallet.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -13,7 +13,7 @@ describe('Wallet', function() {
should.exist(WalletModule); should.exist(WalletModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
Wallet = WalletModule.class(); Wallet = WalletModule;
should.exist(Wallet); should.exist(Wallet);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

6
test/test.WalletKey.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();
@ -14,7 +14,7 @@ describe('WalletKey', function() {
should.exist(WalletKeyModule); should.exist(WalletKeyModule);
}); });
it('should be able to create class', function() { it('should be able to create class', function() {
WalletKey = WalletKeyModule.class(); WalletKey = WalletKeyModule;
should.exist(WalletKey); should.exist(WalletKey);
}); });
it('should be able to create instance', function() { it('should be able to create instance', function() {

22
test/test.basic.js

@ -1,15 +1,27 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var test_data;
if (typeof dataValid !== 'undefined' ) {
test_data = {
dataValid: dataValid,
dataInvalid: dataInvalid,
};
}
else {
test_data = require('./testdata');
}
var should = chai.should(); var should = chai.should();
var Address = bitcore.Address.class(); var Address = bitcore.Address;
var PrivateKey = bitcore.PrivateKey.class(); var PrivateKey = bitcore.PrivateKey;
var networks = bitcore.networks; var networks = bitcore.networks;
var KeyModule = bitcore.KeyModule; var KeyModule = bitcore.KeyModule;
var test_data = require('./testdata');
function test_encode_priv(b58, payload, isTestnet, isCompressed) { function test_encode_priv(b58, payload, isTestnet, isCompressed) {

4
test/test.main.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var expect = chai.expect; var expect = chai.expect;
var should = chai.should(); var should = chai.should();

4
test/test.misc.js

@ -1,7 +1,7 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = chai || require('chai');
var bitcore = require('../bitcore'); var bitcore = bitcore || require('../bitcore');
var should = chai.should(); var should = chai.should();

5
test/test.util.js

@ -1,5 +1,6 @@
var chai = require('chai');
var bitcore = require('../bitcore'); var chai = chai || require('chai');
var bitcore = bitcore || require('../bitcore');
var coinUtil = bitcore.util; var coinUtil = bitcore.util;
var should = chai.should(); var should = chai.should();
var buffertools = require('buffertools'); var buffertools = require('buffertools');

2
test/testdata.js

@ -1,5 +1,3 @@
var fs = require('fs'); var fs = require('fs');
var dataValid = JSON.parse(fs.readFileSync('test/data/base58_keys_valid.json')); var dataValid = JSON.parse(fs.readFileSync('test/data/base58_keys_valid.json'));

252
util/BinaryParser.js

@ -2,147 +2,145 @@
* Simple synchronous parser based on node-binary. * Simple synchronous parser based on node-binary.
*/ */
function spec(b) { var imports = require('soop').imports();
function Parser(buffer) function Parser(buffer)
{ {
this.subject = buffer; this.subject = buffer;
this.pos = 0; this.pos = 0;
}; };
Parser.prototype.buffer = function buffer(len) { Parser.prototype.buffer = function buffer(len) {
var buf = this.subject.slice(this.pos, this.pos+len); var buf = this.subject.slice(this.pos, this.pos+len);
this.pos += len; this.pos += len;
return buf; return buf;
}; };
Parser.prototype.search = function search(needle) { Parser.prototype.search = function search(needle) {
var len; var len;
if ("string" === typeof needle || Buffer.isBuffer(needle)) { if ("string" === typeof needle || Buffer.isBuffer(needle)) {
// TODO: Slicing is probably too slow // TODO: Slicing is probably too slow
len = this.subject.slice(this.pos).indexOf(needle); len = this.subject.slice(this.pos).indexOf(needle);
if (len !== -1) { if (len !== -1) {
this.pos += len + needle.length; this.pos += len + needle.length;
}
return len;
} }
if ("number" === typeof needle) { return len;
needle = needle & 0xff; }
// Search for single byte if ("number" === typeof needle) {
for (var i = this.pos, l = this.subject.length; i < l; i++) { needle = needle & 0xff;
if (this.subject[i] == needle) { // Search for single byte
len = i - this.pos; for (var i = this.pos, l = this.subject.length; i < l; i++) {
this.pos = i+1; if (this.subject[i] == needle) {
return len; len = i - this.pos;
} this.pos = i+1;
return len;
} }
return -1;
} }
}; return -1;
}
};
/** /**
* Like search(), but returns the skipped bytes * Like search(), but returns the skipped bytes
*/ */
Parser.prototype.scan = function scan(needle) { Parser.prototype.scan = function scan(needle) {
var startPos = this.pos; var startPos = this.pos;
var len = this.search(needle); var len = this.search(needle);
if (len !== -1) { if (len !== -1) {
return this.subject.slice(startPos, startPos+len); return this.subject.slice(startPos, startPos+len);
} else { } else {
throw new Error('No match'); throw new Error('No match');
} }
}; };
Parser.prototype.eof = function eof() { Parser.prototype.eof = function eof() {
return this.pos >= this.subject.length; return this.pos >= this.subject.length;
}; };
// convert byte strings to unsigned little endian numbers // convert byte strings to unsigned little endian numbers
function decodeLEu (bytes) { function decodeLEu (bytes) {
var acc = 0; var acc = 0;
for (var i = 0; i < bytes.length; i++) { for (var i = 0; i < bytes.length; i++) {
acc += Math.pow(256,i) * bytes[i]; acc += Math.pow(256,i) * bytes[i];
}
return acc;
} }
return acc;
// convert byte strings to unsigned big endian numbers }
function decodeBEu (bytes) {
var acc = 0; // convert byte strings to unsigned big endian numbers
for (var i = 0; i < bytes.length; i++) { function decodeBEu (bytes) {
acc += Math.pow(256, bytes.length - i - 1) * bytes[i]; var acc = 0;
} for (var i = 0; i < bytes.length; i++) {
return acc; acc += Math.pow(256, bytes.length - i - 1) * bytes[i];
} }
return acc;
// convert byte strings to signed big endian numbers }
function decodeBEs (bytes) {
var val = decodeBEu(bytes); // convert byte strings to signed big endian numbers
if ((bytes[0] & 0x80) == 0x80) { function decodeBEs (bytes) {
val -= Math.pow(256, bytes.length); var val = decodeBEu(bytes);
} if ((bytes[0] & 0x80) == 0x80) {
return val; val -= Math.pow(256, bytes.length);
} }
return val;
// convert byte strings to signed little endian numbers }
function decodeLEs (bytes) {
var val = decodeLEu(bytes); // convert byte strings to signed little endian numbers
if ((bytes[bytes.length - 1] & 0x80) == 0x80) { function decodeLEs (bytes) {
val -= Math.pow(256, bytes.length); var val = decodeLEu(bytes);
} if ((bytes[bytes.length - 1] & 0x80) == 0x80) {
return val; val -= Math.pow(256, bytes.length);
} }
return val;
}
function getDecoder(len, fn) { function getDecoder(len, fn) {
return function () { return function () {
var buf = this.buffer(len); var buf = this.buffer(len);
return fn(buf); return fn(buf);
};
};
[ 1, 2, 4, 8 ].forEach(function (bytes) {
var bits = bytes * 8;
Parser.prototype['word' + bits + 'le']
= Parser.prototype['word' + bits + 'lu']
= getDecoder(bytes, decodeLEu);
Parser.prototype['word' + bits + 'ls']
= getDecoder(bytes, decodeLEs);
Parser.prototype['word' + bits + 'be']
= Parser.prototype['word' + bits + 'bu']
= getDecoder(bytes, decodeBEu);
Parser.prototype['word' + bits + 'bs']
= getDecoder(bytes, decodeBEs);
Parser.prototype.word8 = Parser.prototype.word8u = Parser.prototype.word8be;
Parser.prototype.word8s = Parser.prototype.word8bs;
});
Parser.prototype.varInt = function ()
{
var firstByte = this.word8();
switch (firstByte) {
case 0xFD:
return this.word16le();
case 0xFE:
return this.word32le();
case 0xFF:
return this.word64le();
default:
return firstByte;
}
};
Parser.prototype.varStr = function () {
var len = this.varInt();
return this.buffer(len);
}; };
};
[ 1, 2, 4, 8 ].forEach(function (bytes) {
var bits = bytes * 8;
Parser.prototype['word' + bits + 'le']
= Parser.prototype['word' + bits + 'lu']
= getDecoder(bytes, decodeLEu);
Parser.prototype['word' + bits + 'ls']
= getDecoder(bytes, decodeLEs);
Parser.prototype['word' + bits + 'be']
= Parser.prototype['word' + bits + 'bu']
= getDecoder(bytes, decodeBEu);
Parser.prototype['word' + bits + 'bs']
= getDecoder(bytes, decodeBEs);
Parser.prototype.word8 = Parser.prototype.word8u = Parser.prototype.word8be;
Parser.prototype.word8s = Parser.prototype.word8bs;
});
Parser.prototype.varInt = function ()
{
var firstByte = this.word8();
switch (firstByte) {
case 0xFD:
return this.word16le();
case 0xFE:
return this.word32le();
case 0xFF:
return this.word64le();
default:
return firstByte;
}
};
return Parser; Parser.prototype.varStr = function () {
var len = this.varInt();
return this.buffer(len);
}; };
module.defineClass(spec);
module.exports = require('soop')(Parser);

285
util/EncodedData.js

@ -1,159 +1,158 @@
require('classtool'); var imports = require('soop').imports();
var base58 = imports.base58 || require('base58-native').base58Check;
function ClassSpec(b) {
var base58 = b.base58 || require('base58-native').base58Check;
// Constructor. Takes the following forms:
// Constructor. Takes the following forms: // new EncodedData(<base58_address_string>)
// new EncodedData(<base58_address_string>) // new EncodedData(<binary_buffer>)
// new EncodedData(<binary_buffer>) // new EncodedData(<data>, <encoding>)
// new EncodedData(<data>, <encoding>) // new EncodedData(<version>, <20-byte-hash>)
// new EncodedData(<version>, <20-byte-hash>) function EncodedData(data, encoding) {
function EncodedData(data, encoding) { this.data = data;
this.data = data; if(!encoding && (typeof data == 'string')) {
if(!encoding && (typeof data == 'string')) { this.__proto__ = this.encodings['base58'];
this.__proto__ = this.encodings['base58']; } else {
} else { this.__proto__ = this.encodings[encoding || 'binary'];
this.__proto__ = this.encodings[encoding || 'binary']; }
} };
};
// get or set the encoding used (transforms data) // get or set the encoding used (transforms data)
EncodedData.prototype.encoding = function(encoding) { EncodedData.prototype.encoding = function(encoding) {
if(encoding && (encoding != this._encoding)) { if(encoding && (encoding != this._encoding)) {
this.data = this.as(encoding); this.data = this.as(encoding);
this.__proto__ = this.encodings[encoding]; this.__proto__ = this.encodings[encoding];
} }
return this._encoding; return this._encoding;
}; };
// answer a new instance having the given encoding // answer a new instance having the given encoding
EncodedData.prototype.withEncoding = function(encoding) { EncodedData.prototype.withEncoding = function(encoding) {
return new EncodedData(this.as(encoding), encoding); return new EncodedData(this.as(encoding), encoding);
}; };
// answer the data in the given encoding // answer the data in the given encoding
EncodedData.prototype.as = function(encoding) { EncodedData.prototype.as = function(encoding) {
if(!encodings[encoding]) throw new Error('invalid encoding'); if(!encodings[encoding]) throw new Error('invalid encoding');
return this.converters[encoding].call(this); return this.converters[encoding].call(this);
}; };
// validate that we can convert to binary // validate that we can convert to binary
EncodedData.prototype._validate = function() { EncodedData.prototype._validate = function() {
this.withEncoding('binary'); this.withEncoding('binary');
}; };
// Boolean protocol for testing if valid // Boolean protocol for testing if valid
EncodedData.prototype.isValid = function() { EncodedData.prototype.isValid = function() {
try { try {
this.validate(); this.validate();
return true; return true;
} catch(e) { } catch(e) {
return false; return false;
} }
}; };
// subclasses can override to do more stuff // subclasses can override to do more stuff
EncodedData.prototype.validate = function() { EncodedData.prototype.validate = function() {
this._validate(); this._validate();
}; };
// Boolean protocol for testing if valid // Boolean protocol for testing if valid
EncodedData.prototype.isValid = function() { EncodedData.prototype.isValid = function() {
try { try {
this.validate(); this.validate();
return true; return true;
} catch(e) { } catch(e) {
return false; return false;
} }
}; };
// convert to a string (in base58 form) // convert to a string (in base58 form)
EncodedData.prototype.toString = function() { EncodedData.prototype.toString = function() {
return this.as('base58'); return this.as('base58');
}; };
// utility // utility
EncodedData.prototype.doAsBinary = function(callback) { EncodedData.prototype.doAsBinary = function(callback) {
var oldEncoding = this.encoding(); var oldEncoding = this.encoding();
this.encoding('binary'); this.encoding('binary');
callback.apply(this); callback.apply(this);
this.encoding(oldEncoding); this.encoding(oldEncoding);
}; };
// Setup support for various address encodings. The object for // Setup support for various address encodings. The object for
// each encoding inherits from the EncodedData prototype. This // each encoding inherits from the EncodedData prototype. This
// allows any encoding to override any method...changing the encoding // allows any encoding to override any method...changing the encoding
// for an instance will change the encoding it inherits from. Note, // for an instance will change the encoding it inherits from. Note,
// this will present some problems for anyone wanting to inherit from // this will present some problems for anyone wanting to inherit from
// EncodedData (we'll deal with that when needed). // EncodedData (we'll deal with that when needed).
var encodings = { var encodings = {
'binary': { 'binary': {
converters: { converters: {
'binary': function() { 'binary': function() {
var answer = new Buffer(this.data.length); var answer = new Buffer(this.data.length);
this.data.copy(answer); this.data.copy(answer);
return answer; return answer;
},
'base58': function() {
return base58.encode(this.data);
},
'hex': function() {
return this.data.toString('hex');
},
}, },
'base58': function() {
_validate: function() { return base58.encode(this.data);
//nothing to do here...we make no assumptions about the data },
'hex': function() {
return this.data.toString('hex');
}, },
}, },
'base58': { _validate: function() {
converters: { //nothing to do here...we make no assumptions about the data
'binary': function() { },
return base58.decode(this.data); },
},
'hex': function() { 'base58': {
return this.withEncoding('binary').as('hex'); converters: {
}, 'binary': function() {
return base58.decode(this.data);
},
'hex': function() {
return this.withEncoding('binary').as('hex');
}, },
}, },
},
'hex': { 'hex': {
converters: { converters: {
'binary': function() { 'binary': function() {
return new Buffer(this.data, 'hex'); return new Buffer(this.data, 'hex');
}, },
'base58': function() { 'base58': function() {
return this.withEncoding('binary').as('base58'); return this.withEncoding('binary').as('base58');
},
}, },
}, },
}; },
};
var no_conversion = function() {return this.data;};
for(var k in encodings) {
if(encodings.hasOwnProperty(k)){
if(!encodings[k].converters[k])
encodings[k].converters[k] = no_conversion;
encodings[k]._encoding = k;
}
}
var no_conversion = function() {return this.data;}; EncodedData.applyEncodingsTo = function(aClass) {
var tmp = {};
for(var k in encodings) { for(var k in encodings) {
if(encodings.hasOwnProperty(k)){ var enc = encodings[k];
if(!encodings[k].converters[k]) var obj = {};
encodings[k].converters[k] = no_conversion; for(var j in enc) {
encodings[k]._encoding = k; obj[j] = enc[j];
} }
obj.__proto__ = aClass.prototype;
tmp[k] = obj;
} }
aClass.prototype.encodings = tmp;
};
EncodedData.applyEncodingsTo = function(aClass) { EncodedData.applyEncodingsTo(EncodedData);
var tmp = {};
for(var k in encodings) { module.exports = require('soop')(EncodedData);
var enc = encodings[k];
var obj = {};
for(var j in enc) {
obj[j] = enc[j];
}
obj.__proto__ = aClass.prototype;
tmp[k] = obj;
}
aClass.prototype.encodings = tmp;
};
EncodedData.applyEncodingsTo(EncodedData);
return EncodedData;
}
module.defineClass(ClassSpec);

63
util/VersionedData.js

@ -1,39 +1,38 @@
require('classtool'); var imports = require('soop').imports();
var base58 = imports.base58 || require('base58-native').base58Check;
var parent = imports.parent || require('./EncodedData');
function ClassSpec(b) {
var superclass = b.superclass || require('./EncodedData').class();
function VersionedData(version, payload) { function VersionedData(version, payload) {
if(typeof version != 'number') { if(typeof version != 'number') {
VersionedData.super(this, arguments); VersionedData.super(this, arguments);
return; return;
};
this.data = new Buffer(payload.length + 1);
this.__proto__ = this.encodings['binary'];
this.version(version);
this.payload(payload);
}; };
VersionedData.superclass = superclass; this.data = new Buffer(payload.length + 1);
superclass.applyEncodingsTo(VersionedData); this.__proto__ = this.encodings['binary'];
this.version(version);
this.payload(payload);
};
// get or set the version data (the first byte of the address) VersionedData.parent = parent;
VersionedData.prototype.version = function(num) { parent.applyEncodingsTo(VersionedData);
if(num || (num === 0)) {
this.doAsBinary(function() {this.data.writeUInt8(num, 0);});
return num;
}
return this.as('binary').readUInt8(0);
};
// get or set the payload data (as a Buffer object) // get or set the version data (the first byte of the address)
VersionedData.prototype.payload = function(data) { VersionedData.prototype.version = function(num) {
if(data) { if(num || (num === 0)) {
this.doAsBinary(function() {data.copy(this.data,1);}); this.doAsBinary(function() {this.data.writeUInt8(num, 0);});
return data; return num;
} }
return this.as('binary').slice(1); return this.as('binary').readUInt8(0);
}; };
return VersionedData; // get or set the payload data (as a Buffer object)
VersionedData.prototype.payload = function(data) {
if(data) {
this.doAsBinary(function() {data.copy(this.data,1);});
return data;
}
return this.as('binary').slice(1);
}; };
module.defineClass(ClassSpec);
module.exports = require('soop')(VersionedData);

Loading…
Cancel
Save