private void ProcessTransaction(SmartTransaction tx) { if (!tx.Transaction.IsCoinBase && !Coins.CreatedBy(tx.GetHash()).Any()) { // Transactions we already have and processed would be "double spends" but they shouldn't. var doubleSpends = Coins.SpentBy(tx.Transaction.Inputs); if (doubleSpends.Any()) { if (!tx.Confirmed) { // if the received transaction is spending at least one input already // spent by a previous unconfirmed transaction signaling RBF then it is not a double // spending transaction but a replacement transaction. var isReplacementTx = doubleSpends.Any(x => x.IsReplaceable && !x.Confirmed); if (!isReplacementTx) { return; } // Undo the replaced transaction by removing the coins it created (if other coin // spends it, remove that too and so on) and restoring those that it replaced. // After undoing the replaced transaction it will process the replacement transaction. var replacedTxId = doubleSpends.First().OutPoint.Hash; var(replaced, restored) = Coins.Undo(replacedTxId); } else // new confirmation always enjoys priority { // remove double spent coins recursively (if other coin spends it, remove that too and so on), will add later if they came to our keys foreach (var doubleSpentCoin in doubleSpends) { Coins.Remove(doubleSpentCoin); } var unconfirmedDoubleSpentTxId = doubleSpends.First().OutPoint.Hash; } } } foreach (var(output, i) in tx.Outputs.WithIndex()) { // If transaction received to any of the wallet keys: if (!KeyManager.TryGetKeyForScriptPubKey(output.ScriptPubKey, out var foundKey)) { continue; } foundKey !.MarkAsUsed(); var newCoin = new SmartCoin(tx, i, foundKey); // Do not inherit locked status from key, that's different. // If we did not have it. if (Coins.TryAdd(newCoin)) { // Make sure there's always 21 clean keys generated and indexed. var keyRegistry = foundKey.IsInternal ? KeyManager.InternalKeys : KeyManager.ExternalKeys; keyRegistry.EnsureEnoughKeys(); } else // If we had this coin already. { if (newCoin.Confirmed) // Update the height of this old coin we already had. { var oldCoin = Coins.GetByOutPoint(new OutPoint(tx.GetHash(), i)); oldCoin.Height = newCoin.Height; } } } var alreadySpentCoins = Coins .SpentBy(tx.Transaction.Inputs) .Where(coin => coin.SpenderTransactionId == tx.GetHash()); foreach (var coin in alreadySpentCoins) { Coins.Spend(coin, tx.GetHash()); } }
public SmartCoin(SmartTransaction stx, uint index, HDPubKey pubKey) : this(new OutPoint(stx.Transaction, index), stx.Outputs[index], stx.Inputs.Select(i => i.PrevOut).ToArray(), stx.Height, stx.IsRBF, uint256.Zero, pubKey) { }