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
|
||||
// us to redownload the blocks
|
||||
// on the new main chain.
|
||||
yield this._reset(fork.hash);
|
||||
yield this._reset(fork.hash, true);
|
||||
|
||||
// Emit disconnection events now that
|
||||
// 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) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
return yield this._reset(block);
|
||||
return yield this._reset(block, false);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
@ -1017,7 +1017,7 @@ Chain.prototype.reset = co(function* reset(block) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Chain.prototype._reset = co(function* reset(block) {
|
||||
Chain.prototype._reset = co(function* reset(block, silent) {
|
||||
var tip = yield this.db.reset(block);
|
||||
|
||||
// Reset state.
|
||||
@ -1028,6 +1028,9 @@ Chain.prototype._reset = co(function* reset(block) {
|
||||
|
||||
this.emit('tip', tip);
|
||||
|
||||
if (!silent)
|
||||
this.emit('reset', tip);
|
||||
|
||||
// Reset the orphan map completely. There may
|
||||
// have been some orphans on a forked chain we
|
||||
// no longer need.
|
||||
@ -1042,6 +1045,22 @@ Chain.prototype._reset = co(function* reset(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);
|
||||
|
||||
if (!entry)
|
||||
@ -1050,10 +1069,27 @@ Chain.prototype.replay = co(function* replay(block) {
|
||||
if (!(yield entry.isMainChain()))
|
||||
throw new Error('Cannot reset on alternate chain.');
|
||||
|
||||
if (entry.hash === this.network.genesis.hash)
|
||||
return yield this.reset(entry.hash);
|
||||
if (entry.isGenesis())
|
||||
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)
|
||||
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);
|
||||
});
|
||||
|
||||
this.chain.on('reset', function(tip) {
|
||||
self.walletdb.resetChain(tip).catch(onError);
|
||||
});
|
||||
|
||||
this.miner.on('block', function(block) {
|
||||
self.broadcast(block.toInv()).catch(onError);
|
||||
});
|
||||
@ -296,7 +300,7 @@ FullNode.prototype.watchData = function watchData(chunks) {
|
||||
*/
|
||||
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user