Skip to content

Commit

Permalink
chain: handle chain resetting more gracefully.
Browse files Browse the repository at this point in the history
  • Loading branch information
chjj committed Nov 14, 2016
1 parent 6d3401f commit 025a5b9
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 8 deletions.
50 changes: 43 additions & 7 deletions lib/chain/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();
}
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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)
Expand All @@ -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, 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}
*/

yield this.reset(entry.prevBlock);
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();
}
});

/**
Expand Down Expand Up @@ -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);
});

/**
Expand Down
6 changes: 5 additions & 1 deletion lib/node/fullnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down Expand Up @@ -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);
};

/**
Expand Down
4 changes: 4 additions & 0 deletions lib/node/spvnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
};

/**
Expand Down
29 changes: 29 additions & 0 deletions lib/wallet/walletdb.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down

0 comments on commit 025a5b9

Please sign in to comment.