new verifyContext.

This commit is contained in:
Christopher Jeffrey 2016-02-16 14:27:46 -08:00
parent 02e4c9e266
commit ab610d6fa5

View File

@ -666,11 +666,9 @@ Chain.prototype.resetTime = function resetTime(ts) {
return this.resetHeight(entry.height); return this.resetHeight(entry.height);
}; };
Chain.prototype.add = function add(block, peer, callback) { Chain.prototype.add = function add(initial, peer, callback) {
var self = this; var self = this;
var initial = block;
var code = Chain.codes.unchanged; var code = Chain.codes.unchanged;
var hash, prevHash, prevHeight, entry, tip, existing, checkpoint, prev;
var total = 0; var total = 0;
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
@ -678,10 +676,10 @@ Chain.prototype.add = function add(block, peer, callback) {
if (this._locked) if (this._locked)
return callback(null, total); return callback(null, total);
// (function next(block) { (function next(block) {
(function next() { var hash = block.hash('hex');
hash = block.hash('hex'); var prevHash = block.prevBlock;
prevHash = block.prevBlock; var prevHeight, entry, existing, checkpoint, prev;
// Find the previous block height/index. // Find the previous block height/index.
prevHeight = self.heightLookup[prevHash]; prevHeight = self.heightLookup[prevHash];
@ -746,84 +744,93 @@ Chain.prototype.add = function add(block, peer, callback) {
height: prevHeight + 1 height: prevHeight + 1
}); });
// Add entry if we do not have it (or if // Fork at checkpoint
// there is another entry at its height) // Block did not match the checkpoint. The
// chain could be reset to the last sane
// checkpoint, but it really isn't necessary,
// so we don't do it. The misbehaving peer has
// been killed and hopefully we find a peer
// who isn't trying to fool us.
checkpoint = network.checkpoints[entry.height];
if (checkpoint) {
self.emit('checkpoint', entry.height, entry.hash, checkpoint);
if (hash !== checkpoint) {
// Resetting to the last checkpoint _really_ isn't
// necessary (even bitcoind doesn't do it), but it
// could be used if you want to be on the overly
// safe (see: paranoid) side.
// this.resetLastCheckpoint(entry.height);
code = Chain.codes.badCheckpoint;
self.emit('fork', {
height: entry.height,
expected: network.checkpoints[entry.height],
received: entry.hash,
checkpoint: true
});
return done(null, code);
}
}
// See if the entry already exists.
existing = self.db.get(entry.height); existing = self.db.get(entry.height);
if (!existing || existing.hash !== hash) {
assert(self.heightLookup[entry.hash] == null); // Entry already exists.
if (existing) {
// We already have this block. Do regular
// orphan resolution (won't do anything).
if (existing.hash === hash)
return 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
// don't store by hash so we can't compare // don't store by hash so we can't compare
// chainworks. We reset the chain, find a // chainworks. We reset the chain, find a
// new peer, and wait to see who wins. // new peer, and wait to see who wins.
if (existing) { assert(self.heightLookup[entry.hash] == null);
// The tip has more chainwork, it is a
// higher height than the entry. This is // The tip has more chainwork, it is a
// not an alternate tip. Ignore it. // higher height than the entry. This is
if (self.tip.chainwork.cmp(entry.chainwork) > 0) { // not an alternate tip. Ignore it.
code = Chain.codes.unchanged; if (self.tip.chainwork.cmp(entry.chainwork) > 0) {
return done(null, code); code = Chain.codes.unchanged;
} return done(null, code);
// Get _our_ tip as opposed to
// the attempted alternate tip.
tip = existing;
// The block has equal chainwork (an
// alternate tip). Reset the chain, find
// a new peer, and wait to see who wins.
self._locked = true;
return self._removeBlock(tip.hash, function(err) {
self._locked = false;
if (err)
return done(err);
self.resetHeight(entry.height - 1);
self.emit('fork', {
height: prevHeight + 1,
expected: tip.hash,
received: hash,
checkpoint: false
}, peer);
code = Chain.codes.forked;
return done(null, code);
});
} }
// Fork at checkpoint // The block has equal chainwork (an
// Block did not match the checkpoint. The // alternate tip). Reset the chain, find
// chain could be reset to the last sane // a new peer, and wait to see who wins.
// checkpoint, but it really isn't necessary, self._locked = true;
// so we don't do it. The misbehaving peer has return self._removeBlock(existing.hash, function(err) {
// been killed and hopefully we find a peer self._locked = false;
// who isn't trying to fool us. if (err)
checkpoint = network.checkpoints[entry.height]; return done(err);
if (checkpoint) { self.resetHeight(entry.height - 1);
self.emit('checkpoint', entry.height, entry.hash, checkpoint); self.emit('fork', {
if (hash !== checkpoint) { height: prevHeight + 1,
// Resetting to the last checkpoint _really_ isn't expected: existing.hash,
// necessary (even bitcoind doesn't do it), but it received: hash,
// could be used if you want to be on the overly checkpoint: false
// safe (see: paranoid) side. }, peer);
// this.resetLastCheckpoint(entry.height); code = Chain.codes.forked;
code = Chain.codes.badCheckpoint; return done(null, code);
self.emit('fork', { });
height: entry.height, }
expected: network.checkpoints[entry.height],
received: entry.hash,
checkpoint: true
});
return done(null, code);
}
}
// Lookup previous entry. // Add entry if we do not have it.
prev = self.db.get(prevHeight); assert(self.heightLookup[entry.hash] == null);
assert(prev);
// Do "contextual" verification on our block // Lookup previous entry.
// now that we're certain its previous prev = self.db.get(prevHeight);
// block is in the chain. assert(prev);
// self._verifyContext(block, prev, function(err, verified) {
if (!block.verifyContext(prev)) { // Do "contextual" verification on our block
// now that we're certain its previous
// block is in the chain.
self._verifyContext(block, prev, function(err, verified) {
if (err)
return done(err);
if (!verified) {
code = Chain.codes.invalid; code = Chain.codes.invalid;
self.emit('invalid', { self.emit('invalid', {
height: prevHeight + 1, height: prevHeight + 1,
@ -869,9 +876,7 @@ Chain.prototype.add = function add(block, peer, callback) {
handleOrphans(); handleOrphans();
}); });
} else { });
handleOrphans();
}
function handleOrphans() { function handleOrphans() {
if (!self.orphan.map[hash]) if (!self.orphan.map[hash])
@ -884,9 +889,9 @@ Chain.prototype.add = function add(block, peer, callback) {
self.orphan.count--; self.orphan.count--;
self.orphan.size -= block.getSize(); self.orphan.size -= block.getSize();
next(); next(block);
} }
})(); })(initial);
function done(err, code) { function done(err, code) {
// Failsafe for large orphan chains. Do not // Failsafe for large orphan chains. Do not