private void UpdateForSpends(Transaction tx)
{
// tx is on the best chain by this point.
foreach (var input in tx.Inputs)
{
var result = input.Connect(Unspent, false);
if (result == TransactionInput.ConnectionResult.NoSuchTx)
{
// Not found in the unspent map. Try again with the spent map.
result = input.Connect(Spent, false);
if (result == TransactionInput.ConnectionResult.NoSuchTx)
{
// Doesn't spend any of our outputs or is coinbase.
continue;
}
}
if (result == TransactionInput.ConnectionResult.AlreadySpent)
{
// Double spend! This must have overridden a pending tx, or the block is bad (contains transactions
// that illegally double spend: should never occur if we are connected to an honest node).
//
// Work backwards like so:
//
// A -> spent by B [pending]
// \-> spent by C [chain]
var doubleSpent = input.Outpoint.FromTx; // == A
Debug.Assert(doubleSpent != null);
var index = input.Outpoint.Index;
var output = doubleSpent.Outputs[index];
var spentBy = output.SpentBy;
Debug.Assert(spentBy != null);
var connected = spentBy.ParentTransaction;
Debug.Assert(connected != null);
if (Pending.Remove(connected.Hash))
{
_log.InfoFormat("Saw double spend from chain override pending tx {0}", connected.HashAsString);
_log.Info(" <-pending ->dead");
_dead[connected.Hash] = connected;
// Now forcibly change the connection.
input.Connect(Unspent, true);
// Inform the event listeners of the newly dead tx.
if (DeadTransaction != null)
{
lock (DeadTransaction)
{
DeadTransaction(this, new WalletDeadTransactionEventArgs(connected, tx));
}
}
}
}
else if (result == TransactionInput.ConnectionResult.Success)
{
// Otherwise we saw a transaction spend our coins, but we didn't try and spend them ourselves yet.
// The outputs are already marked as spent by the connect call above, so check if there are any more for
// us to use. Move if not.
var connected = input.Outpoint.FromTx;
MaybeMoveTxToSpent(connected, "prevtx");
}
}
}