txdb: improve insertion handling.
This commit is contained in:
parent
f629c6db5c
commit
7506d89947
@ -418,15 +418,17 @@ TXDB.prototype.getOrphans = co(function* getOrphans(hash, index) {
|
||||
*/
|
||||
|
||||
TXDB.prototype.verify = co(function* verify(tx, info) {
|
||||
var spends = [];
|
||||
var orphans = [];
|
||||
var removed = {};
|
||||
var i, input, prevout, address, coin, spent, conflict;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
return true;
|
||||
return orphans;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
|
||||
address = input.getHash('hex');
|
||||
|
||||
// Only bother if this input is ours.
|
||||
@ -442,21 +444,21 @@ TXDB.prototype.verify = co(function* verify(tx, info) {
|
||||
// Skip invalid transactions
|
||||
if (this.options.verify) {
|
||||
if (!(yield tx.verifyInputAsync(i)))
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
input.coin = null;
|
||||
|
||||
spent = yield this.isSpent(prevout.hash, prevout.index);
|
||||
|
||||
// Are we double-spending?
|
||||
// Replace older txs with newer ones.
|
||||
if (!spent)
|
||||
// Orphan until we see a parent transaction.
|
||||
if (!spent) {
|
||||
orphans[i] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We must be double-spending.
|
||||
coin = yield this.getSpentCoin(spent, prevout);
|
||||
|
||||
if (!coin)
|
||||
@ -467,19 +469,42 @@ TXDB.prototype.verify = co(function* verify(tx, info) {
|
||||
// Skip invalid transactions
|
||||
if (this.options.verify) {
|
||||
if (!(yield tx.verifyInputAsync(i)))
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
spends[i] = spent;
|
||||
}
|
||||
|
||||
// Once we've verified everything to the
|
||||
// best of our ability, go through and
|
||||
// attempt to remove double-spenders.
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
spent = spends[i];
|
||||
|
||||
if (!spent)
|
||||
continue;
|
||||
|
||||
this.logger.warning('Handling conflicting tx: %s.',
|
||||
utils.revHex(spent.hash));
|
||||
|
||||
// Remove the older double spender.
|
||||
conflict = yield this.removeConflict(spent.hash, tx);
|
||||
// We have to maintain a spent list
|
||||
// and pass it in. It needs to know
|
||||
// which txs are considered "deleted"
|
||||
// within this context.
|
||||
conflict = yield this.removeConflict(spent.hash, tx, removed);
|
||||
|
||||
// Spender was not removed, the current
|
||||
// transaction is not elligible to be added.
|
||||
if (!conflict)
|
||||
return false;
|
||||
// Be sure to clear the batch, lest we
|
||||
// remove other transactions on behalf of
|
||||
// a non-eligible tx.
|
||||
if (!conflict) {
|
||||
this.wallet.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.warning('Removed conflict: %s.', conflict.tx.rhash);
|
||||
|
||||
@ -487,7 +512,7 @@ TXDB.prototype.verify = co(function* verify(tx, info) {
|
||||
this.emit('conflict', conflict.tx, conflict.info);
|
||||
}
|
||||
|
||||
return true;
|
||||
return orphans;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -500,7 +525,7 @@ TXDB.prototype.verify = co(function* verify(tx, info) {
|
||||
|
||||
TXDB.prototype.resolveOrphans = co(function* resolveOrphans(tx, index) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, orphans, coin, input, orphan, key;
|
||||
var i, orphans, coin, input, spender, orphan;
|
||||
|
||||
orphans = yield this.getOrphans(hash, index);
|
||||
|
||||
@ -511,33 +536,39 @@ TXDB.prototype.resolveOrphans = co(function* resolveOrphans(tx, index) {
|
||||
|
||||
coin = Coin.fromTX(tx, index);
|
||||
|
||||
// Add input to orphan
|
||||
// Add input to resolved orphan.
|
||||
for (i = 0; i < orphans.length; i++) {
|
||||
orphan = orphans[i];
|
||||
input = orphan.input;
|
||||
spender = orphan.input;
|
||||
tx = orphan.tx;
|
||||
|
||||
// Probably removed by some other means.
|
||||
if (!tx)
|
||||
continue;
|
||||
|
||||
tx.inputs[input.index].coin = coin;
|
||||
input = tx.inputs[spender.index];
|
||||
input.coin = coin;
|
||||
|
||||
assert(tx.inputs[input.index].prevout.hash === hash);
|
||||
assert(tx.inputs[input.index].prevout.index === index);
|
||||
assert(input.prevout.hash === hash);
|
||||
assert(input.prevout.index === index);
|
||||
|
||||
// Verify that input script is correct, if not - add
|
||||
// output to unspent and remove orphan from storage
|
||||
if (!this.options.verify || (yield tx.verifyInputAsync(input.index))) {
|
||||
key = layout.d(input.hash, input.index);
|
||||
this.put(key, coin.toRaw());
|
||||
if (!this.options.verify || (yield tx.verifyInputAsync(spender.index))) {
|
||||
// Add the undo coin record which we never had.
|
||||
this.put(layout.d(spender.hash, spender.index), coin.toRaw());
|
||||
// Add the spender record back in case any evil
|
||||
// transactions were removed with lazyRemove.
|
||||
this.put(layout.s(hash, index), spender.toRaw());
|
||||
return true;
|
||||
}
|
||||
|
||||
yield this.lazyRemove(tx);
|
||||
}
|
||||
|
||||
// Just going to be added again outside.
|
||||
// We had orphans, but they were invalid. The
|
||||
// balance will be (incorrectly) added outside.
|
||||
// Subtract to compensate.
|
||||
this.balance.sub(coin);
|
||||
|
||||
return false;
|
||||
@ -592,9 +623,9 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
|
||||
// Verify and get coins.
|
||||
// This potentially removes double-spenders.
|
||||
result = yield this.verify(tx, info);
|
||||
orphans = yield this.verify(tx, info);
|
||||
|
||||
if (!result)
|
||||
if (!orphans)
|
||||
return false;
|
||||
|
||||
hash = tx.hash('hex');
|
||||
@ -640,8 +671,9 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
spender = Outpoint.fromTX(tx, i).toRaw();
|
||||
this.put(layout.s(prevout.hash, prevout.index), spender);
|
||||
|
||||
// Add orphan, if no parent transaction is yet known
|
||||
if (!input.coin) {
|
||||
// Add orphan if no parent transaction known.
|
||||
// Do not disconnect any coins.
|
||||
if (orphans[i]) {
|
||||
yield this.addOrphan(prevout, spender);
|
||||
continue;
|
||||
}
|
||||
@ -669,6 +701,9 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
|
||||
orphans = yield this.resolveOrphans(tx, i);
|
||||
|
||||
// If this transaction resolves an orphan,
|
||||
// it should not connect coins as they are
|
||||
// already spent by the orphan it resolved.
|
||||
if (orphans)
|
||||
continue;
|
||||
|
||||
@ -707,7 +742,7 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
* @returns {Promise} - Returns Boolean.
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) {
|
||||
TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref, removed) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var info;
|
||||
|
||||
@ -736,7 +771,7 @@ TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) {
|
||||
return;
|
||||
}
|
||||
|
||||
info = yield this.removeRecursive(tx);
|
||||
info = yield this.removeRecursive(tx, removed);
|
||||
|
||||
return new Conflict(tx, info);
|
||||
});
|
||||
|
||||
@ -1017,6 +1017,15 @@ Wallet.prototype.drop = function drop() {
|
||||
return this.db.drop(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear batch.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Wallet.prototype.clear = function clear() {
|
||||
return this.db.clear(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Save batch.
|
||||
* @returns {Promise}
|
||||
|
||||
@ -308,6 +308,17 @@ WalletDB.prototype.drop = function drop(wallet) {
|
||||
batch.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear batch.
|
||||
* @private
|
||||
* @param {WalletID} wid
|
||||
*/
|
||||
|
||||
WalletDB.prototype.clear = function clear(wallet) {
|
||||
var batch = this.batch(wallet);
|
||||
batch.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get batch.
|
||||
* @private
|
||||
|
||||
Loading…
Reference in New Issue
Block a user