walletdb: start marking the start block.

This commit is contained in:
Christopher Jeffrey 2016-11-16 09:26:06 -08:00
parent 025a5b9138
commit 8377a5082e
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 70 additions and 21 deletions

View File

@ -1086,7 +1086,7 @@ Chain.prototype._replay = co(function* replay(block) {
Chain.prototype.scan = co(function* scan(start, filter, iter) {
var unlock = yield this.locker.lock();
try {
return yield this.db.scan(block, filter, iter);
return yield this.db.scan(start, filter, iter);
} finally {
unlock();
}

View File

@ -24,6 +24,7 @@ function ChainState() {
this.startHeight = -1;
this.startHash = constants.NULL_HASH;
this.height = -1;
this.marked = false;
}
/**
@ -36,6 +37,7 @@ ChainState.prototype.clone = function clone() {
state.startHeight = this.startHeight;
state.startHash = this.startHash;
state.height = this.height;
state.marked = this.marked;
return state;
};
@ -47,9 +49,15 @@ ChainState.prototype.clone = function clone() {
ChainState.prototype.fromRaw = function fromRaw(data) {
var p = new BufferReader(data);
this.startHeight = p.readU32();
this.startHash = p.readHash('hex');
this.height = p.readU32();
this.marked = true;
if (p.left() > 0)
this.marked = p.readU8() === 1;
return this;
};
@ -75,6 +83,7 @@ ChainState.prototype.toRaw = function toRaw(writer) {
p.writeU32(this.startHeight);
p.writeHash(this.startHash);
p.writeU32(this.height);
p.writeU8(this.marked ? 1 : 0);
if (!writer)
p = p.render();

View File

@ -504,6 +504,7 @@ TXDB.prototype.resolve = co(function* resolve(tx, block) {
*/
TXDB.prototype.verifyInputs = co(function* verifyInputs(tx, block) {
var flags = constants.flags.MANDATORY_VERIFY_FLAGS;
var hash = tx.hash('hex');
var hasOrphans = false;
var orphans = [];
@ -527,7 +528,7 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx, block) {
if (coin) {
if (this.options.verify && tx.height === -1) {
input.coin = coin;
if (!(yield tx.verifyInputAsync(i)))
if (!(yield tx.verifyInputAsync(i, flags)))
return false;
}
@ -547,7 +548,7 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx, block) {
if (coin) {
if (this.options.verify && tx.height === -1) {
input.coin = coin;
if (!(yield tx.verifyInputAsync(i)))
if (!(yield tx.verifyInputAsync(i, flags)))
return false;
}
continue;
@ -618,6 +619,7 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx, block) {
*/
TXDB.prototype.resolveOutputs = co(function* resolveOutputs(tx, block, resolved) {
var flags = constants.flags.MANDATORY_VERIFY_FLAGS;
var hash = tx.hash('hex');
var i, j, input, output, key;
var orphans, orphan, coin, valid;
@ -661,7 +663,7 @@ TXDB.prototype.resolveOutputs = co(function* resolveOutputs(tx, block, resolved)
// We can finally verify this input.
if (this.options.verify && orphan.tx.height === -1) {
input.coin = coin;
valid = yield orphan.tx.verifyInputAsync(orphan.index);
valid = yield orphan.tx.verifyInputAsync(orphan.index, flags);
}
// If it's valid and fully resolved,

View File

@ -1541,7 +1541,7 @@ WalletDB.prototype.init = co(function* init() {
'Initializing WalletDB chain state at %s (%d).',
utils.revHex(tip.hash), tip.height);
yield this.resetState(tip);
yield this.resetState(tip, false);
});
/**
@ -1564,7 +1564,7 @@ WalletDB.prototype.getState = co(function* getState() {
* @returns {Promise}
*/
WalletDB.prototype.resetState = co(function* resetState(tip) {
WalletDB.prototype.resetState = co(function* resetState(tip, marked) {
var batch = this.db.batch();
var state = this.state.clone();
var iter, item;
@ -1592,6 +1592,7 @@ WalletDB.prototype.resetState = co(function* resetState(tip) {
state.startHeight = tip.height;
state.startHash = tip.hash;
state.height = tip.height;
state.marked = marked;
batch.put(layout.h(tip.height), tip.toHash());
batch.put(layout.R, state.toRaw());
@ -1646,6 +1647,22 @@ WalletDB.prototype.syncState = co(function* syncState(tip) {
this.state = state;
});
/**
* Mark the start block once a confirmed tx is seen.
* @param {BlockMeta} tip
* @returns {Promise}
*/
WalletDB.prototype.maybeMark = co(function* maybeMark(tip) {
if (this.state.marked)
return;
this.logger.info('Marking WalletDB start block at %s (%d).',
utils.revHex(tip.hash), tip.height);
yield this.resetState(tip, true);
});
/**
* Get a block->wallet map.
* @param {Number} height
@ -1770,14 +1787,14 @@ WalletDB.prototype.getTip = co(function* getTip() {
*/
WalletDB.prototype.rollback = co(function* rollback(height) {
var tip;
var tip, marked;
if (height > this.state.height)
throw new Error('WDB: Cannot rollback to the future.');
if (height === this.state.height) {
this.logger.debug('Rolled back to same height (%d).', height);
return;
return true;
}
this.logger.info(
@ -1789,7 +1806,7 @@ WalletDB.prototype.rollback = co(function* rollback(height) {
if (tip) {
yield this.revert(tip.height);
yield this.syncState(tip);
return;
return true;
}
tip = new BlockMeta();
@ -1797,6 +1814,7 @@ WalletDB.prototype.rollback = co(function* rollback(height) {
if (height >= this.state.startHeight) {
tip.height = this.state.startHeight;
tip.hash = this.state.startHash;
marked = this.state.marked;
this.logger.warning(
'Rolling back WalletDB to start block (%d).',
@ -1804,12 +1822,15 @@ WalletDB.prototype.rollback = co(function* rollback(height) {
} else {
tip.height = 0;
tip.hash = this.network.genesis.hash;
marked = false;
this.logger.warning('Rolling back WalletDB to genesis block.');
}
yield this.revert(tip.height);
yield this.resetState(tip);
yield this.resetState(tip, marked);
return false;
});
/**
@ -1899,6 +1920,7 @@ WalletDB.prototype._addBlock = co(function* addBlock(entry, txs) {
throw new Error('WDB: Bad connection (height mismatch).');
}
// Sync the state to the new tip.
yield this.syncState(tip);
if (this.options.useCheckpoints) {
@ -1962,6 +1984,7 @@ WalletDB.prototype._removeBlock = co(function* removeBlock(entry) {
if (!prev)
throw new Error('WDB: Bad disconnection (no previous block).');
// Get the map of txids->wids.
block = yield this.getBlockMap(tip.height);
if (!block) {
@ -1974,6 +1997,7 @@ WalletDB.prototype._removeBlock = co(function* removeBlock(entry) {
yield this._unconfirm(tx);
}
// Sync the state to the previous tip.
yield this.syncState(prev);
this.logger.warning('Disconnected wallet block %s (tx=%d).',
@ -2036,6 +2060,13 @@ WalletDB.prototype._insert = co(function* insert(tx, block) {
'Incoming transaction for %d wallets in WalletDB (%s).',
wids.length, tx.rhash);
// If this is our first transaction
// in a block, set the start block here.
if (block)
yield this.maybeMark(block);
// Insert the transaction
// into every matching wallet.
for (i = 0; i < wids.length; i++) {
wid = wids[i];
wallet = yield this.get(wid);
@ -2101,7 +2132,13 @@ 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);
// Try to rollback.
if (yield this.rollback(entry.height))
return;
// If we rolled back to the
// start block, we need a rescan.
yield this.scan();
});
/*

View File

@ -85,6 +85,7 @@ describe('Chain', function() {
it('should mine a block', cob(function* () {
var block = yield miner.mineBlock();
assert(block);
yield chain.add(block);
}));
it('should mine competing chains', cob(function* () {
@ -121,15 +122,15 @@ describe('Chain', function() {
yield co.timeout(100);
balance = yield wallet.getBalance();
assert.equal(balance.unconfirmed, 500 * 1e8);
assert.equal(balance.confirmed, 500 * 1e8);
assert.equal(balance.unconfirmed, 550 * 1e8);
assert.equal(balance.confirmed, 550 * 1e8);
}));
it('should handle a reorg', cob(function* () {
var entry, block, forked;
assert.equal(walletdb.state.height, chain.height);
assert.equal(chain.height, 10);
assert.equal(chain.height, 11);
entry = yield chain.db.get(tip2.hash);
assert(entry);
@ -158,8 +159,8 @@ describe('Chain', function() {
yield co.timeout(100);
balance = yield wallet.getBalance();
assert.equal(balance.unconfirmed, 1050 * 1e8);
assert.equal(balance.confirmed, 550 * 1e8);
assert.equal(balance.unconfirmed, 1100 * 1e8);
assert.equal(balance.confirmed, 600 * 1e8);
}));
it('should check main chain', cob(function* () {
@ -221,8 +222,8 @@ describe('Chain', function() {
yield co.timeout(100);
balance = yield wallet.getBalance();
assert.equal(balance.unconfirmed, 1200 * 1e8);
assert.equal(balance.confirmed, 700 * 1e8);
assert.equal(balance.unconfirmed, 1250 * 1e8);
assert.equal(balance.confirmed, 750 * 1e8);
assert(wallet.account.receiveDepth >= 8);
assert(wallet.account.changeDepth >= 7);
@ -230,7 +231,7 @@ describe('Chain', function() {
assert.equal(walletdb.state.height, chain.height);
txs = yield wallet.getHistory();
assert.equal(txs.length, 44);
assert.equal(txs.length, 45);
}));
it('should get tips and remove chains', cob(function* () {
@ -255,7 +256,7 @@ describe('Chain', function() {
return Promise.resolve();
});
assert.equal(total, 25);
assert.equal(total, 26);
}));
it('should activate csv', cob(function* () {
@ -265,7 +266,7 @@ describe('Chain', function() {
state = yield chain.getState(prev, 'csv');
assert(state === 0);
for (i = 0; i < 418; i++) {
for (i = 0; i < 417; i++) {
block = yield miner.mineBlock();
yield chain.add(block);
switch (chain.height) {