all classes working with soop and test passing

This commit is contained in:
Matias Alejo Garcia 2014-03-05 16:11:16 -03:00
parent fa1e323d39
commit c0c325dabd
38 changed files with 4700 additions and 4763 deletions

View File

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

1117
Block.js

File diff suppressed because it is too large Load Diff

221
Bloom.js
View File

@ -1,116 +1,111 @@
require('classtool');
var MAX_BLOOM_FILTER_SIZE = 36000; // bytes
var MAX_HASH_FUNCS = 50;
var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
var LN2 = 0.6931471805599453094172321214581765680755001343602552;
var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80];
function ClassSpec(b) {
var MAX_BLOOM_FILTER_SIZE = 36000; // bytes
var MAX_HASH_FUNCS = 50;
var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
var LN2 = 0.6931471805599453094172321214581765680755001343602552;
var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80];
function Bloom() {
this.data = '';
this.hashFuncs = 0;
};
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)) |
(data[idx + 1] << (1 * 8)) |
(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;
var c2 = 0x1b873593;
var nBlocks = data.length / 4;
// data body
for (var i = -nBlocks; i; i++) {
var k1 = getBlockU32(i);
k1 *= c1;
k1 = ROTLF32(k1, 15);
k1 *= c2;
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) {
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);
};
return Bloom;
function Bloom() {
this.data = '';
this.hashFuncs = 0;
};
module.defineClass(ClassSpec);
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)) |
(data[idx + 1] << (1 * 8)) |
(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;
var c2 = 0x1b873593;
var nBlocks = data.length / 4;
// data body
for (var i = -nBlocks; i; i++) {
var k1 = getBlockU32(i);
k1 *= c1;
k1 = ROTLF32(k1, 15);
k1 *= c2;
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) {
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);

File diff suppressed because it is too large Load Diff

315
Opcode.js
View File

@ -1,161 +1,158 @@
require('classtool');
var imports = require('soop').imports();
function spec(b) {
function Opcode(num) {
this.code = num;
};
Opcode.prototype.toString = function () {
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;
function Opcode(num) {
this.code = num;
};
module.defineClass(spec);
Opcode.prototype.toString = function () {
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);
}
}
module.exports = require('soop')(Opcode);

109
Peer.js
View File

@ -1,61 +1,58 @@
require('classtool');
var imports = require('soop').imports();
function spec(b) {
var Net = b.Net || require('net');
var Binary = b.Binary || require('binary');
var buffertools = b.buffertools || require('buffertools');
var Net = imports.Net || require('net');
var Binary = imports.Binary || require('binary');
var buffertools = imports.buffertools || require('buffertools');
function Peer(host, port, services) {
if ("string" === typeof host) {
if (host.indexOf(':') && !port) {
var parts = host.split(':');
host = parts[0];
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);
function Peer(host, port, services) {
if ("string" === typeof host) {
if (host.indexOf(':') && !port) {
var parts = host.split(':');
host = parts[0];
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.services = (services) ? services : null;
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 () {
return new Buffer(this.host.split('.'));
};
Peer.prototype.toString = function () {
return this.host + ":" + this.port;
};
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();
};
return Peer;
this.services = (services) ? services : null;
this.lastSeen = 0;
};
module.defineClass(spec);
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 () {
return new Buffer(this.host.split('.'));
};
Peer.prototype.toString = function () {
return this.host + ":" + this.port;
};
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.exports = require('soop')(Peer);

View File

@ -1,215 +1,212 @@
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() {};
var imports = require('soop').imports();
var config = imports.config || require('./config');
var log = imports.log || require('./util/log');
var network = imports.network || require('./networks')[config.network];
var Connection = imports.Connection ||
require('soop').load('./Connection', {config: config, network: network});
GetAdjustedTime = b.GetAdjustedTime || function () {
// TODO: Implement actual adjustment
return Math.floor(new Date().getTime() / 1000);
};
var Peer = imports.Peer || require('./Peer');
function PeerManager() {
this.active = false;
this.timer = null;
this.peers = [];
this.connections = [];
this.isConnected = false;
this.peerDiscovery = false;
// Move these to the Node's settings object
this.interval = 5000;
this.minConnections = 8;
this.minKnownPeers = 10;
};
PeerManager.superclass = b.superclass || require('events').EventEmitter;
PeerManager.Connection = Connection;
PeerManager.prototype.start = function() {
this.active = true;
if(!this.timer) {
this.timer = setInterval(this.checkStatus.bind(this), this.interval);
}
};
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.addPeer = function(peer, port) {
if(peer instanceof Peer) {
this.peers.push(peer);
} 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.';
}
};
PeerManager.prototype.checkStatus = function checkStatus() {
// Make sure we are connected to all forcePeers
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) {
log.info('connecting to '+peer);
try {
return this.addConnection(peer.createConnection(), peer);
} catch (e) {
log.err('creating connection',e);
return null;
}
};
PeerManager.prototype.addConnection = function(socketConn, peer) {
var conn = new Connection(socketConn, peer);
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) {
if(!this.peerDiscovery) return;
var now = GetAdjustedTime();
e.message.addrs.forEach(function (addr) {
try {
// In case of an invalid time, assume "5 days ago"
if (addr.time <= 100000000 || addr.time > (now + 10 * 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));
if (e.message.addrs.length < 1000 ) {
e.conn.getaddr = false;
}
};
PeerManager.prototype.handleGetAddr = function(e) {
// TODO: Reply with addr message.
};
PeerManager.prototype.handleError = function(e) {
log.err('unkown error with peer '+e.peer+' (disconnecting): '+e.err);
this.handleDisconnect.apply(this, [].slice.call(arguments));
};
PeerManager.prototype.handleDisconnect = function(e) {
log.info('disconnected from peer '+e.peer);
var i = this.connections.indexOf(e.conn);
if(i != -1) this.connections.splice(i, 1);
if(!this.connections.length) {
this.emit('netDisconnected');
this.isConnected = false;
}
};
PeerManager.prototype.getActiveConnection = function () {
var activeConnections = this.connections.filter(function (conn) {
return conn.active;
});
if (activeConnections.length) {
var randomIndex = Math.floor(Math.random()*activeConnections.length);
var candidate = activeConnections[randomIndex];
if (candidate.socket.writable) {
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 {
return null;
}
};
PeerManager.prototype.getActiveConnections = function () {
return this.connections.slice(0);
};
return PeerManager;
GetAdjustedTime = imports.GetAdjustedTime || function () {
// TODO: Implement actual adjustment
return Math.floor(new Date().getTime() / 1000);
};
module.defineClass(spec);
function PeerManager() {
this.active = false;
this.timer = null;
this.peers = [];
this.connections = [];
this.isConnected = false;
this.peerDiscovery = false;
// Move these to the Node's settings object
this.interval = 5000;
this.minConnections = 8;
this.minKnownPeers = 10;
};
PeerManager.parent = imports.parent || require('events').EventEmitter;
PeerManager.Connection = Connection;
PeerManager.prototype.start = function() {
this.active = true;
if(!this.timer) {
this.timer = setInterval(this.checkStatus.bind(this), this.interval);
}
};
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.addPeer = function(peer, port) {
if(peer instanceof Peer) {
this.peers.push(peer);
} 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.';
}
};
PeerManager.prototype.checkStatus = function checkStatus() {
// Make sure we are connected to all forcePeers
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) {
log.info('connecting to '+peer);
try {
return this.addConnection(peer.createConnection(), peer);
} catch (e) {
log.err('creating connection',e);
return null;
}
};
PeerManager.prototype.addConnection = function(socketConn, peer) {
var conn = new Connection(socketConn, peer);
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) {
if(!this.peerDiscovery) return;
var now = GetAdjustedTime();
e.message.addrs.forEach(function (addr) {
try {
// In case of an invalid time, assume "5 days ago"
if (addr.time <= 100000000 || addr.time > (now + 10 * 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));
if (e.message.addrs.length < 1000 ) {
e.conn.getaddr = false;
}
};
PeerManager.prototype.handleGetAddr = function(e) {
// TODO: Reply with addr message.
};
PeerManager.prototype.handleError = function(e) {
log.err('unkown error with peer '+e.peer+' (disconnecting): '+e.err);
this.handleDisconnect.apply(this, [].slice.call(arguments));
};
PeerManager.prototype.handleDisconnect = function(e) {
log.info('disconnected from peer '+e.peer);
var i = this.connections.indexOf(e.conn);
if(i != -1) this.connections.splice(i, 1);
if(!this.connections.length) {
this.emit('netDisconnected');
this.isConnected = false;
}
};
PeerManager.prototype.getActiveConnection = function () {
var activeConnections = this.connections.filter(function (conn) {
return conn.active;
});
if (activeConnections.length) {
var randomIndex = Math.floor(Math.random()*activeConnections.length);
var candidate = activeConnections[randomIndex];
if (candidate.socket.writable) {
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 {
return null;
}
};
PeerManager.prototype.getActiveConnections = function () {
return this.connections.slice(0);
};
module.exports = require('soop')(PeerManager);

View File

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

View File

@ -1,210 +1,208 @@
// RpcClient.js
// MIT/X11-like license. See LICENSE.txt.
// 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) {
var http = b.http || require('http');
var https = b.https || require('https');
var log = b.log || require('./util/log');
function RpcClient(opts) {
opts = opts || {};
this.host = opts.host || '127.0.0.1';
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) {
opts = opts || {};
this.host = opts.host || '127.0.0.1';
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;
}
var callspec = {
addMultiSigAddress: '',
addNode: '',
backupWallet: '',
createMultiSig: '',
createRawTransaction: '',
decodeRawTransaction: '',
dumpPrivKey: '',
encryptWallet: '',
getAccount: '',
getAccountAddress: 'str',
getAddedNodeInfo: '',
getAddressesByAccount: '',
getBalance: 'str int',
getBestBlockHash: '',
getBlock: '',
getBlockCount: '',
getBlockHash: 'int',
getBlockNumber: '',
getBlockTemplate: '',
getConnectionCount: '',
getDifficulty: '',
getGenerate: '',
getHashesPerSec: '',
getInfo: '',
getMemoryPool: '',
getMiningInfo: '',
getNewAddress: '',
getPeerInfo: '',
getRawMemPool: '',
getRawTransaction: 'str int',
getReceivedByAccount: 'str int',
getReceivedByAddress: 'str int',
getTransaction: '',
getTxOut: 'str int bool',
getTxOutSetInfo: '',
getWork: '',
help: '',
importAddress: 'str str bool',
importPrivKey: 'str str bool',
keyPoolRefill: '',
listAccounts: 'int',
listAddressGroupings: '',
listReceivedByAccount: 'int bool',
listReceivedByAddress: 'int bool',
listSinceBlock: 'str int',
listTransactions: 'str int int',
listUnspent: 'int int',
listLockUnspent: 'bool',
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) {
return Array.prototype.slice.call(arr, start, end);
};
function generateRPCMethods(constructor, apiCalls, rpc) {
function createRPCMethod(methodName, argMap) {
return function() {
var limit = arguments.length - 1;
if(this.batchedCalls) var limit = arguments.length;
for (var i=0; i<limit; 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]);
}
};
};
var types = {
str: function(arg) {return arg.toString();},
int: 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');},
};
for(var k in apiCalls) {
if (apiCalls.hasOwnProperty(k)) {
var spec = apiCalls[k].split(' ');
for (var i = 0; i < spec.length; i++) {
if(types[spec[i]]) {
spec[i] = types[spec[i]];
} else {
spec[i] = types.string;
}
}
var methodName = k.toLowerCase();
constructor.prototype[k] = createRPCMethod(methodName, spec);
constructor.prototype[methodName] = constructor.prototype[k];
}
}
}
function rpc(request, callback) {
var self = this;
var request;
request = JSON.stringify(request);
var auth = Buffer(self.user + ':' + self.pass).toString('base64');
var options = {
host: self.host,
path: '/',
method: 'POST',
port: self.port,
agent: self.disableAgent ? false : undefined,
};
if(self.httpOptions) {
for(var k in self.httpOptions) {
options[k] = self.httpOptions[k];
}
}
var err = null;
var req = this.protocol.request(options, function(res) {
var buf = '';
res.on('data', function(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) {
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();
};
generateRPCMethods(RpcClient, callspec, rpc);
return RpcClient;
var callspec = {
addMultiSigAddress: '',
addNode: '',
backupWallet: '',
createMultiSig: '',
createRawTransaction: '',
decodeRawTransaction: '',
dumpPrivKey: '',
encryptWallet: '',
getAccount: '',
getAccountAddress: 'str',
getAddedNodeInfo: '',
getAddressesByAccount: '',
getBalance: 'str int',
getBestBlockHash: '',
getBlock: '',
getBlockCount: '',
getBlockHash: 'int',
getBlockNumber: '',
getBlockTemplate: '',
getConnectionCount: '',
getDifficulty: '',
getGenerate: '',
getHashesPerSec: '',
getInfo: '',
getMemoryPool: '',
getMiningInfo: '',
getNewAddress: '',
getPeerInfo: '',
getRawMemPool: '',
getRawTransaction: 'str int',
getReceivedByAccount: 'str int',
getReceivedByAddress: 'str int',
getTransaction: '',
getTxOut: 'str int bool',
getTxOutSetInfo: '',
getWork: '',
help: '',
importAddress: 'str str bool',
importPrivKey: 'str str bool',
keyPoolRefill: '',
listAccounts: 'int',
listAddressGroupings: '',
listReceivedByAccount: 'int bool',
listReceivedByAddress: 'int bool',
listSinceBlock: 'str int',
listTransactions: 'str int int',
listUnspent: 'int int',
listLockUnspent: 'bool',
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: '',
};
module.defineClass(ClassSpec);
var slice = function(arr, start, end) {
return Array.prototype.slice.call(arr, start, end);
};
function generateRPCMethods(constructor, apiCalls, rpc) {
function createRPCMethod(methodName, argMap) {
return function() {
var limit = arguments.length - 1;
if(this.batchedCalls) var limit = arguments.length;
for (var i=0; i<limit; 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]);
}
};
};
var types = {
str: function(arg) {return arg.toString();},
int: 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');},
};
for(var k in apiCalls) {
if (apiCalls.hasOwnProperty(k)) {
var spec = apiCalls[k].split(' ');
for (var i = 0; i < spec.length; i++) {
if(types[spec[i]]) {
spec[i] = types[spec[i]];
} else {
spec[i] = types.string;
}
}
var methodName = k.toLowerCase();
constructor.prototype[k] = createRPCMethod(methodName, spec);
constructor.prototype[methodName] = constructor.prototype[k];
}
}
}
function rpc(request, callback) {
var self = this;
var request;
request = JSON.stringify(request);
var auth = Buffer(self.user + ':' + self.pass).toString('base64');
var options = {
host: self.host,
path: '/',
method: 'POST',
port: self.port,
agent: self.disableAgent ? false : undefined,
};
if(self.httpOptions) {
for(var k in self.httpOptions) {
options[k] = self.httpOptions[k];
}
}
var err = null;
var req = this.protocol.request(options, function(res) {
var buf = '';
res.on('data', function(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) {
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();
};
generateRPCMethods(RpcClient, callspec, rpc);
module.exports = require('soop')(RpcClient);

109
SIN.js
View File

@ -1,60 +1,55 @@
require('classtool');
var imports = require('soop').imports();
var parent = imports.parent || require('./util/VersionedData');
function ClassSpec(b) {
var superclass = b.superclass || require('./util/VersionedData').class();
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);
function SIN(type, payload) {
if (typeof type != 'number') {
SIN.super(this, arguments);
return;
};
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);
};
// get or set the SIN-type data (the second byte of the address)
SIN.prototype.type = function(num) {
if(num || (num === 0)) {
this.doAsBinary(function() {this.data.writeUInt8(num, 1);});
return num;
}
return this.as('binary').readUInt8(1);
};
// get or set the payload data (as a Buffer object)
SIN.prototype.payload = function(data) {
if(data) {
this.doAsBinary(function() {data.copy(this.data, 2);});
return data;
}
return this.as('binary').slice(1);
};
SIN.prototype.validate = function() {
this.doAsBinary(function() {
SIN.super(this, 'validate', arguments);
if (this.data.length != 22) throw new Error('invalid data length');
});
};
return SIN;
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);
};
module.defineClass(ClassSpec);
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)
SIN.prototype.type = function(num) {
if(num || (num === 0)) {
this.doAsBinary(function() {this.data.writeUInt8(num, 1);});
return num;
}
return this.as('binary').readUInt8(1);
};
// get or set the payload data (as a Buffer object)
SIN.prototype.payload = function(data) {
if(data) {
this.doAsBinary(function() {data.copy(this.data, 2);});
return data;
}
return this.as('binary').slice(1);
};
SIN.prototype.validate = function() {
this.doAsBinary(function() {
SIN.super(this, 'validate', arguments);
if (this.data.length != 22) throw new Error('invalid data length');
});
};
module.exports = require('soop')(SIN);

View File

@ -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) {
var coinUtil = require('./util/util');
var timeUtil = require('./util/time');
var KeyModule = require('./Key');
var SIN = require('./SIN').class();
function SINKey(cfg) {
if (typeof cfg != 'object')
cfg = {};
function SINKey(cfg) {
if (typeof cfg != 'object')
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() {
return coinUtil.sha256ripe160(this.privKey.public);
};
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 obj;
};
return SINKey;
this.created = cfg.created;
this.privKey = cfg.privKey;
};
module.defineClass(ClassSpec);
SINKey.prototype.generate = function() {
this.privKey = KeyModule.Key.generateSync();
this.created = timeUtil.curtime();
};
SINKey.prototype.pubkeyHash = function() {
return coinUtil.sha256ripe160(this.privKey.public);
};
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 obj;
};
module.exports = require('soop')(SINKey);

1028
Script.js

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

272
Wallet.js
View File

@ -1,142 +1,140 @@
require('classtool');
var imports = require('soop').imports();
var hex = function(hex) {return new Buffer(hex, 'hex');};
function ClassSpec(b) {
var fs = require('fs');
var EncFile = require('./util/EncFile');
var Address = require('./Address').class();
var networks = require('./networks');
var util = b.util || require('./util/util');
var ENC_METHOD = 'aes-256-cbc';
var fs = require('fs');
var EncFile = require('./util/EncFile');
var Address = require('./Address');
var networks = require('./networks');
var util = imports.util || require('./util/util');
var ENC_METHOD = 'aes-256-cbc';
var skeleton = {
client: 'libcoin',
client_version: '0.0.1',
network: 'testnet',
version: 1,
best_hash: null,
best_height: -1,
keys: [],
sin: {},
scripts: {},
};
function Wallet(cfg) {
if (typeof cfg !== 'object')
cfg = {};
// deep copy (no references)
if (cfg.datastore)
this.datastore = JSON.parse(JSON.stringify(cfg.datastore));
else
this.datastore = JSON.parse(JSON.stringify(skeleton));
this.network = undefined;
this.dirty = cfg.dirty || true;
};
Wallet.prototype.readSync = function(filename, passphrase) {
this.datastore = EncFile.readJFileSync(ENC_METHOD,
passphrase, filename);
this.dirty = false;
};
Wallet.prototype.writeSync = function(filename, passphrase) {
var tmp_fn = filename + ".tmp";
EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn,
this.datastore);
fs.renameSync(tmp_fn, filename);
this.dirty = false;
};
Wallet.prototype.setNetwork = function(netname) {
if (!netname)
netname = this.datastore.network;
switch (netname) {
case "mainnet":
case "livenet":
this.network = networks.livenet;
break;
case "testnet":
this.network = networks.testnet;
break;
default:
throw new Error("Unsupported network");
}
// store+canonicalize name
this.datastore['network'] = this.network.name;
this.dirty = true;
};
Wallet.prototype.addKey = function(wkey) {
this.datastore.keys.push(wkey);
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;
var skeleton = {
client: 'libcoin',
client_version: '0.0.1',
network: 'testnet',
version: 1,
best_hash: null,
best_height: -1,
keys: [],
sin: {},
scripts: {},
};
module.defineClass(ClassSpec);
function Wallet(cfg) {
if (typeof cfg !== 'object')
cfg = {};
// deep copy (no references)
if (cfg.datastore)
this.datastore = JSON.parse(JSON.stringify(cfg.datastore));
else
this.datastore = JSON.parse(JSON.stringify(skeleton));
this.network = undefined;
this.dirty = cfg.dirty || true;
};
Wallet.prototype.readSync = function(filename, passphrase) {
this.datastore = EncFile.readJFileSync(ENC_METHOD,
passphrase, filename);
this.dirty = false;
};
Wallet.prototype.writeSync = function(filename, passphrase) {
var tmp_fn = filename + ".tmp";
EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn,
this.datastore);
fs.renameSync(tmp_fn, filename);
this.dirty = false;
};
Wallet.prototype.setNetwork = function(netname) {
if (!netname)
netname = this.datastore.network;
switch (netname) {
case "mainnet":
case "livenet":
this.network = networks.livenet;
break;
case "testnet":
this.network = networks.testnet;
break;
default:
throw new Error("Unsupported network");
}
// store+canonicalize name
this.datastore['network'] = this.network.name;
this.dirty = true;
};
Wallet.prototype.addKey = function(wkey) {
this.datastore.keys.push(wkey);
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;
};
module.exports = require('soop')(Wallet);

View File

@ -1,55 +1,52 @@
require('classtool');
var imports = require('soop').imports();
function ClassSpec(b) {
var coinUtil = require('./util/util');
var timeUtil = require('./util/time');
var KeyModule = require('./Key');
var PrivateKey = require('./PrivateKey').class();
var Address = require('./Address').class();
var coinUtil = require('./util/util');
var timeUtil = require('./util/time');
var KeyModule = require('./Key');
var PrivateKey = require('./PrivateKey');
var Address = require('./Address');
function WalletKey(cfg) {
if (!cfg) cfg = {};
if (!cfg.network) throw new Error('network parameter is required');
this.network = cfg.network; // required
this.created = cfg.created;
this.privKey = cfg.privKey;
};
WalletKey.prototype.generate = function() {
this.privKey = KeyModule.Key.generateSync();
this.created = timeUtil.curtime();
};
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(),
};
return obj;
};
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();
};
return WalletKey;
function WalletKey(cfg) {
if (!cfg) cfg = {};
if (!cfg.network) throw new Error('network parameter is required');
this.network = cfg.network; // required
this.created = cfg.created;
this.privKey = cfg.privKey;
};
module.defineClass(ClassSpec);
WalletKey.prototype.generate = function() {
this.privKey = KeyModule.Key.generateSync();
this.created = timeUtil.curtime();
};
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(),
};
return obj;
};
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.exports = require('soop')(WalletKey);

View File

@ -13,7 +13,7 @@ describe('Address', function() {
should.exist(AddressModule);
});
it('should be able to create class', function() {
Address = AddressModule.class();
Address = AddressModule;
should.exist(Address);
});
it('should be able to create instance', function() {

View File

@ -12,7 +12,7 @@ describe('Block', function() {
should.exist(BlockModule);
});
it('should be able to create class', function() {
Block = BlockModule.class();
Block = BlockModule;
should.exist(Block);
});
it('should be able to create instance', function() {

View File

@ -13,7 +13,7 @@ describe('Bloom', function() {
should.exist(BloomModule);
});
it('should be able to create class', function() {
Bloom = BloomModule.class();
Bloom = BloomModule;
should.exist(Bloom);
});
it('should be able to create instance', function() {

View File

@ -14,7 +14,7 @@ describe('Connection', function() {
should.exist(ConnectionModule);
});
it('should be able to create class', function() {
Connection = ConnectionModule.class();
Connection = ConnectionModule;
should.exist(Connection);
});
it('should be able to create instance', function() {

View File

@ -13,7 +13,7 @@ describe('EncodedData', function() {
should.exist(EncodedDataModule);
});
it('should be able to create class', function() {
EncodedData = EncodedDataModule.class();
EncodedData = EncodedDataModule;
should.exist(EncodedData);
});
it('should be able to create an instance', function() {

View File

@ -13,7 +13,7 @@ describe('Opcode', function() {
should.exist(OpcodeModule);
});
it('should be able to create class', function() {
Opcode = OpcodeModule.class();
Opcode = OpcodeModule;
should.exist(Opcode);
});
it('should be able to create instance', function() {

View File

@ -13,7 +13,7 @@ describe('Peer', function() {
should.exist(PeerModule);
});
it('should be able to create class', function() {
Peer = PeerModule.class();
Peer = PeerModule;
should.exist(Peer);
});
it('should be able to create instance', function() {

View File

@ -13,7 +13,7 @@ describe('PeerManager', function() {
should.exist(PeerManagerModule);
});
it('should be able to create class', function() {
PeerManager = PeerManagerModule.class();
PeerManager = PeerManagerModule;
should.exist(PeerManager);
});
it('should be able to create instance', function() {

View File

@ -15,7 +15,7 @@ describe('PrivateKey', function() {
should.exist(PrivateKeyModule);
});
it('should be able to create class', function() {
PrivateKey = PrivateKeyModule.class();
PrivateKey = PrivateKeyModule;
should.exist(PrivateKey);
});
it('should be able to create instance', function() {

View File

@ -7,7 +7,7 @@ var should = chai.should();
var RpcClientModule = bitcore.RpcClient;
var RpcClient;
RpcClient = RpcClientModule.class();
RpcClient = RpcClientModule;
describe('RpcClient', function() {
it('should initialze the main object', function() {

View File

@ -13,7 +13,7 @@ describe('SIN', function() {
should.exist(SINModule);
});
it('should be able to create class', function() {
SIN = SINModule.class();
SIN = SINModule;
should.exist(SIN);
});
it('should be able to create instance', function() {

View File

@ -16,7 +16,7 @@ describe('SINKey', function() {
should.exist(SINKeyModule);
});
it('should be able to create class', function() {
SINKey = SINKeyModule.class();
SINKey = SINKeyModule;
should.exist(SINKey);
});
it('should be able to create instance', function() {

View File

@ -6,7 +6,7 @@ var bitcore = require('../bitcore');
var should = chai.should();
var ScriptModule = bitcore.Script;
var Address = bitcore.Address.class();
var Address = bitcore.Address;
var networks = bitcore.networks;
var Script;
@ -15,7 +15,7 @@ describe('Script', function() {
should.exist(ScriptModule);
});
it('should be able to create class', function() {
Script = ScriptModule.class();
Script = ScriptModule;
should.exist(Script);
});
it('should be able to create instance', function() {

View File

@ -13,7 +13,7 @@ describe('ScriptInterpreter', function() {
should.exist(ScriptInterpreterModule);
});
it('should be able to create class', function() {
ScriptInterpreter = ScriptInterpreterModule.class();
ScriptInterpreter = ScriptInterpreterModule;
should.exist(ScriptInterpreter);
});
it('should be able to create instance', function() {

View File

@ -13,7 +13,7 @@ describe('Transaction', function() {
should.exist(TransactionModule);
});
it('should be able to create class', function() {
Transaction = TransactionModule.class();
Transaction = TransactionModule;
should.exist(Transaction);
});
it('should be able to create instance', function() {

View File

@ -13,7 +13,7 @@ describe('VersionedData', function() {
should.exist(VersionedDataModule);
});
it('should be able to create class', function() {
VersionedData = VersionedDataModule.class();
VersionedData = VersionedDataModule;
should.exist(VersionedData);
});
it('should be able to create an instance', function() {

View File

@ -13,7 +13,7 @@ describe('Wallet', function() {
should.exist(WalletModule);
});
it('should be able to create class', function() {
Wallet = WalletModule.class();
Wallet = WalletModule;
should.exist(Wallet);
});
it('should be able to create instance', function() {

View File

@ -14,7 +14,7 @@ describe('WalletKey', function() {
should.exist(WalletKeyModule);
});
it('should be able to create class', function() {
WalletKey = WalletKeyModule.class();
WalletKey = WalletKeyModule;
should.exist(WalletKey);
});
it('should be able to create instance', function() {

View File

@ -4,8 +4,8 @@ var chai = require('chai');
var bitcore = require('../bitcore');
var should = chai.should();
var Address = bitcore.Address.class();
var PrivateKey = bitcore.PrivateKey.class();
var Address = bitcore.Address;
var PrivateKey = bitcore.PrivateKey;
var networks = bitcore.networks;
var KeyModule = bitcore.KeyModule;

View File

@ -2,147 +2,145 @@
* Simple synchronous parser based on node-binary.
*/
function spec(b) {
function Parser(buffer)
{
this.subject = buffer;
this.pos = 0;
};
Parser.prototype.buffer = function buffer(len) {
var buf = this.subject.slice(this.pos, this.pos+len);
this.pos += len;
return buf;
};
Parser.prototype.search = function search(needle) {
var len;
if ("string" === typeof needle || Buffer.isBuffer(needle)) {
// TODO: Slicing is probably too slow
len = this.subject.slice(this.pos).indexOf(needle);
if (len !== -1) {
this.pos += len + needle.length;
}
return len;
}
if ("number" === typeof needle) {
needle = needle & 0xff;
// Search for single byte
for (var i = this.pos, l = this.subject.length; i < l; i++) {
if (this.subject[i] == needle) {
len = i - this.pos;
this.pos = i+1;
return len;
}
}
return -1;
}
};
/**
* Like search(), but returns the skipped bytes
*/
Parser.prototype.scan = function scan(needle) {
var startPos = this.pos;
var len = this.search(needle);
if (len !== -1) {
return this.subject.slice(startPos, startPos+len);
} else {
throw new Error('No match');
}
};
Parser.prototype.eof = function eof() {
return this.pos >= this.subject.length;
};
// convert byte strings to unsigned little endian numbers
function decodeLEu (bytes) {
var acc = 0;
for (var i = 0; i < bytes.length; i++) {
acc += Math.pow(256,i) * bytes[i];
}
return acc;
}
// convert byte strings to unsigned big endian numbers
function decodeBEu (bytes) {
var acc = 0;
for (var i = 0; i < bytes.length; i++) {
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);
if ((bytes[0] & 0x80) == 0x80) {
val -= Math.pow(256, bytes.length);
}
return val;
}
// convert byte strings to signed little endian numbers
function decodeLEs (bytes) {
var val = decodeLEu(bytes);
if ((bytes[bytes.length - 1] & 0x80) == 0x80) {
val -= Math.pow(256, bytes.length);
}
return val;
}
function getDecoder(len, fn) {
return function () {
var buf = this.buffer(len);
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);
};
return Parser;
var imports = require('soop').imports();
function Parser(buffer)
{
this.subject = buffer;
this.pos = 0;
};
module.defineClass(spec);
Parser.prototype.buffer = function buffer(len) {
var buf = this.subject.slice(this.pos, this.pos+len);
this.pos += len;
return buf;
};
Parser.prototype.search = function search(needle) {
var len;
if ("string" === typeof needle || Buffer.isBuffer(needle)) {
// TODO: Slicing is probably too slow
len = this.subject.slice(this.pos).indexOf(needle);
if (len !== -1) {
this.pos += len + needle.length;
}
return len;
}
if ("number" === typeof needle) {
needle = needle & 0xff;
// Search for single byte
for (var i = this.pos, l = this.subject.length; i < l; i++) {
if (this.subject[i] == needle) {
len = i - this.pos;
this.pos = i+1;
return len;
}
}
return -1;
}
};
/**
* Like search(), but returns the skipped bytes
*/
Parser.prototype.scan = function scan(needle) {
var startPos = this.pos;
var len = this.search(needle);
if (len !== -1) {
return this.subject.slice(startPos, startPos+len);
} else {
throw new Error('No match');
}
};
Parser.prototype.eof = function eof() {
return this.pos >= this.subject.length;
};
// convert byte strings to unsigned little endian numbers
function decodeLEu (bytes) {
var acc = 0;
for (var i = 0; i < bytes.length; i++) {
acc += Math.pow(256,i) * bytes[i];
}
return acc;
}
// convert byte strings to unsigned big endian numbers
function decodeBEu (bytes) {
var acc = 0;
for (var i = 0; i < bytes.length; i++) {
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);
if ((bytes[0] & 0x80) == 0x80) {
val -= Math.pow(256, bytes.length);
}
return val;
}
// convert byte strings to signed little endian numbers
function decodeLEs (bytes) {
var val = decodeLEu(bytes);
if ((bytes[bytes.length - 1] & 0x80) == 0x80) {
val -= Math.pow(256, bytes.length);
}
return val;
}
function getDecoder(len, fn) {
return function () {
var buf = this.buffer(len);
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);
};
module.exports = require('soop')(Parser);

View File

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

View File

@ -1,39 +1,37 @@
require('classtool');
var imports = require('soop').imports();
var base58 = imports.base58 || require('base58-native').base58Check;
var superclass = imports.parent || require('./EncodedData');
function ClassSpec(b) {
var superclass = b.superclass || require('./EncodedData').class();
function VersionedData(version, payload) {
if(typeof version != 'number') {
VersionedData.super(this, arguments);
return;
};
this.data = new Buffer(payload.length + 1);
this.__proto__ = this.encodings['binary'];
this.version(version);
this.payload(payload);
function VersionedData(version, payload) {
if(typeof version != 'number') {
VersionedData.super(this, arguments);
return;
};
VersionedData.superclass = superclass;
superclass.applyEncodingsTo(VersionedData);
// get or set the version data (the first byte of the address)
VersionedData.prototype.version = 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 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);
};
return VersionedData;
this.data = new Buffer(payload.length + 1);
this.__proto__ = this.encodings['binary'];
this.version(version);
this.payload(payload);
};
module.defineClass(ClassSpec);
VersionedData.parent = superclass || require('./Person');
superclass.applyEncodingsTo(VersionedData);
// get or set the version data (the first byte of the address)
VersionedData.prototype.version = 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 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.exports = require('soop')(VersionedData);