public void DustInputsNotAddedToMultiSigWallet() { var transactionData1 = new TransactionData() { Id = 1, Amount = Money.Coins(1), Index = 1, BlockHeight = null, SpendingDetails = null }; var transactionData2 = new TransactionData() { Id = 2, Amount = Money.Coins(0.0001m), Index = 0, BlockHeight = null, SpendingDetails = null }; var transactions = new MultiSigTransactions { transactionData2, transactionData1 }; Assert.Equal(2, transactions.Count); Assert.Single(transactions.GetUnspentTransactions()); }
public void CanChangeHeightOfSpendableTransaction() { var transactionData1 = new TransactionData() { Id = 1, Amount = Money.Coins(1), Index = 1, BlockHeight = null, SpendingDetails = null }; var transactionData2 = new TransactionData() { Id = 2, Amount = Money.Coins(1), Index = 0, BlockHeight = null, SpendingDetails = null }; var transactions = new MultiSigTransactions { transactionData2, transactionData1 }; transactionData2.BlockHeight = 1; transactions.Remove(transactionData2); transactionData2.BlockHeight = null; transactions.Add(transactionData2); }
private IEnumerable <Transaction> CompletedTransactions(IEnumerable <Transaction> transactionsToCheck) { FederationWallet wallet = this.federationWalletManager.GetWallet(); MultiSigTransactions walletTxs = wallet.MultiSigAddress.Transactions; HashSet <uint256> spendingTransactions = walletTxs .Where(t => t.SpendingDetails?.BlockHeight > 0) .Select(t => t.SpendingDetails?.TransactionId) .ToHashSet(); foreach (Transaction tx in transactionsToCheck) { uint256 hash = tx.GetHash(); // If there is a spendable output for this tx and it has a block height then the tx is in completed state. if (walletTxs.TryGetTransaction(hash, 0, out TransactionData tData) && tData.BlockHeight > 0) { yield return(tx); } // If this is a confirmed spending transaction then it is in completed state. else if (spendingTransactions.Contains(hash)) { yield return(tx); } // If the tx has an input that is consumed by another confirmed transaction then it should be removed. else { bool bDone = false; foreach (TxIn txIn in tx.Inputs) { // Find the input's UTXO. if (walletTxs.TryGetTransaction(txIn.PrevOut.Hash, (int)txIn.PrevOut.N, out TransactionData tData2)) { // Check if the input's UTXO is being spent by another confirmed transaction. if (tData2.SpendingDetails?.BlockHeight > 0 && tData2.SpendingDetails?.TransactionId != hash) { bDone = true; break; } } } if (bDone) { yield return(tx); } } } }
public void TransactionDataAddedToMultiSigTransactionsExistsInExpectedLookups(bool hasBlockHeight, bool hasSpendingDetails, bool hasWithdrawalDetails, bool flipBlockHeight, bool flipSpendingDetails, bool flipWithdrawalDetails) { uint256 transactionId = 1; int transactionIndex = 2; uint256 spendingTransactionId = 2; uint256 spendingDepositId = 3; SpendingDetails spendingDetails() { if (!hasSpendingDetails) { return(null); } return(new SpendingDetails() { WithdrawalDetails = hasWithdrawalDetails ? new WithdrawalDetails() { MatchingDepositId = spendingDepositId } : null, BlockHeight = spendingBlockHeight(), TransactionId = spendingTransactionId }); } int?blockHeight() { return(hasBlockHeight ? 3 : (int?)null); } int?spendingBlockHeight() { return(hasBlockHeight ? 4 : (int?)null); } var transactionData = new TransactionData() { Id = transactionId, Amount = Money.Coins(1), Index = transactionIndex, BlockHeight = blockHeight(), SpendingDetails = spendingDetails() }; var transactions = new MultiSigTransactions(); transactions.Add(transactionData); Assert.Contains(transactionData, transactions); void Validate() { if (hasBlockHeight && hasSpendingDetails) { Assert.Single(transactions.SpentTransactionsBeforeHeight(int.MaxValue), x => x.Item1 == spendingBlockHeight()); } else { Assert.Empty(transactions.SpentTransactionsBeforeHeight(int.MaxValue)); } if (hasSpendingDetails && hasWithdrawalDetails) { Assert.Single(transactions.GetSpendingTransactionsByDepositId(spendingDepositId).First().txList); } else { Assert.Empty(transactions.GetSpendingTransactionsByDepositId(spendingDepositId).First().txList); } if (hasSpendingDetails) { Assert.Empty(transactions.GetUnspentTransactions()); } else { Assert.Single(transactions.GetUnspentTransactions(), x => x.BlockHeight == blockHeight()); } } Validate(); hasBlockHeight ^= flipBlockHeight; hasSpendingDetails ^= flipSpendingDetails; hasWithdrawalDetails ^= flipWithdrawalDetails; transactionData.BlockHeight = blockHeight(); transactionData.SpendingDetails = spendingDetails(); Validate(); transactions.Remove(transactionData); Assert.Empty(transactions); Assert.Empty(transactions.SpentTransactionsBeforeHeight(int.MaxValue)); Assert.Empty(transactions.GetUnspentTransactions()); Assert.Empty(transactions.GetSpendingTransactionsByDepositId(spendingDepositId).Single().txList); }
public void TransactionsByDepositDictCantContainNullWithdrawals() { var multiSigTransactions = new MultiSigTransactions(); // Add the transaction to the collection. var spendingDetails1 = new SpendingDetails() { WithdrawalDetails = new WithdrawalDetails() { Amount = Money.Zero, MatchingDepositId = 2 } }; var spendingDetails2 = new SpendingDetails() { WithdrawalDetails = new WithdrawalDetails() { Amount = Money.Zero, MatchingDepositId = 2 } }; TransactionData txData1Fact() => new TransactionData() { Amount = Money.Zero, Id = 1, Index = 0, SpendingDetails = spendingDetails1 }; TransactionData txData2Fact() => new TransactionData() { Amount = Money.Zero, Id = 2, Index = 0, SpendingDetails = spendingDetails2 }; TransactionData txData1 = txData1Fact(); TransactionData txData2 = txData2Fact(); multiSigTransactions.Add(txData1); multiSigTransactions.Add(txData2); // Retrieve the transaction. List <TransactionData> txDataMatch = multiSigTransactions.GetSpendingTransactionsByDepositId(2).Single().txList; Assert.Contains(txDataMatch, txDataMatch => txData1.Id == txDataMatch.Id && txData1.Index == txDataMatch.Index); Assert.Contains(txDataMatch, txDataMatch => txData2.Id == txDataMatch.Id && txData2.Index == txDataMatch.Index); multiSigTransactions.Remove(txData1Fact()); multiSigTransactions.Remove(txData2Fact()); multiSigTransactions.Add(txData1); multiSigTransactions.Add(txData2); // Replace the SpendingDetails with one containing WithdrawalDetails set to null. var spendingDetails3 = new SpendingDetails() { WithdrawalDetails = null }; var spendingDetails4 = new SpendingDetails() { WithdrawalDetails = null }; // Remove and restore one. txData2.SpendingDetails = spendingDetails4; Assert.Single(multiSigTransactions.GetSpendingTransactionsByDepositId(2).Single().txList); txData2.SpendingDetails = spendingDetails2; // Retrieve the transaction again. List <TransactionData> txDataMatch2 = multiSigTransactions.GetSpendingTransactionsByDepositId(2).Single().txList; Assert.Contains(txDataMatch2, txDataMatch => txData1.Id == txDataMatch.Id && txData1.Index == txDataMatch.Index); Assert.Contains(txDataMatch2, txDataMatch => txData2.Id == txDataMatch.Id && txData2.Index == txDataMatch.Index); // Remove and restore one. txData1.SpendingDetails = spendingDetails3; Assert.Single(multiSigTransactions.GetSpendingTransactionsByDepositId(2).Single().txList); txData1.SpendingDetails = spendingDetails1; // Retrieve the transaction again. List <TransactionData> txDataMatch3 = multiSigTransactions.GetSpendingTransactionsByDepositId(2).Single().txList; Assert.Contains(txDataMatch3, txDataMatch => txData1.Id == txDataMatch.Id && txData1.Index == txDataMatch.Index); Assert.Contains(txDataMatch3, txDataMatch => txData2.Id == txDataMatch.Id && txData2.Index == txDataMatch.Index); // Remove and restore both. txData1.SpendingDetails = spendingDetails3; txData2.SpendingDetails = spendingDetails4; Assert.Empty(multiSigTransactions.GetSpendingTransactionsByDepositId(2).Single().txList); txData1.SpendingDetails = spendingDetails1; txData2.SpendingDetails = spendingDetails2; // Retrieve the transaction again. List <TransactionData> txDataMatch4 = multiSigTransactions.GetSpendingTransactionsByDepositId(2).Single().txList; Assert.Contains(txDataMatch4, txDataMatch => txData1.Id == txDataMatch.Id && txData1.Index == txDataMatch.Index); Assert.Contains(txDataMatch4, txDataMatch => txData2.Id == txDataMatch.Id && txData2.Index == txDataMatch.Index); }