txdb: handle coins and double spenders differently.
This commit is contained in:
parent
e3a6d7f35e
commit
b1996b9717
@ -510,6 +510,9 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tx.height === -1)
|
||||
return false;
|
||||
|
||||
coin = yield this.getSpentCoin(spent, prevout);
|
||||
assert(coin);
|
||||
|
||||
@ -625,90 +628,30 @@ TXDB.prototype.resolveOutputs = co(function* resolveOutputs(tx, resolved) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
TXDB.prototype.getInputs = co(function* getInputs(tx, info) {
|
||||
var spends = [];
|
||||
var coins = [];
|
||||
var removed = {};
|
||||
var i, input, prevout, coin, spent, conflict;
|
||||
TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, info) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, input, prevout, spent;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
return coins;
|
||||
return;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
|
||||
// Try to get the coin we're redeeming.
|
||||
coin = yield this.getCoin(prevout.hash, prevout.index);
|
||||
|
||||
if (coin) {
|
||||
input.coin = coin;
|
||||
coins.push(coin);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is it already spent?
|
||||
spent = yield this.isSpent(prevout.hash, prevout.index);
|
||||
|
||||
// If we have no coin or spend
|
||||
// record, this is not our input.
|
||||
// This is assuming everything came
|
||||
// in order!
|
||||
if (!spent) {
|
||||
coins.push(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Yikes, we're double spending. We
|
||||
// still need the coin for after we
|
||||
// resolve the conflict.
|
||||
coin = yield this.getSpentCoin(spent, prevout);
|
||||
assert(coin);
|
||||
|
||||
input.coin = coin;
|
||||
coins.push(coin);
|
||||
|
||||
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));
|
||||
// Did _we_ spend it?
|
||||
if (spent.hash === hash)
|
||||
continue;
|
||||
|
||||
// Remove the older double spender.
|
||||
// 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.
|
||||
// Be sure to clear the batch, lest we
|
||||
// remove other transactions on behalf of
|
||||
// a non-eligible tx.
|
||||
if (!conflict) {
|
||||
this.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.warning('Removed conflict: %s.', conflict.tx.rhash);
|
||||
|
||||
// Emit the _removed_ transaction.
|
||||
this.emit('conflict', conflict.tx, conflict.info);
|
||||
// Remove the double spender.
|
||||
yield this.removeConflict(spent.hash, tx);
|
||||
}
|
||||
|
||||
return coins;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -751,6 +694,14 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
|
||||
assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
|
||||
|
||||
if (tx.height === -1) {
|
||||
if (yield this.isDoubleSpend(tx))
|
||||
return false;
|
||||
} else {
|
||||
// This potentially removes double-spenders.
|
||||
yield this.removeConflicts(tx, info);
|
||||
}
|
||||
|
||||
// Attempt to confirm tx before adding it.
|
||||
result = yield this.confirm(tx, info);
|
||||
|
||||
@ -758,13 +709,6 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
if (result)
|
||||
return true;
|
||||
|
||||
// Verify and get coins.
|
||||
// This potentially removes double-spenders.
|
||||
coins = yield this.getInputs(tx, info);
|
||||
|
||||
if (!coins)
|
||||
return false;
|
||||
|
||||
hash = tx.hash('hex');
|
||||
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
@ -794,7 +738,7 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
coin = coins[i];
|
||||
coin = yield this.getCoin(prevout.hash, prevout.index);
|
||||
|
||||
// Only bother if this input is ours.
|
||||
if (!coin)
|
||||
@ -868,37 +812,24 @@ TXDB.prototype._add = co(function* add(tx, info) {
|
||||
* @returns {Promise} - Returns Boolean.
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref, removed) {
|
||||
TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var info;
|
||||
|
||||
if (!tx)
|
||||
throw new Error('Could not find spender.');
|
||||
assert(tx);
|
||||
|
||||
if (tx.height !== -1) {
|
||||
// If spender is confirmed and replacement
|
||||
// is not confirmed, do nothing.
|
||||
if (ref.height === -1)
|
||||
return;
|
||||
this.logger.warning('Handling conflicting tx: %s.', utils.revHex(hash));
|
||||
|
||||
// If both are confirmed but replacement
|
||||
// is older than spender, do nothing.
|
||||
if (ref.height < tx.height)
|
||||
return;
|
||||
} else {
|
||||
// If spender is unconfirmed and replacement
|
||||
// is confirmed, do nothing.
|
||||
if (ref.height === -1) {
|
||||
// If both are unconfirmed but replacement
|
||||
// is older than spender, do nothing.
|
||||
if (ref.ps < tx.ps)
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.drop();
|
||||
|
||||
info = yield this.removeRecursive(tx, removed);
|
||||
info = yield this.removeRecursive(tx);
|
||||
|
||||
return new Conflict(tx, info);
|
||||
this.start();
|
||||
|
||||
this.logger.warning('Removed conflict: %s.', tx.rhash);
|
||||
|
||||
// Emit the _removed_ transaction.
|
||||
this.emit('conflict', tx, info);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -909,38 +840,32 @@ TXDB.prototype.removeConflict = co(function* removeConflict(hash, ref, removed)
|
||||
* @returns {Promise} - Returns Boolean.
|
||||
*/
|
||||
|
||||
TXDB.prototype.removeRecursive = co(function* removeRecursive(tx, removed) {
|
||||
TXDB.prototype.removeRecursive = co(function* removeRecursive(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, spent, stx, info;
|
||||
|
||||
if (!removed)
|
||||
removed = {};
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
spent = yield this.isSpent(hash, i);
|
||||
|
||||
if (!spent)
|
||||
continue;
|
||||
|
||||
if (removed[spent.hash])
|
||||
continue;
|
||||
|
||||
removed[spent.hash] = true;
|
||||
|
||||
// Remove all of the spender's spenders first.
|
||||
stx = yield this.getTX(spent.hash);
|
||||
|
||||
if (!stx)
|
||||
throw new Error('Could not find spender.');
|
||||
assert(stx);
|
||||
|
||||
yield this.removeRecursive(stx, removed);
|
||||
yield this.removeRecursive(stx);
|
||||
}
|
||||
|
||||
this.start();
|
||||
|
||||
// Remove the spender.
|
||||
info = yield this.lazyRemove(tx);
|
||||
|
||||
if (!info)
|
||||
throw new Error('Cannot remove spender.');
|
||||
assert(info);
|
||||
|
||||
yield this.commit();
|
||||
|
||||
return info;
|
||||
});
|
||||
@ -1106,8 +1031,12 @@ TXDB.prototype._remove = co(function* remove(hash) {
|
||||
if (!tx)
|
||||
return;
|
||||
|
||||
this.drop();
|
||||
|
||||
info = yield this.removeRecursive(tx);
|
||||
|
||||
this.start();
|
||||
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user