http: add chain watching and scanning.

This commit is contained in:
Christopher Jeffrey 2016-08-13 18:27:28 -07:00
parent 59d4fa22ce
commit 487efb6639
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -189,7 +189,7 @@ HTTPServer.prototype._init = function _init() {
assert(typeof params.hash === 'string', 'Hash must be a string.');
if (params.hash.length !== 64) {
options.height = Number(params.hash);
assert(utils.isUInt32(options.height), 'Hash must be a number.');
assert(utils.isUInt32(options.height), 'Height must be a number.');
} else {
options.hash = utils.revHex(params.hash);
}
@ -247,8 +247,6 @@ HTTPServer.prototype._init = function _init() {
if (typeof params.subtractFee === 'number') {
options.subtractFee = params.subtractFee;
assert(utils.isUInt32(options.subtractFee), 'subtractFee must be a number.');
} else if (params.subtractFee === 'true') {
options.subtractFee = true;
} else {
assert(typeof options.subtractFee === 'boolean', 'subtractFee must be a boolean.');
options.subtractFee = params.subtractFee;
@ -917,6 +915,10 @@ HTTPServer.prototype._initIO = function _initIO() {
self.emit('error', err);
});
socket.on('disconnect', function() {
socket.destroy();
});
socket.once('auth', function(apiKey, callback) {
if (typeof callback !== 'function')
return socket.destroy();
@ -990,6 +992,50 @@ HTTPServer.prototype._initIO = function _initIO() {
return callback();
});
socket.on('watch chain', function() {
socket.watchChain();
});
socket.on('unwatch chain', function() {
socket.unwatchChain();
});
socket.on('watch address', function(addresses) {
if (!Array.isArray(addresses))
return socket.destroy();
try {
socket.addFilter(addresses);
} catch (e) {
return socket.destroy();
}
});
socket.on('unwatch address', function(addresses) {
if (!Array.isArray(addresses))
return socket.destroy();
try {
socket.removeFilter(addresses);
} catch (e) {
return socket.destroy();
}
});
socket.on('scan chain', function(start, callback) {
if (typeof callback !== 'function')
return socket.destroy();
if (typeof start !== 'string')
return callback({ error: 'Invalid parameter.' });
try {
socket.scan(start, callback);
} catch (e) {
return callback({ error: e.message });
}
});
});
this.walletdb.on('tx', function(id, tx, info) {
@ -1138,6 +1184,20 @@ function ClientSocket(server, socket) {
this.host = socket.conn.remoteAddress;
this.timeout = null;
this.auth = false;
this.filter = {};
this.filterCount = 0;
this.node = this.server.node;
this.network = this.server.network;
this.chain = this.server.chain;
this.mempool = this.server.mempool;
this.pool = this.server.pool;
this.fees = this.server.fees;
this.miner = this.server.miner;
this.wallet = this.server.wallet;
this.walletdb = this.server.walletdb;
this.logger = this.server.logger;
this.events = [];
this._init();
}
@ -1149,12 +1209,162 @@ ClientSocket.prototype._init = function _init() {
var socket = this.socket;
var emit = EventEmitter.prototype.emit;
var onevent = socket.onevent.bind(socket);
socket.onevent = function(packet) {
var result = onevent(packet);
var args = packet.data || [];
emit.apply(self, args);
return result;
};
socket.on('error', function(err) {
emit.call(self, 'error', err);
});
socket.on('disconnect', function() {
emit.call(self, 'disconnect');
});
};
ClientSocket.prototype.addFilter = function addFilter(addresses) {
var i, hash;
for (i = 0; i < addresses.length; i++) {
hash = bcoin.address.getHash(addresses[i], 'hex');
if (!hash)
throw new Error('Bad address.');
if (!this.filter[hash]) {
this.filter[hash] = true;
this.filterCount++;
}
}
};
ClientSocket.prototype.removeFilter = function removeFilter(addresses) {
var i, hash;
for (i = 0; i < addresses.length; i++) {
hash = bcoin.address.getHash(addresses[i], 'hex');
if (!hash)
throw new Error('Bad address.');
if (this.filter[hash]) {
delete this.filter[hash];
this.filterCount--;
}
}
};
ClientSocket.prototype.bind = function bind(obj, event, listener) {
this.events.push([obj, event, listener]);
obj.on(event, listener);
};
ClientSocket.prototype.unbind = function unbind(obj, event) {
var i, event;
for (i = this.events.length - 1; i >= 0; i--) {
event = this.events[i]
if (event[0] === obj && event[1] === event) {
obj.removeListener(event, event[2]);
this.events.splice(i, 1);
}
}
};
ClientSocket.prototype.unbindAll = function unbindAll() {
var i, event;
for (i = 0; i < this.events.length; i++) {
event = this.events[i];
event[0].removeListener(event[1], event[2]);
}
this.events.length = 0;
};
ClientSocket.prototype.watchChain = function watchChain() {
var self = this;
this.bind(this.chain, 'connect', function(entry, block) {
var txs;
self.emit('block connect', entry.toJSON());
txs = self.testBlock(block);
if (txs)
self.emit('block tx', txs, entry.toJSON());
});
this.bind(this.chain, 'disconnect', function(entry, block) {
self.emit('block disconnect', entry.toJSON());
});
this.bind(this.mempool, 'tx', function(tx) {
if (self.testFilter(tx))
self.emit('mempool tx', tx.toJSON());
});
};
ClientSocket.prototype.unwatchChain = function unwatchChain() {
this.unbind(this.chain, 'connect');
this.unbind(this.chain, 'disconnect');
this.unbind(this.mempool, 'tx');
};
ClientSocket.prototype.testBlock = function testBlock(tx) {
var txs = [];
var i, tx;
if (this.filterCount === 0)
return;
for (i = 0; i < block.txs.length; i++) {
tx = block.txs[i];
if (this.testFilter(tx))
txs.push(tx.toJSON());
}
if (txs.length === 0)
return;
return txs;
};
ClientSocket.prototype.testFilter = function testFilter(tx) {
var i, hashes, hash;
if (this.filterCount === 0)
return;
hashes = tx.getHashes('hex');
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
if (this.filter[hash])
return true;
}
};
ClientSocket.prototype.scan = function scan(start, callback) {
var self = this;
var hash;
hashes = Object.keys(this.filter);
start = utils.revHex(start);
this.chain.db.scan(start, hashes, function(tx, entry, next) {
self.emit('block tx', [tx.toJSON()], entry.toJSON());
next();
}, function(err) {
if (err)
return callback({ error: err.message });
callback();
});
};
ClientSocket.prototype.join = function join(id) {
@ -1186,6 +1396,7 @@ ClientSocket.prototype.stop = function stop() {
};
ClientSocket.prototype.destroy = function() {
this.unbindAll();
this.stop();
this.socket.disconnect();
};