net: better locks.
This commit is contained in:
parent
05353e0e1a
commit
9c352c5d2b
@ -1117,25 +1117,6 @@ Chain.prototype._resetTime = co(function* resetTime(ts) {
|
|||||||
yield this._reset(entry.height, false);
|
yield this._reset(entry.height, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for the chain to drain (finish processing
|
|
||||||
* all of the blocks in its queue).
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Chain.prototype.onDrain = function onDrain() {
|
|
||||||
return this.locker.wait();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether the chain is in the process of adding blocks.
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Chain.prototype.isBusy = function isBusy() {
|
|
||||||
return this.locker.isBusy();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a block to the chain, perform all necessary verification.
|
* Add a block to the chain, perform all necessary verification.
|
||||||
* @param {Block} block
|
* @param {Block} block
|
||||||
|
|||||||
121
lib/net/peer.js
121
lib/net/peer.js
@ -136,9 +136,8 @@ function Peer(pool) {
|
|||||||
this.invFilter = new Bloom.Rolling(50000, 0.000001);
|
this.invFilter = new Bloom.Rolling(50000, 0.000001);
|
||||||
|
|
||||||
this.requestMap = new Map();
|
this.requestMap = new Map();
|
||||||
this.blockQueue = [];
|
|
||||||
this.compactBlocks = new Map();
|
|
||||||
this.responseMap = new Map();
|
this.responseMap = new Map();
|
||||||
|
this.compactBlocks = new Map();
|
||||||
|
|
||||||
if (this.options.bip151) {
|
if (this.options.bip151) {
|
||||||
this.bip151 = new BIP151();
|
this.bip151 = new BIP151();
|
||||||
@ -263,7 +262,7 @@ Peer.prototype._init = function init() {
|
|||||||
|
|
||||||
this.parser.on('packet', co(function* (packet) {
|
this.parser.on('packet', co(function* (packet) {
|
||||||
try {
|
try {
|
||||||
yield self.handlePacket(packet);
|
yield self.readPacket(packet);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
self.error(e);
|
self.error(e);
|
||||||
self.destroy();
|
self.destroy();
|
||||||
@ -1402,28 +1401,14 @@ Peer.prototype.getTX = function getTX(hashes) {
|
|||||||
* @param {Packet} packet
|
* @param {Packet} packet
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Peer.prototype.handlePacket = co(function* handlePacket(packet) {
|
Peer.prototype.readPacket = co(function* readPacket(packet) {
|
||||||
var unlock;
|
var unlock = yield this.locker.lock();
|
||||||
|
try {
|
||||||
// We stop reads and lock the peer for any
|
this.socket.pause();
|
||||||
// packet with significant IO/asynchronocity.
|
return yield this.handlePacket(packet);
|
||||||
switch (packet.type) {
|
} finally {
|
||||||
case packetTypes.GETDATA:
|
this.socket.resume();
|
||||||
case packetTypes.GETBLOCKS:
|
unlock();
|
||||||
case packetTypes.GETHEADERS:
|
|
||||||
case packetTypes.GETUTXOS:
|
|
||||||
case packetTypes.GETBLOCKTXN:
|
|
||||||
unlock = yield this.locker.lock();
|
|
||||||
try {
|
|
||||||
this.socket.pause();
|
|
||||||
return yield this.onPacket(packet);
|
|
||||||
} finally {
|
|
||||||
this.socket.resume();
|
|
||||||
unlock();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return yield this.onPacket(packet);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1433,7 +1418,7 @@ Peer.prototype.handlePacket = co(function* handlePacket(packet) {
|
|||||||
* @param {Packet} packet
|
* @param {Packet} packet
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Peer.prototype.onPacket = co(function* onPacket(packet) {
|
Peer.prototype.handlePacket = co(function* handlePacket(packet) {
|
||||||
var entry;
|
var entry;
|
||||||
|
|
||||||
if (this.destroyed)
|
if (this.destroyed)
|
||||||
@ -1573,19 +1558,78 @@ Peer.prototype.onPacket = co(function* onPacket(packet) {
|
|||||||
entry.resolve(packet);
|
entry.resolve(packet);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a block.
|
||||||
|
* @param {Block} block
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Peer.prototype.emitBlock = co(function* emitBlock(block) {
|
||||||
|
this.emit('block', block);
|
||||||
|
yield this.pool.handleBlock(this, block);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a block inv.
|
||||||
|
* @param {Hash[]} blocks
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Peer.prototype.emitBlockInv = co(function* emitBlockInv(blocks) {
|
||||||
|
this.emit('blocks', blocks);
|
||||||
|
yield this.pool.handleBlockInv(this, blocks);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit headers.
|
||||||
|
* @param {Header[]} headers
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Peer.prototype.emitHeaders = co(function* emitHeaders(headers) {
|
||||||
|
this.emit('headers', headers);
|
||||||
|
yield this.pool.handleHeaders(this, headers);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flush merkle block once all matched
|
* Flush merkle block once all matched
|
||||||
* txs have been received.
|
* txs have been received.
|
||||||
* @private
|
* @private
|
||||||
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Peer.prototype.flushMerkle = function flushMerkle() {
|
Peer.prototype.flushMerkle = co(function* flushMerkle() {
|
||||||
assert(this.lastMerkle);
|
assert(this.lastMerkle);
|
||||||
|
|
||||||
this.lastBlock = util.ms();
|
this.lastBlock = util.ms();
|
||||||
this.emit('merkleblock', this.lastMerkle);
|
|
||||||
|
yield this.emitBlock(this.lastMerkle);
|
||||||
|
|
||||||
this.lastMerkle = null;
|
this.lastMerkle = null;
|
||||||
this.waitingTX = 0;
|
this.waitingTX = 0;
|
||||||
};
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a transaction.
|
||||||
|
* @param {TX} tx
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Peer.prototype.emitTX = co(function* emitTX(tx) {
|
||||||
|
this.emit('tx', tx);
|
||||||
|
yield this.pool.handleTX(this, tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit transaction inv.
|
||||||
|
* @param {Hash[]} txs
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Peer.prototype.emitTXInv = co(function* emitTXInv(txs) {
|
||||||
|
this.emit('txs', txs);
|
||||||
|
yield this.pool.handleTXInv(this, txs);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle `filterload` packet.
|
* Handle `filterload` packet.
|
||||||
@ -1675,7 +1719,7 @@ Peer.prototype.handleMerkleBlock = co(function* handleMerkleBlock(packet) {
|
|||||||
this.waitingTX = block.matches.length;
|
this.waitingTX = block.matches.length;
|
||||||
|
|
||||||
if (this.waitingTX === 0)
|
if (this.waitingTX === 0)
|
||||||
this.flushMerkle();
|
yield this.flushMerkle();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2400,10 +2444,10 @@ Peer.prototype.handleInv = co(function* handleInv(packet) {
|
|||||||
this.emit('inv', items);
|
this.emit('inv', items);
|
||||||
|
|
||||||
if (blocks.length > 0)
|
if (blocks.length > 0)
|
||||||
this.emit('blocks', blocks);
|
yield this.emitBlockInv(blocks);
|
||||||
|
|
||||||
if (txs.length > 0)
|
if (txs.length > 0)
|
||||||
this.emit('txs', txs);
|
yield this.emitTXInv(txs);
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
'Received inv packet with %d items: blocks=%d txs=%d (%s).',
|
'Received inv packet with %d items: blocks=%d txs=%d (%s).',
|
||||||
@ -2434,7 +2478,7 @@ Peer.prototype.handleHeaders = co(function* handleHeaders(packet) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('headers', headers);
|
yield this.emitHeaders(headers);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2464,7 +2508,7 @@ Peer.prototype.handleBlock = co(function* handleBlock(packet) {
|
|||||||
|
|
||||||
this.lastBlock = util.ms();
|
this.lastBlock = util.ms();
|
||||||
|
|
||||||
this.emit('block', packet.block);
|
yield this.emitBlock(packet.block);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2481,12 +2525,12 @@ Peer.prototype.handleTX = co(function* handleTX(packet) {
|
|||||||
if (this.lastMerkle.hasTX(tx)) {
|
if (this.lastMerkle.hasTX(tx)) {
|
||||||
this.lastMerkle.addTX(tx);
|
this.lastMerkle.addTX(tx);
|
||||||
if (--this.waitingTX === 0)
|
if (--this.waitingTX === 0)
|
||||||
this.flushMerkle();
|
yield this.flushMerkle();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('tx', tx);
|
yield this.emitTX(tx);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2700,10 +2744,10 @@ Peer.prototype.handleCmpctBlock = co(function* handleCmpctBlock(packet) {
|
|||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
this.lastBlock = util.ms();
|
this.lastBlock = util.ms();
|
||||||
this.emit('block', block.toBlock());
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
'Received full compact block %s (%s).',
|
'Received full compact block %s (%s).',
|
||||||
block.rhash(), this.hostname);
|
block.rhash(), this.hostname);
|
||||||
|
yield this.emitBlock(block.toBlock());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2799,8 +2843,9 @@ Peer.prototype.handleBlockTxn = co(function* handleBlockTxn(packet) {
|
|||||||
|
|
||||||
this.lastBlock = util.ms();
|
this.lastBlock = util.ms();
|
||||||
|
|
||||||
this.emit('block', block.toBlock());
|
|
||||||
this.emit('getblocktxn', res);
|
this.emit('getblocktxn', res);
|
||||||
|
|
||||||
|
yield this.emitBlock(block.toBlock());
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
385
lib/net/pool.js
385
lib/net/pool.js
@ -99,7 +99,7 @@ function Pool(options) {
|
|||||||
this.chain = this.options.chain;
|
this.chain = this.options.chain;
|
||||||
this.mempool = this.options.mempool;
|
this.mempool = this.options.mempool;
|
||||||
this.server = this.options.createServer();
|
this.server = this.options.createServer();
|
||||||
this.locker = new Lock();
|
this.locker = new Lock(true);
|
||||||
|
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.syncing = false;
|
this.syncing = false;
|
||||||
@ -108,7 +108,6 @@ function Pool(options) {
|
|||||||
this.spvFilter = null;
|
this.spvFilter = null;
|
||||||
this.txFilter = null;
|
this.txFilter = null;
|
||||||
this.requestMap = new Map();
|
this.requestMap = new Map();
|
||||||
this.queueMap = new Map();
|
|
||||||
this.invMap = new Map();
|
this.invMap = new Map();
|
||||||
this.scheduled = false;
|
this.scheduled = false;
|
||||||
this.pendingWatch = null;
|
this.pendingWatch = null;
|
||||||
@ -445,19 +444,11 @@ Pool.prototype.addLoader = function addLoader() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr = this.getHost(false);
|
addr = this.getHost();
|
||||||
|
|
||||||
if (!addr)
|
if (!addr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
peer = this.peers.get(addr.hostname);
|
|
||||||
|
|
||||||
if (peer) {
|
|
||||||
this.logger.info('Repurposing peer for loader (%s).', peer.hostname);
|
|
||||||
this.setLoader(peer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
peer = this.createPeer(addr);
|
peer = this.createPeer(addr);
|
||||||
|
|
||||||
this.logger.info('Setting loader peer (%s).', peer.hostname);
|
this.logger.info('Setting loader peer (%s).', peer.hostname);
|
||||||
@ -661,56 +652,6 @@ Pool.prototype.bindPeer = function bindPeer(peer) {
|
|||||||
self.handleAddr(peer, addrs);
|
self.handleAddr(peer, addrs);
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('merkleblock', co(function* (block) {
|
|
||||||
if (!self.options.spv)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
yield self.handleBlock(peer, block);
|
|
||||||
} catch (e) {
|
|
||||||
self.emit('error', e);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
peer.on('block', co(function* (block) {
|
|
||||||
if (self.options.spv)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
yield self.handleBlock(peer, block);
|
|
||||||
} catch (e) {
|
|
||||||
self.emit('error', e);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
peer.on('tx', co(function* (tx) {
|
|
||||||
try {
|
|
||||||
yield self.handleTX(peer, tx);
|
|
||||||
} catch (e) {
|
|
||||||
self.emit('error', e);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
peer.on('headers', co(function* (headers) {
|
|
||||||
try {
|
|
||||||
yield self.handleHeaders(peer, headers);
|
|
||||||
} catch (e) {
|
|
||||||
self.emit('error', e);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
peer.on('blocks', co(function* (hashes) {
|
|
||||||
try {
|
|
||||||
yield self.handleBlockInv(peer, hashes);
|
|
||||||
} catch (e) {
|
|
||||||
self.emit('error', e);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
peer.on('txs', function(hashes) {
|
|
||||||
self.handleTXInv(peer, hashes);
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.on('reject', function(reject) {
|
peer.on('reject', function(reject) {
|
||||||
self.handleReject(peer, reject);
|
self.handleReject(peer, reject);
|
||||||
});
|
});
|
||||||
@ -840,6 +781,24 @@ Pool.prototype.handleAddr = function handleAddr(peer, addrs) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype.handleBlock = co(function* handleBlock(peer, block) {
|
Pool.prototype.handleBlock = co(function* handleBlock(peer, block) {
|
||||||
|
var hash = block.hash('hex');
|
||||||
|
var unlock = yield this.locker.lock(hash);
|
||||||
|
try {
|
||||||
|
return yield this._handleBlock(peer, block);
|
||||||
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle `block` packet. Attempt to add to chain (without a lock).
|
||||||
|
* @private
|
||||||
|
* @param {Peer} peer
|
||||||
|
* @param {MemBlock|MerkleBlock} block
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype._handleBlock = co(function* handleBlock(peer, block) {
|
||||||
var hash = block.hash('hex');
|
var hash = block.hash('hex');
|
||||||
var requested;
|
var requested;
|
||||||
|
|
||||||
@ -861,10 +820,8 @@ Pool.prototype.handleBlock = co(function* handleBlock(peer, block) {
|
|||||||
try {
|
try {
|
||||||
yield this.chain.add(block);
|
yield this.chain.add(block);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.type !== 'VerifyError') {
|
if (err.type !== 'VerifyError')
|
||||||
this.scheduleRequests(peer);
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
|
||||||
|
|
||||||
if (err.reason === 'bad-prevblk') {
|
if (err.reason === 'bad-prevblk') {
|
||||||
if (this.options.headers) {
|
if (this.options.headers) {
|
||||||
@ -873,24 +830,19 @@ Pool.prototype.handleBlock = co(function* handleBlock(peer, block) {
|
|||||||
}
|
}
|
||||||
this.logger.debug('Peer sent an orphan block. Resolving.');
|
this.logger.debug('Peer sent an orphan block. Resolving.');
|
||||||
yield peer.resolveOrphan(null, hash);
|
yield peer.resolveOrphan(null, hash);
|
||||||
this.scheduleRequests(peer);
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.reject(block, err.code, err.reason, err.score);
|
peer.reject(block, err.code, err.reason, err.score);
|
||||||
|
|
||||||
this.scheduleRequests(peer);
|
|
||||||
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scheduleRequests(peer);
|
|
||||||
|
|
||||||
if (this.logger.level >= 4 && this.chain.total % 20 === 0) {
|
if (this.logger.level >= 4 && this.chain.total % 20 === 0) {
|
||||||
this.logger.debug('Status:'
|
this.logger.debug('Status:'
|
||||||
+ ' ts=%s height=%d progress=%s'
|
+ ' ts=%s height=%d progress=%s'
|
||||||
+ ' blocks=%d orphans=%d active=%d'
|
+ ' blocks=%d orphans=%d active=%d'
|
||||||
+ ' queue=%d target=%s peers=%d'
|
+ ' target=%s peers=%d'
|
||||||
+ ' pending=%d jobs=%d',
|
+ ' pending=%d jobs=%d',
|
||||||
util.date(block.ts),
|
util.date(block.ts),
|
||||||
this.chain.height,
|
this.chain.height,
|
||||||
@ -898,11 +850,10 @@ Pool.prototype.handleBlock = co(function* handleBlock(peer, block) {
|
|||||||
this.chain.total,
|
this.chain.total,
|
||||||
this.chain.orphanCount,
|
this.chain.orphanCount,
|
||||||
this.requestMap.size,
|
this.requestMap.size,
|
||||||
this.queueMap.size,
|
|
||||||
block.bits,
|
block.bits,
|
||||||
this.peers.size(),
|
this.peers.size(),
|
||||||
this.chain.locker.pending,
|
this.locker.pending,
|
||||||
this.chain.locker.jobs.length);
|
this.locker.jobs.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.chain.total % 2000 === 0) {
|
if (this.chain.total % 2000 === 0) {
|
||||||
@ -922,6 +873,24 @@ Pool.prototype.handleBlock = co(function* handleBlock(peer, block) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype.handleTX = co(function* handleTX(peer, tx) {
|
Pool.prototype.handleTX = co(function* handleTX(peer, tx) {
|
||||||
|
var hash = tx.hash('hex');
|
||||||
|
var unlock = yield this.locker.lock(hash);
|
||||||
|
try {
|
||||||
|
return yield this._handleTX(peer, tx);
|
||||||
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a transaction. Attempt to add to mempool (without a lock).
|
||||||
|
* @private
|
||||||
|
* @param {Peer} peer
|
||||||
|
* @param {TX} tx
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype._handleTX = co(function* handleTX(peer, tx) {
|
||||||
var hash = tx.hash('hex');
|
var hash = tx.hash('hex');
|
||||||
var requested = this.fulfill(peer, hash);
|
var requested = this.fulfill(peer, hash);
|
||||||
var missing;
|
var missing;
|
||||||
@ -937,7 +906,6 @@ Pool.prototype.handleTX = co(function* handleTX(peer, tx) {
|
|||||||
if (!requested)
|
if (!requested)
|
||||||
this.txFilter.add(tx.hash());
|
this.txFilter.add(tx.hash());
|
||||||
this.emit('tx', tx, peer);
|
this.emit('tx', tx, peer);
|
||||||
this.scheduleRequests(peer);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -963,15 +931,9 @@ Pool.prototype.handleTX = co(function* handleTX(peer, tx) {
|
|||||||
'Requesting %d missing transactions (%s).',
|
'Requesting %d missing transactions (%s).',
|
||||||
missing.length, peer.hostname);
|
missing.length, peer.hostname);
|
||||||
|
|
||||||
try {
|
this.getTX(peer, missing);
|
||||||
this.getTX(peer, missing);
|
|
||||||
} catch (e) {
|
|
||||||
this.emit('error', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scheduleRequests(peer);
|
|
||||||
|
|
||||||
this.emit('tx', tx, peer);
|
this.emit('tx', tx, peer);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1002,7 +964,9 @@ Pool.prototype.handleHeaders = co(function* handleHeaders(peer, headers) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype._handleHeaders = co(function* handleHeaders(peer, headers) {
|
Pool.prototype._handleHeaders = co(function* handleHeaders(peer, headers) {
|
||||||
var i, ret, header, hash, last;
|
var items = [];
|
||||||
|
var ret = new VerifyResult();
|
||||||
|
var i, header, hash, last;
|
||||||
|
|
||||||
if (!this.options.headers)
|
if (!this.options.headers)
|
||||||
return;
|
return;
|
||||||
@ -1010,8 +974,6 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, headers) {
|
|||||||
if (!this.syncing)
|
if (!this.syncing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ret = new VerifyResult();
|
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
'Received %s headers from peer (%s).',
|
'Received %s headers from peer (%s).',
|
||||||
headers.length,
|
headers.length,
|
||||||
@ -1019,6 +981,9 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, headers) {
|
|||||||
|
|
||||||
this.emit('headers', headers);
|
this.emit('headers', headers);
|
||||||
|
|
||||||
|
if (headers.length === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
for (i = 0; i < headers.length; i++) {
|
for (i = 0; i < headers.length; i++) {
|
||||||
header = headers[i];
|
header = headers[i];
|
||||||
hash = header.hash('hex');
|
hash = header.hash('hex');
|
||||||
@ -1035,14 +1000,14 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, headers) {
|
|||||||
|
|
||||||
last = hash;
|
last = hash;
|
||||||
|
|
||||||
if (yield this.chain.has(hash))
|
if (yield this.hasBlock(hash))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
this.getBlock(peer, hash);
|
items.push(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule the getdata's we just added.
|
// Request the hashes we just added.
|
||||||
this.scheduleRequests(peer);
|
this.getBlock(peer, items);
|
||||||
|
|
||||||
// Restart the getheaders process
|
// Restart the getheaders process
|
||||||
// Technically `last` is not indexed yet so
|
// Technically `last` is not indexed yet so
|
||||||
@ -1051,7 +1016,7 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, headers) {
|
|||||||
// that much since FindForkInGlobalIndex
|
// that much since FindForkInGlobalIndex
|
||||||
// simply tries to find the latest block in
|
// simply tries to find the latest block in
|
||||||
// the peer's chain.
|
// the peer's chain.
|
||||||
if (last && headers.length === 2000)
|
if (headers.length === 2000)
|
||||||
yield peer.getHeaders(last);
|
yield peer.getHeaders(last);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1082,6 +1047,7 @@ Pool.prototype.handleBlockInv = co(function* handleBlockInv(peer, hashes) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype._handleBlockInv = co(function* handleBlockInv(peer, hashes) {
|
Pool.prototype._handleBlockInv = co(function* handleBlockInv(peer, hashes) {
|
||||||
|
var items = [];
|
||||||
var i, hash;
|
var i, hash;
|
||||||
|
|
||||||
if (!this.syncing)
|
if (!this.syncing)
|
||||||
@ -1101,8 +1067,6 @@ Pool.prototype._handleBlockInv = co(function* handleBlockInv(peer, hashes) {
|
|||||||
yield peer.getHeaders(null, hash);
|
yield peer.getHeaders(null, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scheduleRequests(peer);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1118,20 +1082,14 @@ Pool.prototype._handleBlockInv = co(function* handleBlockInv(peer, hashes) {
|
|||||||
|
|
||||||
// Resolve orphan chain.
|
// Resolve orphan chain.
|
||||||
if (this.chain.hasOrphan(hash)) {
|
if (this.chain.hasOrphan(hash)) {
|
||||||
// There is a possible race condition here.
|
|
||||||
// The orphan may get resolved by the time
|
|
||||||
// we create the locator. In that case, we
|
|
||||||
// should probably actually move to the
|
|
||||||
// `exists` clause below if it is the last
|
|
||||||
// hash.
|
|
||||||
this.logger.debug('Received known orphan hash (%s).', peer.hostname);
|
this.logger.debug('Received known orphan hash (%s).', peer.hostname);
|
||||||
yield peer.resolveOrphan(null, hash);
|
yield peer.resolveOrphan(null, hash);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request the block if we don't have it.
|
// Request the block if we don't have it.
|
||||||
if (!(yield this.chain.has(hash))) {
|
if (!(yield this.hasBlock(hash))) {
|
||||||
this.getBlock(peer, hash);
|
items.push(hash);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1148,7 +1106,7 @@ Pool.prototype._handleBlockInv = co(function* handleBlockInv(peer, hashes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scheduleRequests(peer);
|
this.getBlock(peer, items);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1158,14 +1116,42 @@ Pool.prototype._handleBlockInv = co(function* handleBlockInv(peer, hashes) {
|
|||||||
* @param {Hash[]} hashes
|
* @param {Hash[]} hashes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype.handleTXInv = function handleTXInv(peer, hashes) {
|
Pool.prototype.handleTXInv = co(function* handleTXInv(peer, hashes) {
|
||||||
|
var unlock = yield this.locker.lock();
|
||||||
|
try {
|
||||||
|
return yield this._handleTXInv(peer, hashes);
|
||||||
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle peer inv packet (txs).
|
||||||
|
* @private
|
||||||
|
* @param {Peer} peer
|
||||||
|
* @param {Hash[]} hashes
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype._handleTXInv = co(function* handleTXInv(peer, hashes) {
|
||||||
|
var items = [];
|
||||||
|
var i, hash;
|
||||||
|
|
||||||
this.emit('txs', hashes, peer);
|
this.emit('txs', hashes, peer);
|
||||||
|
|
||||||
if (this.syncing && !this.chain.synced)
|
if (this.syncing && !this.chain.synced)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.getTX(peer, hashes);
|
for (i = 0; i < hashes.length; i++) {
|
||||||
};
|
hash = hashes[i];
|
||||||
|
|
||||||
|
if (this.hasTX(hash))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
items.push(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getTX(peer, items);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle peer reject event.
|
* Handle peer reject event.
|
||||||
@ -1290,7 +1276,7 @@ Pool.prototype.addInbound = function addInbound(socket) {
|
|||||||
* @returns {NetAddress}
|
* @returns {NetAddress}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype.getHost = function getHost(unique) {
|
Pool.prototype.getHost = function getHost() {
|
||||||
var services = this.options.requiredServices;
|
var services = this.options.requiredServices;
|
||||||
var now = this.network.now();
|
var now = this.network.now();
|
||||||
var i, entry, addr;
|
var i, entry, addr;
|
||||||
@ -1312,10 +1298,8 @@ Pool.prototype.getHost = function getHost(unique) {
|
|||||||
|
|
||||||
addr = entry.addr;
|
addr = entry.addr;
|
||||||
|
|
||||||
if (unique) {
|
if (this.peers.has(addr.hostname))
|
||||||
if (this.peers.has(addr.hostname))
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!addr.isValid())
|
if (!addr.isValid())
|
||||||
continue;
|
continue;
|
||||||
@ -1355,7 +1339,7 @@ Pool.prototype.addOutbound = function addOutbound() {
|
|||||||
if (!this.peers.load)
|
if (!this.peers.load)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
addr = this.getHost(true);
|
addr = this.getHost();
|
||||||
|
|
||||||
if (!addr)
|
if (!addr)
|
||||||
return;
|
return;
|
||||||
@ -1424,16 +1408,6 @@ Pool.prototype.removePeer = function removePeer(peer) {
|
|||||||
hash = hashes[i];
|
hash = hashes[i];
|
||||||
this.fulfill(peer, hash);
|
this.fulfill(peer, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
hashes = peer.blockQueue;
|
|
||||||
|
|
||||||
for (i = 0; i < hashes.length; i++) {
|
|
||||||
hash = hashes[i];
|
|
||||||
assert(this.queueMap.has(hash));
|
|
||||||
this.queueMap.remove(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
peer.blockQueue.length = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1541,14 +1515,16 @@ Pool.prototype.watchOutpoint = function watchOutpoint(outpoint) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue a `getdata` request to be sent. Checks existence
|
* Queue a `getdata` request to be sent.
|
||||||
* in the chain before requesting.
|
|
||||||
* @param {Peer} peer
|
* @param {Peer} peer
|
||||||
* @param {Hash} hash - Block hash.
|
* @param {Hash[]} hashes
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype.getBlock = function getBlock(peer, hash) {
|
Pool.prototype.getBlock = function getBlock(peer, hashes) {
|
||||||
|
var items = [];
|
||||||
|
var i, hash;
|
||||||
|
|
||||||
if (!this.loaded)
|
if (!this.loaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1558,38 +1534,30 @@ Pool.prototype.getBlock = function getBlock(peer, hash) {
|
|||||||
if (peer.destroyed)
|
if (peer.destroyed)
|
||||||
throw new Error('Peer is destroyed (getdata).');
|
throw new Error('Peer is destroyed (getdata).');
|
||||||
|
|
||||||
if (this.requestMap.has(hash))
|
for (i = 0; i < hashes.length; i++) {
|
||||||
|
hash = hashes[i];
|
||||||
|
|
||||||
|
if (this.requestMap.has(hash))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this.requestMap.insert(hash);
|
||||||
|
peer.requestMap.insert(hash);
|
||||||
|
|
||||||
|
items.push(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.length === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.queueMap.has(hash))
|
this.logger.debug(
|
||||||
return;
|
'Requesting %d/%d blocks from peer with getdata (%s).',
|
||||||
|
items.length,
|
||||||
|
this.requestMap.size,
|
||||||
|
peer.hostname);
|
||||||
|
|
||||||
this.queueMap.insert(hash);
|
peer.getBlock(items);
|
||||||
peer.blockQueue.push(hash);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether the chain has or has seen an item.
|
|
||||||
* @param {Hash} hash
|
|
||||||
* @returns {Promise} - Returns Boolean.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Pool.prototype.hasBlock = co(function* hasBlock(hash) {
|
|
||||||
// Check the chain.
|
|
||||||
if (yield this.chain.has(hash))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Check the pending requests.
|
|
||||||
if (this.requestMap.has(hash))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Check the queued requests.
|
|
||||||
if (this.queueMap.has(hash))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue a `getdata` request to be sent. Checks existence
|
* Queue a `getdata` request to be sent. Checks existence
|
||||||
* in the mempool before requesting.
|
* in the mempool before requesting.
|
||||||
@ -1614,11 +1582,9 @@ Pool.prototype.getTX = function getTX(peer, hashes) {
|
|||||||
for (i = 0; i < hashes.length; i++) {
|
for (i = 0; i < hashes.length; i++) {
|
||||||
hash = hashes[i];
|
hash = hashes[i];
|
||||||
|
|
||||||
if (this.hasTX(hash))
|
if (this.requestMap.has(hash))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
assert(!this.requestMap.has(hash));
|
|
||||||
|
|
||||||
this.requestMap.insert(hash);
|
this.requestMap.insert(hash);
|
||||||
peer.requestMap.insert(hash);
|
peer.requestMap.insert(hash);
|
||||||
|
|
||||||
@ -1637,6 +1603,24 @@ Pool.prototype.getTX = function getTX(peer, hashes) {
|
|||||||
peer.getTX(items);
|
peer.getTX(items);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether the chain has or has seen an item.
|
||||||
|
* @param {Hash} hash
|
||||||
|
* @returns {Promise} - Returns Boolean.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype.hasBlock = co(function* hasBlock(hash) {
|
||||||
|
// Check the lock.
|
||||||
|
if (this.locker.has(hash))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check the chain.
|
||||||
|
if (yield this.chain.has(hash))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether the mempool has or has seen an item.
|
* Test whether the mempool has or has seen an item.
|
||||||
* @param {Hash} hash
|
* @param {Hash} hash
|
||||||
@ -1644,6 +1628,10 @@ Pool.prototype.getTX = function getTX(peer, hashes) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype.hasTX = function hasTX(hash) {
|
Pool.prototype.hasTX = function hasTX(hash) {
|
||||||
|
// Check the lock queue.
|
||||||
|
if (this.locker.has(hash))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (!this.mempool) {
|
if (!this.mempool) {
|
||||||
// Check the TX filter if
|
// Check the TX filter if
|
||||||
// we don't have a mempool.
|
// we don't have a mempool.
|
||||||
@ -1661,100 +1649,9 @@ Pool.prototype.hasTX = function hasTX(hash) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the pending requests.
|
|
||||||
if (this.requestMap.has(hash))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedule next batch of `getdata` requests for peer.
|
|
||||||
* @param {Peer} peer
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Pool.prototype.scheduleRequests = co(function* scheduleRequests(peer) {
|
|
||||||
if (this.scheduled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.scheduled = true;
|
|
||||||
|
|
||||||
yield this.chain.onDrain();
|
|
||||||
|
|
||||||
this.sendBlockRequests(peer);
|
|
||||||
|
|
||||||
this.scheduled = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send scheduled requests in the request queues.
|
|
||||||
* @private
|
|
||||||
* @param {Peer} peer
|
|
||||||
*/
|
|
||||||
|
|
||||||
Pool.prototype.sendBlockRequests = function sendBlockRequests(peer) {
|
|
||||||
var queue = peer.blockQueue;
|
|
||||||
var hashes = [];
|
|
||||||
var i, size, hash;
|
|
||||||
|
|
||||||
if (peer.destroyed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (queue.length === 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this.options.spv) {
|
|
||||||
if (this.requestMap.size >= 2000)
|
|
||||||
return;
|
|
||||||
|
|
||||||
size = Math.min(queue.length, 50000);
|
|
||||||
} else {
|
|
||||||
size = this.network.getBatchSize(this.chain.height);
|
|
||||||
|
|
||||||
if (this.requestMap.size >= size)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < queue.length; i++) {
|
|
||||||
hash = queue[i];
|
|
||||||
|
|
||||||
if (hashes.length === size)
|
|
||||||
break;
|
|
||||||
|
|
||||||
assert(this.queueMap.has(hash));
|
|
||||||
|
|
||||||
this.queueMap.remove(hash);
|
|
||||||
|
|
||||||
assert(!this.requestMap.has(hash));
|
|
||||||
|
|
||||||
// Do a second check to make sure
|
|
||||||
// we don't have this in the chain.
|
|
||||||
// Avoids a potential race condition
|
|
||||||
// with the `chain.has()` call.
|
|
||||||
if (this.chain.seen(hash))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
this.requestMap.insert(hash);
|
|
||||||
peer.requestMap.insert(hash);
|
|
||||||
|
|
||||||
hashes.push(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
peer.blockQueue = queue.slice(i);
|
|
||||||
|
|
||||||
if (hashes.length === 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.logger.debug(
|
|
||||||
'Requesting %d/%d blocks from peer with getdata (%s).',
|
|
||||||
hashes.length,
|
|
||||||
this.requestMap.size,
|
|
||||||
peer.hostname);
|
|
||||||
|
|
||||||
peer.getBlock(hashes);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fulfill a requested item.
|
* Fulfill a requested item.
|
||||||
* @param {Peer} peer
|
* @param {Peer} peer
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user