event based system for chain error handling.
This commit is contained in:
parent
7a04ac25d0
commit
5d9f106e2a
@ -39,11 +39,13 @@ function Chain(options) {
|
|||||||
this.request = new utils.RequestCache();
|
this.request = new utils.RequestCache();
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.tip = null;
|
this.tip = null;
|
||||||
|
this.height = -1;
|
||||||
this.mempool = options.mempool;
|
this.mempool = options.mempool;
|
||||||
this.blockdb = options.blockdb;
|
this.blockdb = options.blockdb;
|
||||||
this.locked = false;
|
this.locked = false;
|
||||||
this.pending = [];
|
this.pending = [];
|
||||||
this.orphanLimit = options.orphanLimit || 20 * 1024 * 1024;
|
this.orphanLimit = options.orphanLimit || 20 * 1024 * 1024;
|
||||||
|
this.invalid = {};
|
||||||
|
|
||||||
this.orphan = {
|
this.orphan = {
|
||||||
map: {},
|
map: {},
|
||||||
@ -86,6 +88,47 @@ Chain.msg = function msg(code) {
|
|||||||
Chain.prototype._init = function _init() {
|
Chain.prototype._init = function _init() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
// Hook into events for debugging
|
||||||
|
this.on('fork', function(data, peer) {
|
||||||
|
var host = peer ? peer.host : 'unknown';
|
||||||
|
utils.debug(
|
||||||
|
'Fork at height %d: expected=%s received=%s checkpoint=%s peer=%s',
|
||||||
|
data.height,
|
||||||
|
utils.revHex(data.expected),
|
||||||
|
utils.revHex(data.received),
|
||||||
|
data.checkpoint,
|
||||||
|
host
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on('invalid', function(data, peer) {
|
||||||
|
var host = peer ? peer.host : 'unknown';
|
||||||
|
utils.debug(
|
||||||
|
'Invalid block at height %d: hash=%s peer=%s',
|
||||||
|
data.height,
|
||||||
|
utils.revHex(data.hash),
|
||||||
|
host
|
||||||
|
);
|
||||||
|
if (data.chain) {
|
||||||
|
utils.debug(
|
||||||
|
'Peer is sending an invalid continuation chain (%s)',
|
||||||
|
host);
|
||||||
|
} else if (data.seen) {
|
||||||
|
utils.debug('Peer is sending an invalid chain (%s)', host);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on('exists', function(data, peer) {
|
||||||
|
var host = peer ? peer.host : 'unknown';
|
||||||
|
utils.debug('Already have block %s (%s)',
|
||||||
|
data.height, host);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on('orphan', function(data, peer) {
|
||||||
|
var host = peer ? peer.host : 'unknown';
|
||||||
|
utils.debug('Handled orphan %s (%s)', utils.revHex(data.hash), host);
|
||||||
|
});
|
||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
utils.debug('Chain is loading.');
|
utils.debug('Chain is loading.');
|
||||||
@ -725,6 +768,7 @@ Chain.prototype._saveEntry = function _saveEntry(entry, save, callback) {
|
|||||||
|
|
||||||
if (!this.tip || entry.height > this.tip.height) {
|
if (!this.tip || entry.height > this.tip.height) {
|
||||||
this.tip = entry;
|
this.tip = entry;
|
||||||
|
this.height = this.tip.height;
|
||||||
this.emit('tip', this.tip);
|
this.emit('tip', this.tip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,6 +816,8 @@ Chain.prototype.resetHeight = function resetHeight(height) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.tip = this.db.get(height);
|
this.tip = this.db.get(height);
|
||||||
|
assert(this.tip);
|
||||||
|
this.height = this.tip.height;
|
||||||
this.emit('tip', this.tip);
|
this.emit('tip', this.tip);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -784,6 +830,7 @@ Chain.prototype.resetTime = function resetTime(ts) {
|
|||||||
|
|
||||||
Chain.prototype.add = function add(initial, peer, callback) {
|
Chain.prototype.add = function add(initial, peer, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var host = peer ? peer.host : 'unknown';
|
||||||
var code = Chain.codes.unchanged;
|
var code = Chain.codes.unchanged;
|
||||||
var total = 0;
|
var total = 0;
|
||||||
|
|
||||||
@ -797,7 +844,23 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
(function next(block) {
|
(function next(block) {
|
||||||
var hash = block.hash('hex');
|
var hash = block.hash('hex');
|
||||||
var prevHash = block.prevBlock;
|
var prevHash = block.prevBlock;
|
||||||
var prevHeight, entry, existing, checkpoint, prev;
|
var prevHeight, entry, existing, checkpoint, prev, orphan;
|
||||||
|
|
||||||
|
// Special case for genesis block.
|
||||||
|
if (block.isGenesis())
|
||||||
|
return done(null, code);
|
||||||
|
|
||||||
|
// Do not revalidate known invalid blocks.
|
||||||
|
if (self.invalid[hash] || self.invalid[prevHash]) {
|
||||||
|
code = Chain.codes.invalid;
|
||||||
|
self.emit('invalid', {
|
||||||
|
height: -1,
|
||||||
|
hash: hash,
|
||||||
|
seen: true,
|
||||||
|
chain: self.invalid[prevHash]
|
||||||
|
}, peer);
|
||||||
|
return done(null, code);;
|
||||||
|
}
|
||||||
|
|
||||||
// Find the previous block height/index.
|
// Find the previous block height/index.
|
||||||
prevHeight = self.heightLookup[prevHash];
|
prevHeight = self.heightLookup[prevHash];
|
||||||
@ -808,32 +871,41 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
// orphans.
|
// orphans.
|
||||||
if (block === initial && !block.verify()) {
|
if (block === initial && !block.verify()) {
|
||||||
code = Chain.codes.invalid;
|
code = Chain.codes.invalid;
|
||||||
|
self.invalid[hash] = true;
|
||||||
self.emit('invalid', {
|
self.emit('invalid', {
|
||||||
height: prevHeight + 1,
|
height: prevHeight + 1,
|
||||||
hash: hash
|
hash: hash,
|
||||||
|
seen: false,
|
||||||
|
chain: false
|
||||||
}, peer);
|
}, peer);
|
||||||
return done(null, code);
|
return done(null, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the block is already known to be
|
// If the block is already known to be
|
||||||
// an orphan, ignore it.
|
// an orphan, ignore it.
|
||||||
if (self.orphan.map[prevHash]) {
|
orphan = self.orphan.map[prevHash];
|
||||||
|
if (orphan) {
|
||||||
// If the orphan chain forked, simply
|
// If the orphan chain forked, simply
|
||||||
// reset the orphans and find a new peer.
|
// reset the orphans and find a new peer.
|
||||||
if (self.orphan.map[prevHash].hash('hex') !== hash) {
|
if (orphan.hash('hex') !== hash) {
|
||||||
self.orphan.map = {};
|
self.orphan.map = {};
|
||||||
self.orphan.bmap = {};
|
self.orphan.bmap = {};
|
||||||
self.orphan.count = 0;
|
self.orphan.count = 0;
|
||||||
self.orphan.size = 0;
|
self.orphan.size = 0;
|
||||||
self.emit('fork', {
|
self.emit('fork', {
|
||||||
height: -1,
|
height: -1,
|
||||||
expected: self.orphan.map[prevHash].hash('hex'),
|
expected: orphan.hash('hex'),
|
||||||
received: hash,
|
received: hash,
|
||||||
checkpoint: false
|
checkpoint: false
|
||||||
}, peer);
|
}, peer);
|
||||||
code = Chain.codes.forked;
|
code = Chain.codes.forked;
|
||||||
return done(null, code);
|
return done(null, code);
|
||||||
}
|
}
|
||||||
|
self.emit('orphan', {
|
||||||
|
height: -1,
|
||||||
|
hash: hash,
|
||||||
|
seen: true
|
||||||
|
}, peer);
|
||||||
code = Chain.codes.knownOrphan;
|
code = Chain.codes.knownOrphan;
|
||||||
return done(null, code);
|
return done(null, code);
|
||||||
}
|
}
|
||||||
@ -846,6 +918,11 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
self.orphan.map[prevHash] = block;
|
self.orphan.map[prevHash] = block;
|
||||||
self.orphan.bmap[hash] = block;
|
self.orphan.bmap[hash] = block;
|
||||||
code = Chain.codes.newOrphan;
|
code = Chain.codes.newOrphan;
|
||||||
|
self.emit('orphan', {
|
||||||
|
height: -1,
|
||||||
|
hash: hash,
|
||||||
|
seen: false
|
||||||
|
}, peer);
|
||||||
return done(null, code);
|
return done(null, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,7 +947,11 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
// who isn't trying to fool us.
|
// who isn't trying to fool us.
|
||||||
checkpoint = network.checkpoints[entry.height];
|
checkpoint = network.checkpoints[entry.height];
|
||||||
if (checkpoint) {
|
if (checkpoint) {
|
||||||
self.emit('checkpoint', entry.height, entry.hash, checkpoint);
|
self.emit('checkpoint', {
|
||||||
|
height: entry.height,
|
||||||
|
hash: entry.hash,
|
||||||
|
checkpoint: checkpoint
|
||||||
|
});
|
||||||
if (hash !== checkpoint) {
|
if (hash !== checkpoint) {
|
||||||
// Resetting to the last checkpoint _really_ isn't
|
// Resetting to the last checkpoint _really_ isn't
|
||||||
// necessary (even bitcoind doesn't do it), but it
|
// necessary (even bitcoind doesn't do it), but it
|
||||||
@ -883,7 +964,7 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
expected: network.checkpoints[entry.height],
|
expected: network.checkpoints[entry.height],
|
||||||
received: entry.hash,
|
received: entry.hash,
|
||||||
checkpoint: true
|
checkpoint: true
|
||||||
});
|
}, peer);
|
||||||
return done(null, code);
|
return done(null, code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -898,8 +979,13 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
// NOTE: Wrap this in a nextTick to avoid
|
// NOTE: Wrap this in a nextTick to avoid
|
||||||
// a stack overflow if there are a lot of
|
// a stack overflow if there are a lot of
|
||||||
// existing blocks.
|
// existing blocks.
|
||||||
if (existing.hash === hash)
|
if (existing.hash === hash) {
|
||||||
|
self.emit('exists', {
|
||||||
|
height: entry.height,
|
||||||
|
hash: entry.hash
|
||||||
|
}, peer);
|
||||||
return utils.nextTick(handleOrphans);
|
return utils.nextTick(handleOrphans);
|
||||||
|
}
|
||||||
|
|
||||||
// A valid block with an already existing
|
// A valid block with an already existing
|
||||||
// height came in, that spells fork. We
|
// height came in, that spells fork. We
|
||||||
@ -928,7 +1014,7 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
self.emit('fork', {
|
self.emit('fork', {
|
||||||
height: existing.height,
|
height: existing.height,
|
||||||
expected: existing.hash,
|
expected: existing.hash,
|
||||||
received: hash,
|
received: entry.hash,
|
||||||
checkpoint: false
|
checkpoint: false
|
||||||
}, peer);
|
}, peer);
|
||||||
|
|
||||||
@ -954,9 +1040,12 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
|
|
||||||
if (!verified) {
|
if (!verified) {
|
||||||
code = Chain.codes.invalid;
|
code = Chain.codes.invalid;
|
||||||
|
self.invalid[entry.hash] = true;
|
||||||
self.emit('invalid', {
|
self.emit('invalid', {
|
||||||
height: prevHeight + 1,
|
height: entry.height,
|
||||||
hash: hash
|
hash: entry.hash,
|
||||||
|
seen: false,
|
||||||
|
chain: false
|
||||||
}, peer);
|
}, peer);
|
||||||
return done(null, code);
|
return done(null, code);
|
||||||
}
|
}
|
||||||
@ -1057,7 +1146,7 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
|
|
||||||
item = self.pending.shift();
|
item = self.pending.shift();
|
||||||
|
|
||||||
self.chain.add(item[0], item[1], item[2]);
|
self.add(item[0], item[1], item[2]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1174,7 +1263,7 @@ Chain.prototype.hashRange = function hashRange(start, end) {
|
|||||||
|
|
||||||
Chain.prototype.getLocator = function getLocator(start) {
|
Chain.prototype.getLocator = function getLocator(start) {
|
||||||
var hashes = [];
|
var hashes = [];
|
||||||
var top = this.height();
|
var top = this.height;
|
||||||
var step = 1;
|
var step = 1;
|
||||||
var i, existing;
|
var i, existing;
|
||||||
|
|
||||||
@ -1269,12 +1358,6 @@ Chain.prototype.getSize = function getSize() {
|
|||||||
// Legacy
|
// Legacy
|
||||||
Chain.prototype.size = Chain.prototype.getSize;
|
Chain.prototype.size = Chain.prototype.getSize;
|
||||||
|
|
||||||
Chain.prototype.height = function height() {
|
|
||||||
if (!this.tip)
|
|
||||||
return -1;
|
|
||||||
return this.tip.height;
|
|
||||||
};
|
|
||||||
|
|
||||||
Chain.prototype.getCurrentTarget = function getCurrentTarget() {
|
Chain.prototype.getCurrentTarget = function getCurrentTarget() {
|
||||||
if (!this.tip)
|
if (!this.tip)
|
||||||
return utils.toCompact(network.powLimit);
|
return utils.toCompact(network.powLimit);
|
||||||
|
|||||||
@ -85,7 +85,7 @@ Coin.prototype.getConfirmations = function getConfirmations(height) {
|
|||||||
if (!this.chain)
|
if (!this.chain)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
top = this.chain.height();
|
top = this.chain.height;
|
||||||
} else {
|
} else {
|
||||||
top = height;
|
top = height;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -251,7 +251,7 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
return callback(new Error('TX is spending coins that it does not have.'));
|
return callback(new Error('TX is spending coins that it does not have.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
height = self.pool.chain.height() + 1;
|
height = self.pool.chain.height + 1;
|
||||||
ts = utils.now();
|
ts = utils.now();
|
||||||
if (!tx.isFinal(height, ts)) {
|
if (!tx.isFinal(height, ts)) {
|
||||||
return callback(new Error('TX is not final.'));
|
return callback(new Error('TX is not final.'));
|
||||||
|
|||||||
@ -148,7 +148,7 @@ Peer.prototype._init = function init() {
|
|||||||
this.once('version', function() {
|
this.once('version', function() {
|
||||||
utils.debug(
|
utils.debug(
|
||||||
'Sent version (%s): height=%s',
|
'Sent version (%s): height=%s',
|
||||||
self.host, this.pool.chain.height());
|
self.host, this.pool.chain.height);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._ping.timer = setInterval(function() {
|
this._ping.timer = setInterval(function() {
|
||||||
@ -176,7 +176,7 @@ Peer.prototype._init = function init() {
|
|||||||
|
|
||||||
// Send hello
|
// Send hello
|
||||||
this._write(this.framer.version({
|
this._write(this.framer.version({
|
||||||
height: this.pool.chain.height(),
|
height: this.pool.chain.height,
|
||||||
relay: this.options.relay
|
relay: this.options.relay
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|||||||
@ -84,9 +84,6 @@ function Pool(options) {
|
|||||||
interval: options.loadInterval || 5000
|
interval: options.loadInterval || 5000
|
||||||
};
|
};
|
||||||
|
|
||||||
this._pendingBlocks = [];
|
|
||||||
this._locked = false;
|
|
||||||
|
|
||||||
this.requestTimeout = options.requestTimeout || 600000;
|
this.requestTimeout = options.requestTimeout || 600000;
|
||||||
|
|
||||||
this.chain = new bcoin.chain({
|
this.chain = new bcoin.chain({
|
||||||
@ -192,16 +189,7 @@ Pool.prototype._init = function _init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.chain.on('fork', function(data, peer) {
|
this.chain.on('fork', function(data, peer) {
|
||||||
utils.debug(
|
self.emit('fork', data, peer);
|
||||||
'Fork at height %d: expected=%s received=%s checkpoint=%s peer=%s',
|
|
||||||
data.height,
|
|
||||||
utils.revHex(data.expected),
|
|
||||||
utils.revHex(data.received),
|
|
||||||
data.checkpoint,
|
|
||||||
peer ? peer.host : ''
|
|
||||||
);
|
|
||||||
|
|
||||||
self.emit('fork', data.expected, data.received);
|
|
||||||
|
|
||||||
if (!peer)
|
if (!peer)
|
||||||
return;
|
return;
|
||||||
@ -217,21 +205,47 @@ Pool.prototype._init = function _init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.chain.on('invalid', function(data, peer) {
|
this.chain.on('invalid', function(data, peer) {
|
||||||
utils.debug(
|
|
||||||
'Invalid block at height %d: hash=%s peer=%s',
|
|
||||||
data.height,
|
|
||||||
utils.revHex(data.hash),
|
|
||||||
peer ? peer.host : ''
|
|
||||||
);
|
|
||||||
|
|
||||||
self.block.invalid[data.hash] = true;
|
|
||||||
|
|
||||||
if (!peer)
|
if (!peer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
self.setMisbehavior(peer, 100);
|
self.setMisbehavior(peer, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.chain.on('exists', function(data, peer) {
|
||||||
|
if (!peer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self.setMisbehavior(peer, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.chain.on('orphan', function(data, peer) {
|
||||||
|
var host = peer ? peer.host : 'unknown';
|
||||||
|
|
||||||
|
if (!peer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Resolve orphan chain
|
||||||
|
if (!self.options.headers) {
|
||||||
|
// Make sure the peer doesn't send us
|
||||||
|
// more than 200 orphans every 3 minutes.
|
||||||
|
if (self.isOrphaning(peer)) {
|
||||||
|
utils.debug('Peer is orphaning (%s)', host);
|
||||||
|
self.setMisbehavior(peer, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve orphan chain.
|
||||||
|
self.peers.load.loadBlocks(
|
||||||
|
self.chain.getLocator(),
|
||||||
|
self.chain.getOrphanRoot(data.hash)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Increase banscore by 10 if we're using getheaders.
|
||||||
|
if (!self.options.multiplePeers)
|
||||||
|
self.setMisbehavior(peer, 10);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.options.wallets.forEach(function(w) {
|
this.options.wallets.forEach(function(w) {
|
||||||
self.addWallet(w);
|
self.addWallet(w);
|
||||||
});
|
});
|
||||||
@ -240,7 +254,7 @@ Pool.prototype._init = function _init() {
|
|||||||
if (this.chain.isFull()) {
|
if (this.chain.isFull()) {
|
||||||
this.synced = true;
|
this.synced = true;
|
||||||
this.emit('full');
|
this.emit('full');
|
||||||
utils.debug('Chain is fully synced (height=%d).', this.chain.height());
|
utils.debug('Chain is fully synced (height=%d).', this.chain.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.startServer();
|
this.startServer();
|
||||||
@ -299,7 +313,7 @@ Pool.prototype._startTimer = function _startTimer() {
|
|||||||
self._stopInterval();
|
self._stopInterval();
|
||||||
self.synced = true;
|
self.synced = true;
|
||||||
self.emit('full');
|
self.emit('full');
|
||||||
utils.debug('Chain is fully synced (height=%d).', self.chain.height());
|
utils.debug('Chain is fully synced (height=%d).', self.chain.height);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,176 +622,53 @@ Pool.prototype._prehandleBlock = function _prehandleBlock(block, peer, callback)
|
|||||||
|
|
||||||
Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var requested;
|
||||||
|
|
||||||
if (this._locked) {
|
callback = utils.asyncify(callback);
|
||||||
this._pendingBlocks.push([block, peer, callback]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._locked = true;
|
// Fulfill our request.
|
||||||
|
requested = self._response(block);
|
||||||
|
|
||||||
function done(err, result) {
|
// Someone is sending us blocks without
|
||||||
var item;
|
// us requesting them.
|
||||||
|
if (!requested) {
|
||||||
utils.nextTick(function() {
|
utils.debug(
|
||||||
callback(err, result);
|
'Recieved unrequested block: %s (%s)',
|
||||||
|
block.rhash, peer.host);
|
||||||
self._locked = false;
|
|
||||||
|
|
||||||
if (self._pendingBlocks.length === 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
item = self._pendingBlocks.shift();
|
|
||||||
|
|
||||||
self._handleBlock(item[0], item[1], item[2]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._prehandleBlock(block, peer, function(err) {
|
this._prehandleBlock(block, peer, function(err) {
|
||||||
var requested;
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
return done(err);
|
|
||||||
|
|
||||||
// Fulfill our request.
|
|
||||||
requested = self._response(block);
|
|
||||||
|
|
||||||
// Ensure the block was not invalid last time.
|
|
||||||
// Someone might be sending us bad blocks to DoS us.
|
|
||||||
if (self.block.invalid[block.hash('hex')]) {
|
|
||||||
utils.debug('Peer is sending an invalid chain (%s)', peer.host);
|
|
||||||
self.setMisbehavior(peer, 100);
|
|
||||||
return done(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure this is not a continuation
|
|
||||||
// of an invalid chain.
|
|
||||||
if (self.block.invalid[block.prevBlock]) {
|
|
||||||
utils.debug(
|
|
||||||
'Peer is sending an invalid continuation chain (%s)',
|
|
||||||
peer.host);
|
|
||||||
self.setMisbehavior(peer, 100);
|
|
||||||
return done(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore if we already have.
|
|
||||||
if (self.chain.has(block)) {
|
|
||||||
utils.debug('Already have block %s (%s)',
|
|
||||||
self.chain.getHeight(block), peer.host);
|
|
||||||
self.setMisbehavior(peer, 1);
|
|
||||||
return done(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the block is valid.
|
|
||||||
if (!block.verify()) {
|
|
||||||
utils.debug(
|
|
||||||
'Block verification failed for %s (%s)',
|
|
||||||
block.rhash, peer.host);
|
|
||||||
self.block.invalid[block.hash('hex')] = true;
|
|
||||||
self.setMisbehavior(peer, 100);
|
|
||||||
return done(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Someone is sending us blocks without
|
|
||||||
// us requesting them.
|
|
||||||
if (!requested) {
|
|
||||||
utils.debug(
|
|
||||||
'Recieved unrequested block: %s (%s)',
|
|
||||||
block.rhash, peer.host);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve orphan chain
|
|
||||||
if (!self.options.headers) {
|
|
||||||
if (!self.chain.hasBlock(block.prevBlock)) {
|
|
||||||
// Special case for genesis block.
|
|
||||||
if (block.isGenesis())
|
|
||||||
return done(null, false);
|
|
||||||
|
|
||||||
// Make sure the peer doesn't send us
|
|
||||||
// more than 200 orphans every 3 minutes.
|
|
||||||
if (self.isOrphaning(peer)) {
|
|
||||||
utils.debug('Peer is orphaning (%s)', peer.host);
|
|
||||||
self.setMisbehavior(peer, 100);
|
|
||||||
return done(null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: If we were to emit new orphans here, we
|
|
||||||
// would not need to store full blocks as orphans.
|
|
||||||
// However, the listener would not be able to see
|
|
||||||
// the height until later.
|
|
||||||
self._addIndex(block, peer, function(err, added) {
|
|
||||||
if (err)
|
|
||||||
return done(err);
|
|
||||||
|
|
||||||
// Resolve orphan chain.
|
|
||||||
self.peers.load.loadBlocks(
|
|
||||||
self.chain.getLocator(),
|
|
||||||
self.chain.getOrphanRoot(block)
|
|
||||||
);
|
|
||||||
|
|
||||||
utils.debug('Handled orphan %s (%s)', block.rhash, peer.host);
|
|
||||||
|
|
||||||
return done(null, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!self.chain.hasBlock(block.prevBlock)) {
|
|
||||||
// Special case for genesis block.
|
|
||||||
if (block.isGenesis())
|
|
||||||
return done(null, false);
|
|
||||||
|
|
||||||
// Increase banscore by 10 if we're using getheaders.
|
|
||||||
if (!self.options.multiplePeers) {
|
|
||||||
if (self.setMisbehavior(peer, 10))
|
|
||||||
return done(null, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to index and emit/save
|
|
||||||
self._addIndex(block, peer, function(err, added) {
|
|
||||||
if (err)
|
|
||||||
return done(err);
|
|
||||||
|
|
||||||
if (added)
|
|
||||||
return done(null, true);
|
|
||||||
|
|
||||||
return done(null, false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Pool.prototype._addIndex = function _addIndex(block, peer, callback) {
|
|
||||||
var self = this;
|
|
||||||
this.chain.add(block, peer, function(err, added) {
|
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (added === 0)
|
self.chain.add(block, peer, function(err, added) {
|
||||||
return callback(null, false);
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
self.emit('chain-progress', self.chain.fillPercent(), peer);
|
if (added === 0)
|
||||||
|
return callback(null, false);
|
||||||
|
|
||||||
self.block.total += added;
|
self.emit('chain-progress', self.chain.fillPercent(), peer);
|
||||||
|
|
||||||
if (self.chain.height() % 20 === 0) {
|
self.block.total += added;
|
||||||
utils.debug(
|
|
||||||
'Got: %s from %s chain len %d blocks %d orp %d act %d queue %d target %s peers %d pending %d',
|
|
||||||
block.rhash,
|
|
||||||
new Date(block.ts * 1000).toString(),
|
|
||||||
self.chain.height(),
|
|
||||||
self.block.total,
|
|
||||||
self.chain.orphan.count,
|
|
||||||
self.request.active,
|
|
||||||
peer._blockQueue.length,
|
|
||||||
self.chain.currentTarget(),
|
|
||||||
self.peers.all.length,
|
|
||||||
self._pendingBlocks.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, true);
|
if (self.chain.height % 20 === 0) {
|
||||||
|
utils.debug(
|
||||||
|
'Got: %s from %s chain len %d blocks %d orp %d act %d queue %d target %s peers %d pending %d',
|
||||||
|
block.rhash,
|
||||||
|
new Date(block.ts * 1000).toString(),
|
||||||
|
self.chain.height,
|
||||||
|
self.block.total,
|
||||||
|
self.chain.orphan.count,
|
||||||
|
self.request.activeBlocks,
|
||||||
|
peer._blockQueue.length,
|
||||||
|
self.chain.currentTarget(),
|
||||||
|
self.peers.all.length,
|
||||||
|
self.chain.pending.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(null, true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -887,9 +778,13 @@ Pool.prototype._createPeer = function _createPeer(options) {
|
|||||||
|
|
||||||
Pool.prototype._handleTX = function _handleTX(tx, peer, callback) {
|
Pool.prototype._handleTX = function _handleTX(tx, peer, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var requested, added;
|
||||||
|
|
||||||
callback = utils.asyncify(callback);
|
callback = utils.asyncify(callback);
|
||||||
|
|
||||||
|
requested = self._response(tx);
|
||||||
|
added = self._addTX(tx, 1);
|
||||||
|
|
||||||
function addMempool(tx, peer, callback) {
|
function addMempool(tx, peer, callback) {
|
||||||
if (!self.mempool)
|
if (!self.mempool)
|
||||||
return callback();
|
return callback();
|
||||||
@ -903,14 +798,9 @@ Pool.prototype._handleTX = function _handleTX(tx, peer, callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
addMempool(tx, peer, function(err) {
|
addMempool(tx, peer, function(err) {
|
||||||
var requested, added;
|
|
||||||
|
|
||||||
if (err && self.synced)
|
if (err && self.synced)
|
||||||
utils.debug('Mempool error: %s', err.message);
|
utils.debug('Mempool error: %s', err.message);
|
||||||
|
|
||||||
requested = self._response(tx);
|
|
||||||
added = self._addTX(tx, 1);
|
|
||||||
|
|
||||||
if (added || tx.block)
|
if (added || tx.block)
|
||||||
self.emit('tx', tx, peer);
|
self.emit('tx', tx, peer);
|
||||||
|
|
||||||
@ -1403,13 +1293,13 @@ Pool.prototype.searchWallet = function(w, h) {
|
|||||||
if (height > 0) {
|
if (height > 0) {
|
||||||
// Back one week
|
// Back one week
|
||||||
if (!height || height === -1)
|
if (!height || height === -1)
|
||||||
height = this.chain.height() - (7 * 24 * 6);
|
height = this.chain.height - (7 * 24 * 6);
|
||||||
|
|
||||||
utils.nextTick(function() {
|
utils.nextTick(function() {
|
||||||
utils.debug('Wallet height: %s', height);
|
utils.debug('Wallet height: %s', height);
|
||||||
utils.debug(
|
utils.debug(
|
||||||
'Reverted chain to height=%d',
|
'Reverted chain to height=%d',
|
||||||
self.chain.height()
|
self.chain.height
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1425,7 +1315,7 @@ Pool.prototype.searchWallet = function(w, h) {
|
|||||||
utils.debug('Wallet time: %s', new Date(ts * 1000));
|
utils.debug('Wallet time: %s', new Date(ts * 1000));
|
||||||
utils.debug(
|
utils.debug(
|
||||||
'Reverted chain to height=%d (%s)',
|
'Reverted chain to height=%d (%s)',
|
||||||
self.chain.height(),
|
self.chain.height,
|
||||||
new Date(self.chain.tip.ts * 1000)
|
new Date(self.chain.tip.ts * 1000)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1406,7 +1406,7 @@ TX.prototype.avoidFeeSnipping = function avoidFeeSnipping(height) {
|
|||||||
if (!this.chain)
|
if (!this.chain)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
height = this.chain.height();
|
height = this.chain.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height === -1)
|
if (height === -1)
|
||||||
@ -1704,7 +1704,7 @@ TX.prototype.getConfirmations = function getConfirmations(height) {
|
|||||||
if (!this.chain)
|
if (!this.chain)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
top = this.chain.height();
|
top = this.chain.height;
|
||||||
} else {
|
} else {
|
||||||
top = height;
|
top = height;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user