refactor: peer. etc.

This commit is contained in:
Christopher Jeffrey 2016-09-22 15:47:24 -07:00
parent 19e5359f0f
commit 63a9dc61f6
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
10 changed files with 599 additions and 299 deletions

View File

@ -928,6 +928,13 @@ Chain.prototype.reset = co(function* reset(height) {
}
});
/**
* Reset the chain to the desired height without a lock.
* @private
* @param {Number} height
* @param {Function} callback
*/
Chain.prototype._reset = co(function* reset(height) {
var result = yield this.db.reset(height);
@ -956,6 +963,13 @@ Chain.prototype.resetTime = co(function* resetTime(ts) {
}
});
/**
* Reset the chain to the desired timestamp without a lock.
* @private
* @param {Number} ts - Timestamp.
* @param {Function} callback
*/
Chain.prototype._resetTime = co(function* resetTime(ts) {
var entry = yield this.byTime(ts);
@ -1003,6 +1017,13 @@ Chain.prototype.add = co(function* add(block) {
}
});
/**
* Add a block to the chain without a lock.
* @private
* @param {Block|MerkleBlock|MemBlock} block
* @param {Function} callback - Returns [{@link VerifyError}].
*/
Chain.prototype._add = co(function* add(block) {
var ret, initial, hash, prevBlock;
var height, checkpoint, orphan, entry;
@ -1017,7 +1038,8 @@ Chain.prototype._add = co(function* add(block) {
hash = block.hash('hex');
prevBlock = block.prevBlock;
this._mark();
// Mark the start time.
this.mark();
// Do not revalidate known invalid blocks.
if (this.invalid[hash] || this.invalid[prevBlock]) {
@ -1176,7 +1198,7 @@ Chain.prototype._add = co(function* add(block) {
}
// Keep track of stats.
this._done(block, entry);
this.finish(block, entry);
// No orphan chain.
if (!this.orphan.map[hash])
@ -1225,7 +1247,7 @@ Chain.prototype._isSlow = function _isSlow() {
* @private
*/
Chain.prototype._mark = function _mark() {
Chain.prototype.mark = function mark() {
this._time = utils.hrtime();
};
@ -1237,7 +1259,7 @@ Chain.prototype._mark = function _mark() {
* @param {ChainEntry} entry
*/
Chain.prototype._done = function _done(block, entry) {
Chain.prototype.finish = function finish(block, entry) {
var elapsed, time;
// Keep track of total blocks handled.
@ -1500,6 +1522,13 @@ Chain.prototype.getLocator = co(function* getLocator(start) {
}
});
/**
* Calculate chain locator without a lock.
* @private
* @param {(Number|Hash)?} start
* @param {Function} callback
*/
Chain.prototype._getLocator = co(function* getLocator(start) {
var hashes = [];
var step = 1;

View File

@ -154,6 +154,14 @@ Mempool.prototype.addBlock = co(function* addBlock(block) {
}
});
/**
* Notify the mempool that a new block
* has come without a lock.
* @private
* @param {Block} block
* @param {Function} callback
*/
Mempool.prototype._addBlock = co(function* addBlock(block) {
var entries = [];
var i, entry, tx, hash;
@ -207,6 +215,14 @@ Mempool.prototype.removeBlock = co(function* removeBlock(block) {
}
});
/**
* Notify the mempool that a block
* has been disconnected without a lock.
* @private
* @param {Block} block
* @param {Function} callback
*/
Mempool.prototype._removeBlock = co(function* removeBlock(block) {
var i, entry, tx, hash;
@ -553,7 +569,7 @@ Mempool.prototype.addTX = co(function* addTX(tx) {
});
/**
* Add a transaction to the mempool.
* Add a transaction to the mempool without a lock.
* @private
* @param {TX} tx
* @param {Function} callback - Returns [{@link VerifyError}].
@ -688,6 +704,13 @@ Mempool.prototype.addUnchecked = co(function* addUnchecked(entry) {
}
});
/**
* Add a transaction to the mempool without a lock.
* @private
* @param {MempoolEntry} entry
* @param {Function} callback - Returns [{@link VerifyError}].
*/
Mempool.prototype._addUnchecked = co(function* addUnchecked(entry) {
var i, resolved, tx, orphan;

View File

@ -11,6 +11,7 @@
var EventEmitter = require('events').EventEmitter;
var bcoin = require('../env');
var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var crypto = require('../crypto/crypto');
var packets = require('./packets');
var assert = utils.assert;
@ -247,7 +248,14 @@ BIP150.prototype.complete = function complete(err) {
this.callback = null;
};
BIP150.prototype.wait = function wait(timeout, callback) {
BIP150.prototype.wait = function wait(timeout) {
var self = this;
return new Promise(function(resolve, reject) {
self._wait(timeout, spawn.wrap(resolve, reject));
});
};
BIP150.prototype._wait = function wait(timeout, callback) {
var self = this;
assert(!this.auth, 'Cannot wait for init after handshake.');

View File

@ -15,6 +15,7 @@
var EventEmitter = require('events').EventEmitter;
var bcoin = require('../env');
var utils = require('../utils/utils');
var spawn = require('../utils/spawn');
var crypto = require('../crypto/crypto');
var assert = utils.assert;
var constants = bcoin.constants;
@ -455,7 +456,19 @@ BIP151.prototype.complete = function complete(err) {
* @param {Function} callback
*/
BIP151.prototype.wait = function wait(timeout, callback) {
BIP151.prototype.wait = function wait(timeout) {
var self = this;
return new Promise(function(resolve, reject) {
self._wait(timeout, spawn.wrap(resolve, reject));
});
};
/**
* Set a timeout and wait for handshake to complete.
* @private
*/
BIP151.prototype._wait = function wait(timeout, callback) {
var self = this;
assert(!this.handshake, 'Cannot wait for init after handshake.');

View File

@ -39,6 +39,7 @@ function CompactBlock(options) {
this.count = 0;
this.sipKey = null;
this.timeout = null;
this.callback = null;
if (options)
this.fromOptions(options);
@ -362,12 +363,31 @@ CompactBlock.fromBlock = function fromBlock(block, nonce) {
return new CompactBlock().fromBlock(block, nonce);
};
CompactBlock.prototype.startTimeout = function startTimeout(time, callback) {
assert(this.timeout == null);
this.timeout = setTimeout(callback, time);
CompactBlock.prototype.wait = function wait(callback) {
var self = this;
return new Promise(function(resolve, reject) {
self._wait(time, spawn.wrap(resolve, reject));
});
};
CompactBlock.prototype.stopTimeout = function stopTimeout() {
CompactBlock.prototype._wait = function wait(time, callback) {
var self = this;
assert(this.timeout == null);
this.callback = callback;
this.timeout = setTimeout(function() {
self.complete(new Error('Timed out.'));
}, time);
};
CompactBlock.prototype.complete = function complete(err) {
if (this.timeout != null) {
clearTimeout(this.timeout);
this.timeout = null;
this.callback(err);
}
};
CompactBlock.prototype.destroy = function destroy() {
if (this.timeout != null) {
clearTimeout(this.timeout);
this.timeout = null;

View File

@ -179,10 +179,6 @@ Peer.uid = 0;
Peer.prototype._init = function init() {
var self = this;
this.socket.once('connect', function() {
self._onConnect();
});
this.socket.once('error', function(err) {
self.error(err);
@ -229,11 +225,96 @@ Peer.prototype._init = function init() {
});
}
if (this.connected) {
utils.nextTick(function() {
self._onConnect();
});
this.open();
};
/**
* Create the socket and begin connecting. This method
* will use `options.createSocket` if provided.
* @param {String} host
* @param {Number} port
* @returns {net.Socket}
*/
Peer.prototype.connect = function connect(port, host) {
var self = this;
var socket, proxy, net;
assert(!this.socket);
if (this.createSocket) {
socket = this.createSocket(port, host);
} else {
if (utils.isBrowser) {
proxy = require('./proxysocket');
socket = proxy.connect(this.pool.proxyServer, port, host);
} else {
net = require('net');
socket = net.connect(port, host);
}
}
this.logger.debug('Connecting to %s.', this.hostname);
socket.once('connect', function() {
self.logger.info('Connected to %s.', self.hostname);
});
return socket;
};
/**
* Open and initialize the peer.
*/
Peer.prototype.open = co(function* open() {
try {
yield this._connect();
yield this._bip151();
yield this._bip150();
yield this._handshake();
yield this._finalize();
} catch (e) {
this.error(e);
return;
}
// Finally we can let the pool know
// that this peer is ready to go.
this.emit('open');
});
/**
* Wait for connection.
* @private
*/
Peer.prototype._connect = function _connect() {
var self = this;
if (this.connected) {
assert(!this.outbound);
return spawn.wait();
}
return new Promise(function(resolve, reject) {
self.socket.once('connect', function() {
self.ts = utils.now();
self.connected = true;
self.emit('connect');
clearTimeout(self.connectTimeout);
self.connectTimeout = null;
resolve();
});
self.connectTimeout = setTimeout(function() {
self.connectTimeout = null;
reject(new Error('Connection timed out.'));
self.ignore();
}, 10000);
});
};
/**
@ -242,96 +323,76 @@ Peer.prototype._init = function init() {
* @private
*/
Peer.prototype._onConnect = function _onConnect() {
var self = this;
this.ts = utils.now();
this.connected = true;
this.emit('connect');
if (this.connectTimeout != null) {
clearTimeout(this.connectTimeout);
this.connectTimeout = null;
}
Peer.prototype._bip151 = co(function* _bip151() {
// Send encinit. Wait for handshake to complete.
if (this.bip151) {
assert(!this.bip151.completed);
this.logger.info('Attempting BIP151 handshake (%s).', this.hostname);
this.send(this.bip151.toEncinit());
return this.bip151.wait(3000, function(err) {
if (err)
self.error(err, true);
self._onBIP151();
});
}
if (!this.bip151)
return;
this._onBIP151();
};
assert(!this.bip151.completed);
this.logger.info('Attempting BIP151 handshake (%s).', this.hostname);
this.send(this.bip151.toEncinit());
try {
yield this.bip151.wait(3000);
} catch (err) {
this.error(err, true);
}
});
/**
* Handle post bip151-handshake.
* @private
*/
Peer.prototype._onBIP151 = function _onBIP151() {
var self = this;
Peer.prototype._bip150 = co(function* _bip150() {
if (!this.bip151)
return;
if (this.bip151) {
assert(this.bip151.completed);
assert(this.bip151.completed);
if (this.bip151.handshake) {
this.logger.info('BIP151 handshake complete (%s).', this.hostname);
this.logger.info('Connection is encrypted (%s).', this.hostname);
}
if (this.bip150) {
assert(!this.bip150.completed);
if (!this.bip151.handshake)
return this.error('BIP151 handshake was not completed for BIP150.');
this.logger.info('Attempting BIP150 handshake (%s).', this.hostname);
if (this.bip150.outbound) {
if (!this.bip150.peerIdentity)
return this.error('No known identity for peer.');
this.send(this.bip150.toChallenge());
}
return this.bip150.wait(3000, function(err) {
if (err)
return self.error(err);
self._onHandshake();
});
}
if (this.bip151.handshake) {
this.logger.info('BIP151 handshake complete (%s).', this.hostname);
this.logger.info('Connection is encrypted (%s).', this.hostname);
}
this._onHandshake();
};
if (!this.bip150)
return;
assert(!this.bip150.completed);
if (!this.bip151.handshake)
throw new Error('BIP151 handshake was not completed for BIP150.');
this.logger.info('Attempting BIP150 handshake (%s).', this.hostname);
if (this.bip150.outbound) {
if (!this.bip150.peerIdentity)
return this.error('No known identity for peer.');
this.send(this.bip150.toChallenge());
}
yield this.bip150.wait(3000);
if (!this.bip150)
return;
assert(this.bip150.completed);
if (this.bip150.auth) {
this.logger.info('BIP150 handshake complete (%s).', this.hostname);
this.logger.info('Peer is authed (%s): %s.',
this.hostname, this.bip150.getAddress());
}
});
/**
* Handle post handshake.
* @private
*/
Peer.prototype._onHandshake = function _onHandshake() {
var self = this;
if (this.bip150) {
assert(this.bip150.completed);
if (this.bip150.auth) {
this.logger.info('BIP150 handshake complete (%s).', this.hostname);
this.logger.info('Peer is authed (%s): %s.',
this.hostname, this.bip150.getAddress());
}
}
this.request('verack', function(err) {
self._onAck(err);
});
Peer.prototype._handshake = co(function* _handshake() {
// Say hello.
this.sendVersion();
@ -341,32 +402,33 @@ Peer.prototype._onHandshake = function _onHandshake() {
&& this.pool.server) {
this.send(new packets.AddrPacket([this.pool.address]));
}
};
/**
* Handle `ack` event (called on verack).
* @private
*/
Peer.prototype._onAck = function _onAck(err) {
var self = this;
if (err) {
this.error(err);
return;
}
yield this.request('verack');
// Wait for _their_ version.
if (!this.version) {
this.logger.debug(
'Peer sent a verack without a version (%s).',
this.hostname);
this.request('version', this._onAck.bind(this));
return;
yield this.request('version');
assert(this.version);
}
this.ack = true;
this.logger.debug('Received verack (%s).', this.hostname);
});
/**
* Handle `ack` event (called on verack).
* @private
*/
Peer.prototype._finalize = co(function* _finalize() {
var self = this;
// Setup the ping interval.
this.pingTimeout = setInterval(function() {
self.sendPing();
@ -407,53 +469,7 @@ Peer.prototype._onAck = function _onAck(err) {
// Start syncing the chain.
this.sync();
this.logger.debug('Received verack (%s).', this.hostname);
// Finally we can let the pool know
// that this peer is ready to go.
this.emit('ack');
};
/**
* Create the socket and begin connecting. This method
* will use `options.createSocket` if provided.
* @param {String} host
* @param {Number} port
* @returns {net.Socket}
*/
Peer.prototype.connect = function connect(port, host) {
var self = this;
var socket, proxy, net;
assert(!this.socket);
if (this.createSocket) {
socket = this.createSocket(port, host);
} else {
if (utils.isBrowser) {
proxy = require('./proxysocket');
socket = proxy.connect(this.pool.proxyServer, port, host);
} else {
net = require('net');
socket = net.connect(port, host);
}
}
this.logger.debug('Connecting to %s.', this.hostname);
socket.once('connect', function() {
self.logger.info('Connected to %s.', self.hostname);
});
this.connectTimeout = setTimeout(function() {
self.error('Connection timed out.');
self.ignore();
}, 10000);
return socket;
};
});
/**
* Test whether the peer is the loader peer.
@ -604,6 +620,7 @@ Peer.prototype.sendVersion = function sendVersion() {
height: this.chain.height,
relay: this.options.relay
});
this.send(packet);
};
@ -678,7 +695,7 @@ Peer.prototype.sendFeeRate = function sendFeeRate(rate) {
*/
Peer.prototype.destroy = function destroy() {
var i, j, keys, cmd, queue;
var i, j, keys, cmd, queue, hash;
if (this.destroyed)
return;
@ -715,6 +732,13 @@ Peer.prototype.destroy = function destroy() {
queue[j].destroy();
}
keys = Object.keys(this.compactBlocks);
for (i = 0; i < keys.length; i++) {
hash = keys[i];
this.compactBlocks[hash].destroy();
}
this.emit('close');
};
@ -797,20 +821,21 @@ Peer.prototype.error = function error(err, keep) {
* Executed on timeout or once packet is received.
*/
Peer.prototype.request = function request(cmd, callback) {
var entry;
Peer.prototype.request = function request(cmd) {
var self = this;
return new Promise(function(resolve, reject) {
var entry;
if (this.destroyed)
return callback(new Error('Destroyed'));
if (self.destroyed)
return reject(new Error('Destroyed'));
entry = new RequestEntry(this, cmd, callback);
entry = new RequestEntry(self, cmd, resolve, reject);
if (!this.requestMap[cmd])
this.requestMap[cmd] = [];
if (!self.requestMap[cmd])
self.requestMap[cmd] = [];
this.requestMap[cmd].push(entry);
return entry;
self.requestMap[cmd].push(entry);
});
};
/**
@ -832,7 +857,7 @@ Peer.prototype.response = function response(cmd, payload) {
if (!entry)
return false;
res = entry.callback(null, payload, cmd);
res = entry.resolve(payload);
if (res === false)
return false;
@ -1114,11 +1139,19 @@ Peer.prototype._handleGetUTXOs = co(function* _handleGetUTXOs(packet) {
var unlock = yield this.locker.lock();
try {
return yield this.__handleGetUTXOs(packet);
} catch (e) {
this.emit('error', e);
} finally {
unlock();
}
});
/**
* Handle `getheaders` packet without lock.
* @private
* @param {GetHeadersPacket}
*/
Peer.prototype.__handleGetUTXOs = co(function* _handleGetUTXOs(packet) {
var i, utxos, prevout, hash, index, coin;
@ -1142,12 +1175,7 @@ Peer.prototype.__handleGetUTXOs = co(function* _handleGetUTXOs(packet) {
index = prevout.index;
if (this.mempool && packet.mempool) {
try {
coin = this.mempool.getCoin(hash, index);
} catch (e) {
this.emit('error', e);
return;
}
coin = this.mempool.getCoin(hash, index);
if (coin) {
utxos.hits.push(1);
@ -1161,12 +1189,7 @@ Peer.prototype.__handleGetUTXOs = co(function* _handleGetUTXOs(packet) {
}
}
try {
coin = yield this.chain.db.getCoin(hash, index);
} catch (e) {
this.emit('error', e);
return;
}
coin = yield this.chain.db.getCoin(hash, index);
if (!coin) {
utxos.hits.push(0);
@ -1203,12 +1226,20 @@ Peer.prototype._handleHaveWitness = function _handleHaveWitness(packet) {
Peer.prototype._handleGetHeaders = co(function* _handleGetHeaders(packet) {
var unlock = yield this.locker.lock();
try {
return this.__handleGetHeaders(packet);
return yield this.__handleGetHeaders(packet);
} catch (e) {
this.emit('error', e);
} finally {
unlock();
}
});
/**
* Handle `getheaders` packet without lock.
* @private
* @param {GetHeadersPacket}
*/
Peer.prototype.__handleGetHeaders = co(function* _handleGetHeaders(packet) {
var headers = [];
var hash, entry;
@ -1260,12 +1291,20 @@ Peer.prototype.__handleGetHeaders = co(function* _handleGetHeaders(packet) {
Peer.prototype._handleGetBlocks = co(function* _handleGetBlocks(packet) {
var unlock = yield this.locker.lock();
try {
return this.__handleGetBlocks(packet);
return yield this.__handleGetBlocks(packet);
} catch (e) {
this.emit('error', e);
} finally {
unlock();
}
});
/**
* Handle `getblocks` packet without lock.
* @private
* @param {GetBlocksPacket}
*/
Peer.prototype.__handleGetBlocks = co(function* _handleGetBlocks(packet) {
var blocks = [];
var hash;
@ -1310,7 +1349,7 @@ Peer.prototype.__handleGetBlocks = co(function* _handleGetBlocks(packet) {
* @param {VersionPacket}
*/
Peer.prototype._handleVersion = function _handleVersion(version) {
Peer.prototype._handleVersion = co(function* _handleVersion(version) {
var self = this;
if (!this.network.selfConnect) {
@ -1352,43 +1391,33 @@ Peer.prototype._handleVersion = function _handleVersion(version) {
}
if (this.options.witness) {
if (!version.hasWitness()) {
this.haveWitness = version.hasWitness();
if (!this.haveWitness) {
if (!this.network.oldWitness) {
this.error('Peer does not support segregated witness.');
this.ignore();
return;
}
return this.request('havewitness', function(err) {
if (err) {
self.error('Peer does not support segregated witness.');
self.ignore();
return;
}
self._finishVersion(version);
});
try {
yield this.request('havewitness');
} catch (err) {
this.error('Peer does not support segregated witness.');
this.ignore();
return;
}
this.haveWitness = true;
}
}
this._finishVersion(version);
};
/**
* Finish handling `version` packet.
* @private
* @param {VersionPacket}
*/
Peer.prototype._finishVersion = function _finishVersion(version) {
if (version.hasWitness())
this.haveWitness = true;
if (!version.relay)
this.relay = false;
this.relay = version.relay;
this.version = version;
this.send(new packets.VerackPacket());
this.version = version;
this.fire('version', version);
};
});
/**
* Handle `verack` packet.
@ -1407,7 +1436,6 @@ Peer.prototype._handleVerack = function _handleVerack(packet) {
*/
Peer.prototype._handleMempool = function _handleMempool(packet) {
var self = this;
var items = [];
var i, hashes;
@ -1425,9 +1453,9 @@ Peer.prototype._handleMempool = function _handleMempool(packet) {
for (i = 0; i < hashes.length; i++)
items.push(new InvItem(constants.inv.TX, hashes[i]));
self.logger.debug('Sending mempool snapshot (%s).', self.hostname);
this.logger.debug('Sending mempool snapshot (%s).', this.hostname);
self.sendInv(items);
this.sendInv(items);
};
/**
@ -1490,11 +1518,19 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
var unlock = yield this.locker.lock();
try {
return yield this.__handleGetData(packet);
} catch (e) {
this.emit('error', e);
} finally {
unlock();
}
});
/**
* Handle `getdata` packet without lock.
* @private
* @param {GetDataPacket}
*/
Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
var notFound = [];
var items = packet.items;
@ -2032,8 +2068,7 @@ Peer.prototype._handleSendCmpct = function _handleSendCmpct(packet) {
* @param {CmpctBlockPacket}
*/
Peer.prototype._handleCmpctBlock = function _handleCmpctBlock(packet) {
var self = this;
Peer.prototype._handleCmpctBlock = co(function* _handleCmpctBlock(packet) {
var block = packet.block;
var hash = block.hash('hex');
var result;
@ -2075,13 +2110,16 @@ Peer.prototype._handleCmpctBlock = function _handleCmpctBlock(packet) {
'Received semi-full compact block %s (%s).',
block.rhash, this.hostname);
block.startTimeout(10000, function() {
self.logger.debug(
try {
yield block.wait(10000);
} catch (e) {
this.logger.debug(
'Compact block timed out: %s (%s).',
block.rhash, self.hostname);
delete self.compactBlocks[hash];
});
};
block.rhash, this.hostname);
delete this.compactBlocks[hash];
}
});
/**
* Handle `getblocktxn` packet.
@ -2089,18 +2127,9 @@ Peer.prototype._handleCmpctBlock = function _handleCmpctBlock(packet) {
* @param {GetBlockTxnPacket}
*/
Peer.prototype._handleGetBlockTxn = function _handleGetBlockTxn(packet) {
var self = this;
Peer.prototype._handleGetBlockTxn = co(function* _handleGetBlockTxn(packet) {
var req = packet.request;
var res, item;
function done(err) {
if (err) {
self.emit('error', err);
return;
}
self.fire('blocktxn', req);
}
var res, item, block;
if (this.chain.db.options.spv)
return done();
@ -2113,32 +2142,28 @@ Peer.prototype._handleGetBlockTxn = function _handleGetBlockTxn(packet) {
item = new InvItem(constants.inv.BLOCK, req.hash);
this._getItem(item, function(err, block) {
if (err)
return done(err);
block = yield this._getItem(item);
if (!block) {
self.logger.debug(
'Peer sent getblocktxn for non-existent block (%s).',
self.hostname);
self.setMisbehavior(100);
return done();
}
if (!block) {
this.logger.debug(
'Peer sent getblocktxn for non-existent block (%s).',
this.hostname);
this.setMisbehavior(100);
return;
}
if (block.height < self.chain.tip.height - 15) {
self.logger.debug(
'Peer sent a getblocktxn for a block > 15 deep (%s)',
self.hostname);
return done();
}
if (block.height < this.chain.tip.height - 15) {
this.logger.debug(
'Peer sent a getblocktxn for a block > 15 deep (%s)',
this.hostname);
return;
}
res = bcoin.bip152.TXResponse.fromBlock(block, req);
res = bcoin.bip152.TXResponse.fromBlock(block, req);
self.send(new packets.BlockTxnPacket(res, false));
done();
});
};
this.send(new packets.BlockTxnPacket(res, false));
this.fire('blocktxn', req);
});
/**
* Handle `blocktxn` packet.
@ -2155,9 +2180,8 @@ Peer.prototype._handleBlockTxn = function _handleBlockTxn(packet) {
return;
}
this.fire('getblocktxn', res);
block.complete();
block.stopTimeout();
delete this.compactBlocks[res.hash];
if (!block.fillMissing(res)) {
@ -2171,6 +2195,7 @@ Peer.prototype._handleBlockTxn = function _handleBlockTxn(packet) {
block.rhash, this.hostname);
this.fire('block', block.toBlock());
this.fire('getblocktxn', res);
};
/**
@ -2461,10 +2486,11 @@ Peer.prototype.inspect = function inspect() {
* @constructor
*/
function RequestEntry(peer, cmd, callback) {
function RequestEntry(peer, cmd, resolve, reject) {
this.peer = peer;
this.cmd = cmd;
this.callback = callback;
this.resolve = resolve;
this.reject = reject;
this.id = peer.uid++;
this.onTimeout = this._onTimeout.bind(this);
this.timeout = setTimeout(this.onTimeout, this.peer.requestTimeout);
@ -2479,7 +2505,7 @@ RequestEntry.prototype._onTimeout = function _onTimeout() {
if (utils.binaryRemove(queue, this, compare)) {
if (queue.length === 0)
delete this.peer.requestMap[this.cmd];
this.callback(new Error('Timed out: ' + this.cmd));
this.reject(new Error('Timed out: ' + this.cmd));
}
};

View File

@ -1006,7 +1006,7 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
var self = this;
var peer = new bcoin.peer(this, addr, socket);
peer.once('ack', function() {
peer.once('open', function() {
if (!peer.outbound)
return;

View File

@ -384,7 +384,7 @@ TXDB.prototype.getInfo = function getInfo(tx) {
* @param {Function} callback - Returns [Error, Buffer].
*/
TXDB.prototype._addOrphan = co(function* _addOrphan(prevout, spender) {
TXDB.prototype.addOrphan = co(function* addOrphan(prevout, spender) {
var p = new BufferWriter();
var key = layout.o(prevout.hash, prevout.index);
var data = yield this.get(key);
@ -405,7 +405,7 @@ TXDB.prototype._addOrphan = co(function* _addOrphan(prevout, spender) {
* @param {Function} callback - Returns [Error, {@link Orphan}].
*/
TXDB.prototype._getOrphans = co(function* _getOrphans(hash, index) {
TXDB.prototype.getOrphans = co(function* getOrphans(hash, index) {
var items = [];
var i, data, orphans, orphan, tx, p;
@ -438,7 +438,7 @@ TXDB.prototype._getOrphans = co(function* _getOrphans(hash, index) {
* @param {Function} callback - Returns [Error].
*/
TXDB.prototype._verify = co(function* _verify(tx, info) {
TXDB.prototype.verify = co(function* verify(tx, info) {
var i, input, prevout, address, coin, spent, rtx, rinfo, result;
for (i = 0; i < tx.inputs.length; i++) {
@ -494,7 +494,7 @@ TXDB.prototype._verify = co(function* _verify(tx, info) {
this.logger.warning('Removing conflicting tx: %s.',
utils.revHex(spent.hash));
result = yield this._removeConflict(spent.hash, tx);
result = yield this.removeConflict(spent.hash, tx);
// Spender was not removed, the current
// transaction is not elligible to be added.
@ -519,11 +519,11 @@ TXDB.prototype._verify = co(function* _verify(tx, info) {
* @param {Function} callback
*/
TXDB.prototype._resolveOrphans = co(function* _resolveOrphans(tx, index) {
TXDB.prototype.resolveOrphans = co(function* resolveOrphans(tx, index) {
var hash = tx.hash('hex');
var i, orphans, coin, item, input, orphan;
orphans = yield this._getOrphans(hash, index);
orphans = yield this.getOrphans(hash, index);
if (!orphans)
return false;
@ -554,7 +554,7 @@ TXDB.prototype._resolveOrphans = co(function* _resolveOrphans(tx, index) {
return true;
}
yield this._lazyRemove(orphan);
yield this.lazyRemove(orphan);
}
// Just going to be added again outside.
@ -564,9 +564,7 @@ TXDB.prototype._resolveOrphans = co(function* _resolveOrphans(tx, index) {
});
/**
* Add transaction, runs _confirm (separate batch) and
* verify (separate batch for double spenders).
* @private
* Add transaction, runs `confirm()` and `verify()`.
* @param {TX} tx
* @param {PathInfo} info
* @param {Function} callback
@ -575,13 +573,21 @@ TXDB.prototype._resolveOrphans = co(function* _resolveOrphans(tx, index) {
TXDB.prototype.add = co(function* add(tx, info) {
var unlock = yield this.locker.lock();
try {
return yield this.addl(tx, info);
return yield this._add(tx, info);
} finally {
unlock();
}
});
TXDB.prototype.addl = co(function* add(tx, info) {
/**
* Add transaction without a lock.
* @private
* @param {TX} tx
* @param {PathInfo} info
* @param {Function} callback
*/
TXDB.prototype._add = co(function* add(tx, info) {
var hash, path, account;
var i, result, input, output, coin;
var prevout, key, address, spender, orphans;
@ -589,14 +595,14 @@ TXDB.prototype.addl = co(function* add(tx, info) {
assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
// Attempt to confirm tx before adding it.
result = yield this._confirm(tx, info);
result = yield this.confirm(tx, info);
// Ignore if we already have this tx.
if (result)
return true;
// Verify and get coins.
result = yield this._verify(tx, info);
result = yield this.verify(tx, info);
if (!result)
return false;
@ -647,7 +653,7 @@ TXDB.prototype.addl = co(function* add(tx, info) {
// Add orphan, if no parent transaction is yet known
if (!input.coin) {
try {
yield this._addOrphan(prevout, spender);
yield this.addOrphan(prevout, spender);
} catch (e) {
this.drop();
throw e;
@ -676,7 +682,7 @@ TXDB.prototype.addl = co(function* add(tx, info) {
continue;
try {
orphans = yield this._resolveOrphans(tx, i);
orphans = yield this.resolveOrphans(tx, i);
} catch (e) {
this.drop();
throw e;
@ -720,7 +726,7 @@ TXDB.prototype.addl = co(function* add(tx, info) {
* @param {Function} callback - Returns [Error, Boolean].
*/
TXDB.prototype._removeConflict = co(function* _removeConflict(hash, ref) {
TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) {
var tx = yield this.getTX(hash);
var info;
@ -749,7 +755,7 @@ TXDB.prototype._removeConflict = co(function* _removeConflict(hash, ref) {
return;
}
info = yield this._removeRecursive(tx);
info = yield this.removeRecursive(tx);
return [tx, info];
});
@ -762,7 +768,7 @@ TXDB.prototype._removeConflict = co(function* _removeConflict(hash, ref) {
* @param {Function} callback - Returns [Error, Boolean].
*/
TXDB.prototype._removeRecursive = co(function* _removeRecursive(tx) {
TXDB.prototype.removeRecursive = co(function* removeRecursive(tx) {
var hash = tx.hash('hex');
var i, spent, stx, info;
@ -777,14 +783,14 @@ TXDB.prototype._removeRecursive = co(function* _removeRecursive(tx) {
if (!stx)
throw new Error('Could not find spender.');
yield this._removeRecursive(stx);
yield this.removeRecursive(stx);
}
this.start();
// Remove the spender.
try {
info = yield this._lazyRemove(tx);
info = yield this.lazyRemove(tx);
} catch (e) {
this.drop();
throw e;
@ -848,7 +854,7 @@ TXDB.prototype.isSpent = co(function* isSpent(hash, index) {
* transaction was confirmed, or should be ignored.
*/
TXDB.prototype._confirm = co(function* _confirm(tx, info) {
TXDB.prototype.confirm = co(function* confirm(tx, info) {
var hash = tx.hash('hex');
var i, account, existing, output, coin;
var address, key;
@ -945,20 +951,27 @@ TXDB.prototype._confirm = co(function* _confirm(tx, info) {
TXDB.prototype.remove = co(function* remove(hash) {
var unlock = yield this.locker.lock();
try {
return yield this.removel(hash);
return yield this._remove(hash);
} finally {
unlock();
}
});
TXDB.prototype.removel = co(function* remove(hash) {
/**
* Remove a transaction without a lock.
* @private
* @param {Hash} hash
* @param {Function} callback - Returns [Error].
*/
TXDB.prototype._remove = co(function* remove(hash) {
var tx = yield this.getTX(hash);
var info;
if (!tx)
return;
info = yield this._removeRecursive(tx);
info = yield this.removeRecursive(tx);
if (!info)
return;
@ -974,12 +987,12 @@ TXDB.prototype.removel = co(function* remove(hash) {
* @param {Function} callback - Returns [Error].
*/
TXDB.prototype._lazyRemove = co(function* lazyRemove(tx) {
TXDB.prototype.lazyRemove = co(function* lazyRemove(tx) {
var info = yield this.getInfo(tx);
if (!info)
return;
return yield this._remove(tx, info);
return yield this.__remove(tx, info);
});
/**
@ -990,7 +1003,7 @@ TXDB.prototype._lazyRemove = co(function* lazyRemove(tx) {
* @param {Function} callback - Returns [Error].
*/
TXDB.prototype._remove = co(function* remove(tx, info) {
TXDB.prototype.__remove = co(function* remove(tx, info) {
var hash = tx.hash('hex');
var i, path, account, key, prevout;
var address, input, output, coin;
@ -1080,13 +1093,20 @@ TXDB.prototype._remove = co(function* remove(tx, info) {
TXDB.prototype.unconfirm = co(function* unconfirm(hash) {
var unlock = yield this.locker.lock();
try {
return yield this.unconfirml(hash);
return yield this._unconfirm(hash);
} finally {
unlock();
}
});
TXDB.prototype.unconfirml = co(function* unconfirm(hash) {
/**
* Unconfirm a transaction without a lock.
* @private
* @param {Hash} hash
* @param {Function} callback
*/
TXDB.prototype._unconfirm = co(function* unconfirm(hash) {
var tx = yield this.getTX(hash);
var info, result;
@ -1101,7 +1121,7 @@ TXDB.prototype.unconfirml = co(function* unconfirm(hash) {
this.start();
try {
result = yield this._unconfirm(tx, info);
result = yield this.__unconfirm(tx, info);
} catch (e) {
this.drop();
throw e;
@ -1119,7 +1139,7 @@ TXDB.prototype.unconfirml = co(function* unconfirm(hash) {
* @param {Function} callback
*/
TXDB.prototype._unconfirm = co(function* unconfirm(tx, info) {
TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
var hash = tx.hash('hex');
var height = tx.height;
var i, account, output, key, coin;
@ -1613,8 +1633,10 @@ TXDB.prototype.getAccountCoins = co(function* getCoins(account) {
for (i = 0; i < hashes.length; i++) {
key = hashes[i];
coin = yield this.getCoin(key[0], key[1]);
if (!coin)
continue;
coins.push(coin);
}
@ -1921,6 +1943,7 @@ TXDB.prototype.getAccountBalance = co(function* getBalance(account) {
});
/**
* Zap pending transactions older than `age`.
* @param {Number?} account
* @param {Number} age - Age delta (delete transactions older than `now - age`).
* @param {Function} callback
@ -1935,6 +1958,14 @@ TXDB.prototype.zap = co(function* zap(account, age) {
}
});
/**
* Zap pending transactions without a lock.
* @private
* @param {Number?} account
* @param {Number} age
* @param {Function} callback
*/
TXDB.prototype._zap = co(function* zap(account, age) {
var i, txs, tx, hash;
@ -1953,7 +1984,7 @@ TXDB.prototype._zap = co(function* zap(account, age) {
if (tx.ts !== 0)
continue;
yield this.removel(hash);
yield this._remove(hash);
}
});

View File

@ -236,6 +236,13 @@ Wallet.prototype.addKey = co(function* addKey(account, key) {
}
});
/**
* Add a public account key to the wallet without a lock.
* @private
* @param {HDPublicKey} key
* @param {Function} callback
*/
Wallet.prototype._addKey = co(function* addKey(account, key) {
var result;
@ -255,7 +262,7 @@ Wallet.prototype._addKey = co(function* addKey(account, key) {
this.start();
try {
result = yield account.addKey(key, true);
result = yield account.addKey(key);
} catch (e) {
this.drop();
throw e;
@ -268,7 +275,6 @@ Wallet.prototype._addKey = co(function* addKey(account, key) {
/**
* Remove a public account key from the wallet (multisig).
* Remove the key from the wallet database.
* @param {HDPublicKey} key
* @param {Function} callback
*/
@ -282,6 +288,13 @@ Wallet.prototype.removeKey = co(function* removeKey(account, key) {
}
});
/**
* Remove a public account key from the wallet (multisig).
* @private
* @param {HDPublicKey} key
* @param {Function} callback
*/
Wallet.prototype._removeKey = co(function* removeKey(account, key) {
var result;
@ -301,7 +314,7 @@ Wallet.prototype._removeKey = co(function* removeKey(account, key) {
this.start();
try {
result = yield account.removeKey(key, true);
result = yield account.removeKey(key);
} catch (e) {
this.drop();
throw e;
@ -328,6 +341,14 @@ Wallet.prototype.setPassphrase = co(function* setPassphrase(old, new_) {
}
});
/**
* Change or set master key's passphrase without a lock.
* @private
* @param {(String|Buffer)?} old
* @param {String|Buffer} new_
* @param {Function} callback
*/
Wallet.prototype._setPassphrase = co(function* setPassphrase(old, new_) {
if (new_ == null) {
new_ = old;
@ -361,6 +382,13 @@ Wallet.prototype.retoken = co(function* retoken(passphrase) {
}
});
/**
* Generate a new token without a lock.
* @private
* @param {(String|Buffer)?} passphrase
* @param {Function} callback
*/
Wallet.prototype._retoken = co(function* retoken(passphrase) {
var master = yield this.unlock(passphrase);
@ -463,6 +491,12 @@ Wallet.prototype.createAccount = co(function* createAccount(options) {
}
});
/**
* Create an account without a lock.
* @param {Object} options - See {@link Account} options.
* @param {Function} callback - Returns [Error, {@link Account}].
*/
Wallet.prototype._createAccount = co(function* createAccount(options) {
var passphrase = options.passphrase;
var timeout = options.timeout;
@ -618,6 +652,13 @@ Wallet.prototype.createAddress = co(function* createAddress(account, change) {
}
});
/**
* Create a new address (increments depth) without a lock.
* @param {(Number|String)?} account
* @param {Boolean} change
* @param {Function} callback - Returns [Error, {@link KeyRing}].
*/
Wallet.prototype._createAddress = co(function* createAddress(account, change) {
var result;
@ -763,6 +804,15 @@ Wallet.prototype.importKey = co(function* importKey(account, ring, passphrase) {
}
});
/**
* Import a keyring (will not exist on derivation chain) without a lock.
* @private
* @param {(String|Number)?} account
* @param {KeyRing} ring
* @param {(String|Buffer)?} passphrase
* @param {Function} callback
*/
Wallet.prototype._importKey = co(function* importKey(account, ring, passphrase) {
var exists, raw, path;
@ -847,7 +897,14 @@ Wallet.prototype.fund = co(function* fund(tx, options, force) {
}
});
Wallet.prototype._fund = co(function* fund(tx, options, force) {
/**
* Fill a transaction with inputs without a lock.
* @private
* @see MTX#selectCoins
* @see MTX#fill
*/
Wallet.prototype._fund = co(function* fund(tx, options) {
var rate, account, coins;
if (!options)
@ -968,6 +1025,14 @@ Wallet.prototype.send = co(function* send(options) {
}
});
/**
* Build and send a transaction without a lock.
* @private
* @param {Object} options - See {@link Wallet#fund options}.
* @param {Object[]} options.outputs - See {@link MTX#addOutput}.
* @param {Function} callback - Returns [Error, {@link TX}].
*/
Wallet.prototype._send = co(function* send(options) {
var tx = yield this.createTX(options, true);
@ -1144,6 +1209,13 @@ Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) {
}
});
/**
* Sync address depths without a lock.
* @private
* @param {PathInfo} info
* @param {Function} callback - Returns [Error, Boolean]
*/
Wallet.prototype._syncOutputDepth = co(function* syncOutputDepth(info) {
var receive = [];
var accounts = {};
@ -1300,7 +1372,7 @@ Wallet.prototype.template = co(function* template(tx) {
*/
Wallet.prototype.sign = co(function* sign(tx, options) {
var passphrase, timeout, master, rings;
var master, rings;
if (!options)
options = {};
@ -1308,10 +1380,8 @@ Wallet.prototype.sign = co(function* sign(tx, options) {
if (typeof options === 'string' || Buffer.isBuffer(options))
options = { passphrase: options };
passphrase = options.passphrase;
timeout = options.timeout;
master = yield this.unlock(options.passphrase, options.timeout);
master = yield this.unlock(passphrase, timeout);
rings = yield this.deriveInputs(tx);
return yield this.signAsync(rings, tx);
@ -1991,6 +2061,14 @@ MasterKey.prototype.unlock = co(function* _unlock(passphrase, timeout) {
}
});
/**
* Decrypt the key without a lock.
* @private
* @param {Buffer|String} passphrase - Zero this yourself.
* @param {Number} [timeout=60000] timeout in ms.
* @param {Function} callback - Returns [Error, {@link HDPrivateKey}].
*/
MasterKey.prototype._unlock = co(function* _unlock(passphrase, timeout) {
var data, key;
@ -2046,6 +2124,13 @@ MasterKey.prototype.stop = function stop() {
}
};
/**
* Encrypt data with in-memory aes key.
* @param {Buffer} data
* @param {Buffer} iv
* @returns {Buffer}
*/
MasterKey.prototype.encipher = function encipher(data, iv) {
if (!this.aesKey)
return;
@ -2056,6 +2141,13 @@ MasterKey.prototype.encipher = function encipher(data, iv) {
return crypto.encipher(data, this.aesKey, iv.slice(0, 16));
};
/**
* Decrypt data with in-memory aes key.
* @param {Buffer} data
* @param {Buffer} iv
* @returns {Buffer}
*/
MasterKey.prototype.decipher = function decipher(data, iv) {
if (!this.aesKey)
return;
@ -2107,6 +2199,13 @@ MasterKey.prototype.decrypt = co(function* decrypt(passphrase) {
}
});
/**
* Decrypt the key permanently without a lock.
* @private
* @param {Buffer|String} passphrase - Zero this yourself.
* @param {Function} callback
*/
MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
var data;
@ -2143,6 +2242,13 @@ MasterKey.prototype.encrypt = co(function* encrypt(passphrase) {
}
});
/**
* Encrypt the key permanently without a lock.
* @private
* @param {Buffer|String} passphrase - Zero this yourself.
* @param {Function} callback
*/
MasterKey.prototype._encrypt = co(function* encrypt(passphrase) {
var data, iv;

View File

@ -471,6 +471,13 @@ WalletDB.prototype.get = co(function* get(wid) {
}
});
/**
* Get a wallet from the database without a lock.
* @private
* @param {WalletID} wid
* @param {Function} callback - Returns [Error, {@link Wallet}].
*/
WalletDB.prototype._get = co(function* get(wid) {
var data = yield this.db.get(layout.w(wid));
var wallet;
@ -490,7 +497,6 @@ WalletDB.prototype._get = co(function* get(wid) {
/**
* Save a wallet to the database.
* @param {Wallet} wallet
* @param {Function} callback
*/
WalletDB.prototype.save = function save(wallet) {
@ -548,6 +554,13 @@ WalletDB.prototype.create = co(function* create(options) {
}
});
/**
* Create a new wallet, save to database without a lock.
* @private
* @param {Object} options - See {@link Wallet}.
* @param {Function} callback - Returns [Error, {@link Wallet}].
*/
WalletDB.prototype._create = co(function* create(options) {
var exists = yield this.has(options.id);
var wallet;
@ -611,11 +624,12 @@ WalletDB.prototype.getAccount = co(function* getAccount(wid, name) {
return;
yield account.open();
return account;
});
/**
* Get an account from the database. Do not setup watcher.
* Get an account from the database by wid.
* @private
* @param {WalletID} wid
* @param {Number} index - Account index.
@ -942,6 +956,7 @@ WalletDB.prototype.getWallets = function getWallets() {
/**
* Rescan the blockchain.
* @param {ChainDB} chaindb
* @param {Number} height
* @param {Function} callback
*/
@ -954,6 +969,14 @@ WalletDB.prototype.rescan = co(function* rescan(chaindb, height) {
}
});
/**
* Rescan the blockchain without a lock.
* @private
* @param {ChainDB} chaindb
* @param {Number} height
* @param {Function} callback
*/
WalletDB.prototype._rescan = co(function* rescan(chaindb, height) {
var self = this;
var hashes;
@ -1241,6 +1264,13 @@ WalletDB.prototype.addBlock = co(function* addBlock(entry, txs) {
}
});
/**
* Add a block's transactions without a lock.
* @private
* @param {ChainEntry} entry
* @param {Function} callback
*/
WalletDB.prototype._addBlock = co(function* addBlock(entry, txs) {
var i, block, matches, hash, tx, wallets;
@ -1299,6 +1329,13 @@ WalletDB.prototype.removeBlock = co(function* removeBlock(entry) {
}
});
/**
* Unconfirm a block's transactions.
* @private
* @param {ChainEntry} entry
* @param {Function} callback
*/
WalletDB.prototype._removeBlock = co(function* removeBlock(entry) {
var block = WalletBlock.fromEntry(entry);
var i, j, data, hash, wallets, wid, wallet;
@ -1361,6 +1398,13 @@ WalletDB.prototype.addTX = co(function* addTX(tx, force) {
}
});
/**
* Add a transaction to the database without a lock.
* @private
* @param {TX} tx
* @param {Function} callback - Returns [Error].
*/
WalletDB.prototype._addTX = co(function* addTX(tx, force) {
var i, wallets, info, wallet;