net: always pause socket when handling a packet.
This commit is contained in:
parent
9aaf5ea2a0
commit
37de8bf2a7
@ -1480,6 +1480,25 @@ ChainDB.prototype.getBlock = co(function* getBlock(hash) {
|
|||||||
return block;
|
return block;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a block from the database (not filled with coins).
|
||||||
|
* @param {Hash} hash
|
||||||
|
* @returns {Promise} - Returns {@link Block}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ChainDB.prototype.getRawBlock = co(function* getRawBlock(hash) {
|
||||||
|
var items = yield this.getBoth(hash);
|
||||||
|
var height;
|
||||||
|
|
||||||
|
if (!items)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hash = items[0];
|
||||||
|
height = items[1];
|
||||||
|
|
||||||
|
return yield this.db.get(layout.b(hash));
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether coins are still unspent. Necessary for bip30.
|
* Check whether coins are still unspent. Necessary for bip30.
|
||||||
* @see https://bitcointalk.org/index.php?topic=67738.0
|
* @see https://bitcointalk.org/index.php?topic=67738.0
|
||||||
|
|||||||
190
lib/net/peer.js
190
lib/net/peer.js
@ -775,7 +775,7 @@ Peer.prototype.write = function write(data) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Peer.prototype.send = function send(packet) {
|
Peer.prototype.send = function send(packet) {
|
||||||
var tx, checksum, payload;
|
var tx, checksum;
|
||||||
|
|
||||||
// Used cached hashes as the
|
// Used cached hashes as the
|
||||||
// packet checksum for speed.
|
// packet checksum for speed.
|
||||||
@ -789,8 +789,17 @@ Peer.prototype.send = function send(packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
payload = this.framer.packet(packet.cmd, packet.toRaw(), checksum);
|
return this.sendRaw(packet.cmd, packet.toRaw(), checksum);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet.
|
||||||
|
* @param {Packet} packet
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Peer.prototype.sendRaw = function sendRaw(cmd, body, checksum) {
|
||||||
|
var payload = this.framer.packet(cmd, body, checksum);
|
||||||
return this.write(payload);
|
return this.write(payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1334,39 +1343,34 @@ Peer.prototype._handleGetBlocks = co(function* _handleGetBlocks(packet) {
|
|||||||
Peer.prototype._handleVersion = co(function* _handleVersion(version) {
|
Peer.prototype._handleVersion = co(function* _handleVersion(version) {
|
||||||
if (!this.network.selfConnect) {
|
if (!this.network.selfConnect) {
|
||||||
if (version.nonce.cmp(this.pool.localNonce) === 0) {
|
if (version.nonce.cmp(this.pool.localNonce) === 0) {
|
||||||
this.error('We connected to ourself. Oops.');
|
|
||||||
this.ignore();
|
this.ignore();
|
||||||
return;
|
throw new Error('We connected to ourself. Oops.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version.version < constants.MIN_VERSION) {
|
if (version.version < constants.MIN_VERSION) {
|
||||||
this.error('Peer does not support required protocol version.');
|
|
||||||
this.ignore();
|
this.ignore();
|
||||||
return;
|
throw new Error('Peer does not support required protocol version.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.outbound) {
|
if (this.outbound) {
|
||||||
if (!version.hasNetwork()) {
|
if (!version.hasNetwork()) {
|
||||||
this.error('Peer does not support network services.');
|
|
||||||
this.ignore();
|
this.ignore();
|
||||||
return;
|
throw new Error('Peer does not support network services.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.headers) {
|
if (this.options.headers) {
|
||||||
if (!version.hasHeaders()) {
|
if (!version.hasHeaders()) {
|
||||||
this.error('Peer does not support getheaders.');
|
|
||||||
this.ignore();
|
this.ignore();
|
||||||
return;
|
throw new Error('Peer does not support getheaders.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.spv) {
|
if (this.options.spv) {
|
||||||
if (!version.hasBloom()) {
|
if (!version.hasBloom()) {
|
||||||
this.error('Peer does not support BIP37.');
|
|
||||||
this.ignore();
|
this.ignore();
|
||||||
return;
|
throw new Error('Peer does not support BIP37.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1375,17 +1379,15 @@ Peer.prototype._handleVersion = co(function* _handleVersion(version) {
|
|||||||
|
|
||||||
if (!this.haveWitness) {
|
if (!this.haveWitness) {
|
||||||
if (!this.network.oldWitness) {
|
if (!this.network.oldWitness) {
|
||||||
this.error('Peer does not support segregated witness.');
|
|
||||||
this.ignore();
|
this.ignore();
|
||||||
return;
|
throw new Error('Peer does not support segregated witness.');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
yield this.request('havewitness');
|
yield this.request('havewitness');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.error('Peer does not support segregated witness.');
|
|
||||||
this.ignore();
|
this.ignore();
|
||||||
return;
|
throw new Error('Peer does not support segregated witness.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.haveWitness = true;
|
this.haveWitness = true;
|
||||||
@ -1446,31 +1448,48 @@ Peer.prototype._handleMempool = function _handleMempool(packet) {
|
|||||||
* [Error, {@link Block}|{@link MempoolEntry}].
|
* [Error, {@link Block}|{@link MempoolEntry}].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Peer.prototype._getItem = co(function* _getItem(item) {
|
Peer.prototype._getBroadcasted = function _getBroadcasted(item) {
|
||||||
var entry = this.pool.invMap[item.hash];
|
var entry = this.pool.invMap[item.hash];
|
||||||
|
|
||||||
if (entry) {
|
if (!entry)
|
||||||
this.logger.debug(
|
return;
|
||||||
'Peer requested %s %s as a %s packet (%s).',
|
|
||||||
entry.type === constants.inv.TX ? 'tx' : 'block',
|
|
||||||
utils.revHex(entry.hash),
|
|
||||||
item.hasWitness() ? 'witness' : 'normal',
|
|
||||||
this.hostname);
|
|
||||||
|
|
||||||
entry.ack(this);
|
this.logger.debug(
|
||||||
|
'Peer requested %s %s as a %s packet (%s).',
|
||||||
|
entry.type === constants.inv.TX ? 'tx' : 'block',
|
||||||
|
utils.revHex(entry.hash),
|
||||||
|
item.hasWitness() ? 'witness' : 'normal',
|
||||||
|
this.hostname);
|
||||||
|
|
||||||
if (entry.msg) {
|
entry.ack(this);
|
||||||
if (item.isTX()) {
|
|
||||||
if (entry.type === constants.inv.TX)
|
if (!entry.msg)
|
||||||
return entry.msg;
|
return;
|
||||||
} else {
|
|
||||||
if (entry.type === constants.inv.BLOCK)
|
if (item.isTX()) {
|
||||||
return entry.msg;
|
if (entry.type !== constants.inv.TX)
|
||||||
}
|
return;
|
||||||
|
} else {
|
||||||
|
if (entry.type !== constants.inv.BLOCK)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return entry.msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a block/tx either from the broadcast map, mempool, or blockchain.
|
||||||
|
* @param {InvItem} item
|
||||||
|
* @returns {Promise}
|
||||||
|
* [Error, {@link Block}|{@link MempoolEntry}].
|
||||||
|
*/
|
||||||
|
|
||||||
|
Peer.prototype._getItem = co(function* _getItem(item) {
|
||||||
|
var entry = this._getBroadcasted(item);
|
||||||
|
|
||||||
|
if (entry)
|
||||||
|
return entry;
|
||||||
|
|
||||||
if (this.options.selfish)
|
if (this.options.selfish)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1486,7 +1505,47 @@ Peer.prototype._getItem = co(function* _getItem(item) {
|
|||||||
if (this.chain.db.options.prune)
|
if (this.chain.db.options.prune)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
return yield this.chain.db.getBlock(item.hash);
|
return yield this.chain.db.getRawBlock(item.hash);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a block/tx either from the broadcast map, mempool, or blockchain.
|
||||||
|
* @param {InvItem} item
|
||||||
|
* @returns {Promise}
|
||||||
|
* [Error, {@link Block}|{@link MempoolEntry}].
|
||||||
|
*/
|
||||||
|
|
||||||
|
Peer.prototype._sendBlock = co(function* _sendBlock(item) {
|
||||||
|
var block = this._getBroadcasted(item);
|
||||||
|
|
||||||
|
if (block) {
|
||||||
|
yield this.send(new packets.BlockPacket(block, item.hasWitness()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.selfish
|
||||||
|
|| this.chain.db.options.spv
|
||||||
|
|| this.chain.db.options.prune) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.hasWitness() === !!this.options.witness) {
|
||||||
|
block = yield this.chain.db.getRawBlock(item.hash);
|
||||||
|
|
||||||
|
if (!block)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
yield this.sendRaw('block', block);
|
||||||
|
} else {
|
||||||
|
block = yield this.chain.db.getBlock(item.hash);
|
||||||
|
|
||||||
|
if (!block)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
yield this.send(new packets.BlockPacket(block, item.hasWitness()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1498,24 +1557,21 @@ Peer.prototype._getItem = co(function* _getItem(item) {
|
|||||||
Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
|
Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
|
||||||
var notFound = [];
|
var notFound = [];
|
||||||
var items = packet.items;
|
var items = packet.items;
|
||||||
var i, j, item, entry, tx, block;
|
var i, j, item, tx, block, result;
|
||||||
|
|
||||||
if (items.length > 50000) {
|
if (items.length > 50000)
|
||||||
this.error('getdata size too large (%s).', items.length);
|
throw new Error('getdata size too large (' + items.length + ').');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < items.length; i++) {
|
for (i = 0; i < items.length; i++) {
|
||||||
item = items[i];
|
item = items[i];
|
||||||
entry = yield this._getItem(item);
|
|
||||||
|
|
||||||
if (!entry) {
|
|
||||||
notFound.push(item);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.isTX()) {
|
if (item.isTX()) {
|
||||||
tx = entry;
|
tx = yield this._getItem(item);
|
||||||
|
|
||||||
|
if (!tx) {
|
||||||
|
notFound.push(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Coinbases are an insta-ban from any node.
|
// Coinbases are an insta-ban from any node.
|
||||||
// This should technically never happen, but
|
// This should technically never happen, but
|
||||||
@ -1532,12 +1588,14 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
block = entry;
|
|
||||||
|
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case constants.inv.BLOCK:
|
case constants.inv.BLOCK:
|
||||||
case constants.inv.WITNESS_BLOCK:
|
case constants.inv.WITNESS_BLOCK:
|
||||||
yield this.send(new packets.BlockPacket(block, item.hasWitness()));
|
result = yield this._sendBlock(item);
|
||||||
|
if (!result) {
|
||||||
|
notFound.push(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case constants.inv.FILTERED_BLOCK:
|
case constants.inv.FILTERED_BLOCK:
|
||||||
case constants.inv.WITNESS_FILTERED_BLOCK:
|
case constants.inv.WITNESS_FILTERED_BLOCK:
|
||||||
@ -1546,6 +1604,13 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block = yield this._getItem(item);
|
||||||
|
|
||||||
|
if (!block) {
|
||||||
|
notFound.push(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
block = block.toMerkle(this.spvFilter);
|
block = block.toMerkle(this.spvFilter);
|
||||||
|
|
||||||
yield this.send(new packets.MerkleBlockPacket(block));
|
yield this.send(new packets.MerkleBlockPacket(block));
|
||||||
@ -1559,10 +1624,21 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
|
|||||||
case constants.inv.CMPCT_BLOCK:
|
case constants.inv.CMPCT_BLOCK:
|
||||||
// Fallback to full block.
|
// Fallback to full block.
|
||||||
if (block.height < this.chain.tip.height - 10) {
|
if (block.height < this.chain.tip.height - 10) {
|
||||||
yield this.send(new packets.BlockPacket(block, false));
|
result = yield this._sendBlock(item);
|
||||||
|
if (!result) {
|
||||||
|
notFound.push(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block = yield this._getItem(item);
|
||||||
|
|
||||||
|
if (!block) {
|
||||||
|
notFound.push(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Try again with a new nonce
|
// Try again with a new nonce
|
||||||
// if we get a siphash collision.
|
// if we get a siphash collision.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -2213,7 +2289,7 @@ Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
|
|||||||
|
|
||||||
Peer.prototype.sendMempool = function sendMempool() {
|
Peer.prototype.sendMempool = function sendMempool() {
|
||||||
if (!this.version)
|
if (!this.version)
|
||||||
return;
|
return Promise.resolve(null);
|
||||||
|
|
||||||
if (!this.version.hasBloom()) {
|
if (!this.version.hasBloom()) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
@ -2372,20 +2448,20 @@ Peer.prototype.sync = function sync() {
|
|||||||
var tip;
|
var tip;
|
||||||
|
|
||||||
if (!this.pool.syncing)
|
if (!this.pool.syncing)
|
||||||
return;
|
return Promise.resolve(null);
|
||||||
|
|
||||||
if (!this.ack)
|
if (!this.ack)
|
||||||
return;
|
return Promise.resolve(null);
|
||||||
|
|
||||||
if (this.syncSent)
|
if (this.syncSent)
|
||||||
return;
|
return Promise.resolve(null);
|
||||||
|
|
||||||
if (!this.version.hasNetwork())
|
if (!this.version.hasNetwork())
|
||||||
return;
|
return Promise.resolve(null);
|
||||||
|
|
||||||
if (!this.isLoader()) {
|
if (!this.isLoader()) {
|
||||||
if (!this.chain.synced)
|
if (!this.chain.synced)
|
||||||
return;
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask for the mempool if we're synced.
|
// Ask for the mempool if we're synced.
|
||||||
|
|||||||
@ -18,6 +18,8 @@ function ProxySocket(uri) {
|
|||||||
|
|
||||||
this.socket = new IOClient(uri, { reconnection: false });
|
this.socket = new IOClient(uri, { reconnection: false });
|
||||||
this.sendBuffer = [];
|
this.sendBuffer = [];
|
||||||
|
this.recvBuffer = [];
|
||||||
|
this.paused = false;
|
||||||
this.snonce = null;
|
this.snonce = null;
|
||||||
this.bytesWritten = 0;
|
this.bytesWritten = 0;
|
||||||
this.bytesRead = 0;
|
this.bytesRead = 0;
|
||||||
@ -60,6 +62,10 @@ ProxySocket.prototype._init = function _init() {
|
|||||||
|
|
||||||
this.socket.on('tcp data', function(data) {
|
this.socket.on('tcp data', function(data) {
|
||||||
data = new Buffer(data, 'hex');
|
data = new Buffer(data, 'hex');
|
||||||
|
if (self.paused) {
|
||||||
|
self.recvBuffer.push(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.bytesRead += data.length;
|
self.bytesRead += data.length;
|
||||||
self.emit('data', data);
|
self.emit('data', data);
|
||||||
});
|
});
|
||||||
@ -149,6 +155,27 @@ ProxySocket.prototype.write = function write(data, callback) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ProxySocket.prototype.pause = function pause() {
|
||||||
|
this.paused = true;
|
||||||
|
this.socket.emit('tcp pause');
|
||||||
|
};
|
||||||
|
|
||||||
|
ProxySocket.prototype.resume = function resume() {
|
||||||
|
var recv = this.recvBuffer;
|
||||||
|
var i, data;
|
||||||
|
|
||||||
|
this.paused = false;
|
||||||
|
this.recvBuffer = [];
|
||||||
|
|
||||||
|
for (i = 0; i < recv.length; i++) {
|
||||||
|
data = recv[i];
|
||||||
|
this.bytesRead += data.length;
|
||||||
|
this.emit('data', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.emit('tcp resume');
|
||||||
|
};
|
||||||
|
|
||||||
ProxySocket.prototype.destroy = function destroy() {
|
ProxySocket.prototype.destroy = function destroy() {
|
||||||
if (this.closed)
|
if (this.closed)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -86,7 +86,7 @@ Locker.prototype.lock = function lock(arg1, arg2) {
|
|||||||
|
|
||||||
if (force) {
|
if (force) {
|
||||||
assert(this.busy);
|
assert(this.busy);
|
||||||
return new Promise(nop);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.busy) {
|
if (this.busy) {
|
||||||
@ -220,7 +220,7 @@ MappedLock.prototype.lock = function lock(key, force) {
|
|||||||
|
|
||||||
if (force || key == null) {
|
if (force || key == null) {
|
||||||
assert(key == null || this.busy[key]);
|
assert(key == null || this.busy[key]);
|
||||||
return new Promise(nop);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.busy[key]) {
|
if (this.busy[key]) {
|
||||||
@ -268,14 +268,6 @@ MappedLock.prototype.unlock = function unlock(key) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* Helpers
|
|
||||||
*/
|
|
||||||
|
|
||||||
function nop(resolve, reject) {
|
|
||||||
resolve(utils.nop);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -324,4 +324,4 @@ exports.call = call;
|
|||||||
exports.promisify = promisify;
|
exports.promisify = promisify;
|
||||||
exports.every = every;
|
exports.every = every;
|
||||||
|
|
||||||
module.exports = spawn;
|
module.exports = exports;
|
||||||
|
|||||||
@ -1042,7 +1042,7 @@ Wallet.prototype.send = co(function* send(options) {
|
|||||||
Wallet.prototype._send = co(function* send(options) {
|
Wallet.prototype._send = co(function* send(options) {
|
||||||
var tx = yield this.createTX(options, true);
|
var tx = yield this.createTX(options, true);
|
||||||
|
|
||||||
yield this.sign(tx);
|
yield this.sign(tx, options);
|
||||||
|
|
||||||
if (!tx.isSigned())
|
if (!tx.isSigned())
|
||||||
throw new Error('TX could not be fully signed.');
|
throw new Error('TX could not be fully signed.');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user