示例#1
0
        private async void TransactionProcessor_WalletRelevantTransactionProcessedAsync(object sender, ProcessedResult e)
        {
            try
            {
                foreach (var coin in e.NewlySpentCoins.Concat(e.ReplacedCoins).Concat(e.SuccessfullyDoubleSpentCoins).Distinct())
                {
                    ChaumianClient.ExposedLinks.TryRemove(coin.GetTxoRef(), out _);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }

            try
            {
                IEnumerable <SmartCoin> newCoins = e.NewlyReceivedCoins.Concat(e.RestoredCoins).Distinct();
                if (newCoins.Any())
                {
                    if (ChaumianClient.State.Contains(e.Transaction.Transaction.Inputs.Select(x => x.PrevOut.ToTxoRef())))
                    {
                        var coinsToQueue = new HashSet <SmartCoin>();
                        foreach (var newCoin in newCoins)
                        {
                            // If it's being mixed and anonset is not sufficient, then queue it.
                            if (newCoin.Unspent && ChaumianClient.HasIngredients &&
                                newCoin.AnonymitySet < ServiceConfiguration.MixUntilAnonymitySet)
                            {
                                coinsToQueue.Add(newCoin);
                            }
                        }

                        await ChaumianClient.QueueCoinsToMixAsync(coinsToQueue).ConfigureAwait(false);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }
        }
示例#2
0
        private void ProcessTransaction(SmartTransaction tx, List <HdPubKey> keys = null)
        {
            if (tx.Height != Height.MemPool && tx.Height != Height.Unknown)
            {
                MemPool.TransactionHashes.TryRemove(tx.GetHash());
            }

            //iterate tx
            //	if already have the coin
            //		if NOT mempool
            //			update height

            //if double spend
            //	if mempool
            //		if all double spent coins are mempool and RBF
            //			remove double spent coins(if other coin spends it, remove that too and so on) // will add later if they came to our keys
            //		else
            //			return
            //	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

            //iterate tx
            //	if came to our keys
            //		add coin

            // If key list is not provided refresh the key list.
            if (keys == null)
            {
                keys = KeyManager.GetKeys().ToList();
            }

            for (var i = 0; i < tx.Transaction.Outputs.Count; i++)
            {
                // If we already had it, just update the height. Maybe got from mempool to block or reorged.
                var foundCoin = Coins.FirstOrDefault(x => x.TransactionId == tx.GetHash() && x.Index == i);
                if (foundCoin != default)
                {
                    // If tx height is mempool then don't, otherwise update the height.
                    if (tx.Height != Height.MemPool)
                    {
                        foundCoin.Height = tx.Height;
                    }
                }
            }

            // If double spend:
            List <SmartCoin> doubleSpends = Coins
                                            .Where(x => tx.Transaction.Inputs.Any(y => x.SpentOutputs.Select(z => z.ToOutPoint()).Contains(y.PrevOut)))
                                            .ToList();

            if (doubleSpends.Any())
            {
                if (tx.Height == Height.MemPool)
                {
                    // if all double spent coins are mempool and RBF
                    if (doubleSpends.All(x => x.Height == Height.MemPool && x.RBF))
                    {
                        // remove double spent coins(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)
                        {
                            RemoveCoinRecursively(doubleSpentCoin);
                        }
                    }
                    else
                    {
                        return;
                    }
                }
                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)
                    {
                        RemoveCoinRecursively(doubleSpentCoin);
                    }
                }
            }

            for (var i = 0U; i < tx.Transaction.Outputs.Count; i++)
            {
                // If transaction received to any of the wallet keys:
                var      output   = tx.Transaction.Outputs[i];
                HdPubKey foundKey = keys.SingleOrDefault(x => x.GetP2wpkhScript() == output.ScriptPubKey);
                if (foundKey != default)
                {
                    foundKey.SetKeyState(KeyState.Used, KeyManager);
                    List <SmartCoin> spentOwnCoins = Coins.Where(x => tx.Transaction.Inputs.Any(y => y.PrevOut.Hash == x.TransactionId && y.PrevOut.N == x.Index)).ToList();
                    var mixin = tx.Transaction.GetMixin(i);
                    if (spentOwnCoins.Count != 0)
                    {
                        mixin += spentOwnCoins.Min(x => x.Mixin);
                    }
                    var coin = new SmartCoin(tx.GetHash(), i, output.ScriptPubKey, output.Value, tx.Transaction.Inputs.ToTxoRefs().ToArray(), tx.Height, tx.Transaction.RBF, mixin, foundKey.Label, spenderTransactionId: null, locked: false);                     // Don't inherit locked status from key, that's different.
                    Coins.TryAdd(coin);
                    if (coin.Unspent && coin.Label == "ZeroLink Change" && ChaumianClient.OnePiece != null)
                    {
                        Task.Run(async() =>
                        {
                            try
                            {
                                await ChaumianClient.QueueCoinsToMixAsync(ChaumianClient.OnePiece, coin);
                            }
                            catch (Exception ex)
                            {
                                Logger.LogError <WalletService>(ex);
                            }
                        });
                    }

                    // Make sure there's always 21 clean keys generated and indexed.
                    if (AssertCleanKeysIndexed(21, foundKey.IsInternal()))
                    {
                        // If it generated a new key refresh the keys:
                        keys = KeyManager.GetKeys().ToList();
                    }
                }
            }

            // If spends any of our coin
            for (var i = 0; i < tx.Transaction.Inputs.Count; i++)
            {
                var input = tx.Transaction.Inputs[i];

                var foundCoin = Coins.FirstOrDefault(x => x.TransactionId == input.PrevOut.Hash && x.Index == input.PrevOut.N);
                if (foundCoin != null)
                {
                    foundCoin.SpenderTransactionId = tx.GetHash();
                    CoinSpentOrSpenderConfirmed?.Invoke(this, foundCoin);
                }
            }
        }