chain: handle chain resetting more gracefully.
This commit is contained in:
parent
6d3401f08b
commit
025a5b9138
@ -824,7 +824,7 @@ Chain.prototype.reorganizeSPV = co(function* reorganizeSPV(competitor, block) {
|
|||||||
// to the fork block, causing
|
// to the fork block, causing
|
||||||
// us to redownload the blocks
|
// us to redownload the blocks
|
||||||
// on the new main chain.
|
// on the new main chain.
|
||||||
yield this._reset(fork.hash);
|
yield this._reset(fork.hash, true);
|
||||||
|
|
||||||
// Emit disconnection events now that
|
// Emit disconnection events now that
|
||||||
// the chain has successfully reset.
|
// the chain has successfully reset.
|
||||||
@ -1004,7 +1004,7 @@ Chain.prototype.saveAlternate = co(function* saveAlternate(entry, block, prev) {
|
|||||||
Chain.prototype.reset = co(function* reset(block) {
|
Chain.prototype.reset = co(function* reset(block) {
|
||||||
var unlock = yield this.locker.lock();
|
var unlock = yield this.locker.lock();
|
||||||
try {
|
try {
|
||||||
return yield this._reset(block);
|
return yield this._reset(block, false);
|
||||||
} finally {
|
} finally {
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
@ -1017,7 +1017,7 @@ Chain.prototype.reset = co(function* reset(block) {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Chain.prototype._reset = co(function* reset(block) {
|
Chain.prototype._reset = co(function* reset(block, silent) {
|
||||||
var tip = yield this.db.reset(block);
|
var tip = yield this.db.reset(block);
|
||||||
|
|
||||||
// Reset state.
|
// Reset state.
|
||||||
@ -1028,6 +1028,9 @@ Chain.prototype._reset = co(function* reset(block) {
|
|||||||
|
|
||||||
this.emit('tip', tip);
|
this.emit('tip', tip);
|
||||||
|
|
||||||
|
if (!silent)
|
||||||
|
this.emit('reset', tip);
|
||||||
|
|
||||||
// Reset the orphan map completely. There may
|
// Reset the orphan map completely. There may
|
||||||
// have been some orphans on a forked chain we
|
// have been some orphans on a forked chain we
|
||||||
// no longer need.
|
// no longer need.
|
||||||
@ -1042,6 +1045,22 @@ Chain.prototype._reset = co(function* reset(block) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Chain.prototype.replay = co(function* replay(block) {
|
Chain.prototype.replay = co(function* replay(block) {
|
||||||
|
var unlock = yield this.locker.lock();
|
||||||
|
try {
|
||||||
|
return yield this._replay(block);
|
||||||
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the chain without a lock.
|
||||||
|
* @private
|
||||||
|
* @param {Hash|Number} block - hash/height
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Chain.prototype._replay = co(function* replay(block) {
|
||||||
var entry = yield this.db.get(block);
|
var entry = yield this.db.get(block);
|
||||||
|
|
||||||
if (!entry)
|
if (!entry)
|
||||||
@ -1050,10 +1069,27 @@ Chain.prototype.replay = co(function* replay(block) {
|
|||||||
if (!(yield entry.isMainChain()))
|
if (!(yield entry.isMainChain()))
|
||||||
throw new Error('Cannot reset on alternate chain.');
|
throw new Error('Cannot reset on alternate chain.');
|
||||||
|
|
||||||
if (entry.hash === this.network.genesis.hash)
|
if (entry.isGenesis())
|
||||||
return yield this.reset(entry.hash);
|
return yield this._reset(entry.hash, true);
|
||||||
|
|
||||||
yield this.reset(entry.prevBlock);
|
yield this._reset(entry.prevBlock, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan the blockchain for transactions containing specified address hashes.
|
||||||
|
* @param {Hash} start - Block hash to start at.
|
||||||
|
* @param {Bloom} filter - Bloom filter containing tx and address hashes.
|
||||||
|
* @param {Function} iter - Iterator.
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Chain.prototype.scan = co(function* scan(start, filter, iter) {
|
||||||
|
var unlock = yield this.locker.lock();
|
||||||
|
try {
|
||||||
|
return yield this.db.scan(block, filter, iter);
|
||||||
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1086,7 +1122,7 @@ Chain.prototype._resetTime = co(function* resetTime(ts) {
|
|||||||
if (!entry)
|
if (!entry)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
yield this._reset(entry.height);
|
yield this._reset(entry.height, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -223,6 +223,10 @@ FullNode.prototype._init = function _init() {
|
|||||||
self.mempool.removeBlock(block).catch(onError);
|
self.mempool.removeBlock(block).catch(onError);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.chain.on('reset', function(tip) {
|
||||||
|
self.walletdb.resetChain(tip).catch(onError);
|
||||||
|
});
|
||||||
|
|
||||||
this.miner.on('block', function(block) {
|
this.miner.on('block', function(block) {
|
||||||
self.broadcast(block.toInv()).catch(onError);
|
self.broadcast(block.toInv()).catch(onError);
|
||||||
});
|
});
|
||||||
@ -296,7 +300,7 @@ FullNode.prototype.watchData = function watchData(chunks) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
FullNode.prototype.scan = function scan(start, filter, iter) {
|
FullNode.prototype.scan = function scan(start, filter, iter) {
|
||||||
return this.chain.db.scan(start, filter, iter);
|
return this.chain.scan(start, filter, iter);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -153,6 +153,10 @@ SPVNode.prototype._init = function _init() {
|
|||||||
this.chain.on('disconnect', function(entry, block) {
|
this.chain.on('disconnect', function(entry, block) {
|
||||||
self.walletdb.removeBlock(entry).catch(onError);
|
self.walletdb.removeBlock(entry).catch(onError);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.chain.on('reset', function(tip) {
|
||||||
|
self.walletdb.resetChain(tip).catch(onError);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -2075,6 +2075,35 @@ WalletDB.prototype._unconfirm = co(function* unconfirm(tx) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a chain reset.
|
||||||
|
* @param {ChainEntry} entry
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.prototype.resetChain = co(function* resetChain(entry) {
|
||||||
|
var unlock = yield this.txLock.lock();
|
||||||
|
try {
|
||||||
|
return yield this._resetChain(entry);
|
||||||
|
} finally {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a chain reset without a lock.
|
||||||
|
* @private
|
||||||
|
* @param {ChainEntry} entry
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
WalletDB.prototype._resetChain = co(function* resetChain(entry) {
|
||||||
|
if (entry.height > this.state.height)
|
||||||
|
throw new Error('WDB: Bad reset height.');
|
||||||
|
|
||||||
|
yield this.scan(entry.height);
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helpers
|
* Helpers
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user