public void VerifyThatColdStakeTransactionCanBeFiltered()
        {
            this.Initialize();
            this.CreateMempoolManager();

            this.coldStakingManager.CreateWallet(walletPassword, walletName1, walletPassphrase, new Mnemonic(walletMnemonic1));

            Wallet.Wallet wallet1 = this.coldStakingManager.GetWalletByName(walletName1);

            // This will add a normal account to our wallet.
            Transaction trx1 = this.AddSpendableTransactionToWallet(wallet1);

            // This will add a secondary account to our wallet.
            Transaction trx2 = this.AddSpendableColdstakingTransactionToWallet(wallet1);

            // THis will add a cold staking transaction to the secondary normal account address. This simulates activation of cold staking onto any normal address.
            Transaction trx3 = this.AddSpendableColdstakingTransactionToNormalWallet(wallet1);

            var accounts = wallet1.GetAccounts(Wallet.Wallet.AllAccounts).ToArray();

            // We should have 2 accounts in our wallet.
            Assert.Equal(2, accounts.Length);

            // But not if we use default or specify to only return normal accounts.
            Assert.Single(wallet1.GetAccounts().ToArray()); // Defaults to NormalAccounts
            Assert.Single(wallet1.GetAccounts(Wallet.Wallet.NormalAccounts).ToArray());

            // Verify that we actually have an cold staking activation UTXO in the wallet of 202 coins.
            // This should normally not be returned by the GetAllTransactions, and should never be included in balance calculations.
            Assert.True(accounts[0].ExternalAddresses.ToArray()[1].Transactions.ToArray()[0].IsColdCoinStake);
            Assert.Equal(new Money(202, MoneyUnit.BTC), accounts[0].ExternalAddresses.ToArray()[1].Transactions.ToArray()[0].Amount);

            Assert.Single(wallet1.GetAllTransactions().ToArray());                                                  // Default to NormalAccounts, should filter out cold staking (trx3) from normal wallet.
            Assert.Single(wallet1.GetAllTransactions(Wallet.Wallet.NormalAccounts).ToArray());
            Assert.Single(wallet1.GetAllSpendableTransactions(5, 0, Wallet.Wallet.NormalAccounts).ToArray());       // Default to NormalAccounts
            Assert.Equal(2, wallet1.GetAllTransactions(Wallet.Wallet.AllAccounts).ToArray().Length);
            Assert.Equal(2, wallet1.GetAllSpendableTransactions(5, 0, Wallet.Wallet.AllAccounts).ToArray().Length); // Specified AllAccounts, should include cold-staking transaction.

            // Verify balance on normal account
            var balance1 = accounts[0].GetBalances(true);
            var balance2 = accounts[0].GetBalances(false);

            Assert.Equal(new Money(101, MoneyUnit.BTC), balance1.ConfirmedAmount);
            Assert.Equal(new Money(303, MoneyUnit.BTC), balance2.ConfirmedAmount);

            // Verify balance on special account.
            // Verify balance on normal account
            var balance3 = accounts[1].GetBalances(true);
            var balance4 = accounts[1].GetBalances(false);

            // The only transaction that exists in the cold staking wallet, is a normal one, and should be returned for both balance queries.
            Assert.Equal(new Money(101, MoneyUnit.BTC), balance3.ConfirmedAmount);
            Assert.Equal(new Money(101, MoneyUnit.BTC), balance4.ConfirmedAmount);
        }
        public async Task GenerateBlocksAsync_does_not_use_small_coins()
        {
            var walletSecret = new WalletSecret()
            {
                WalletName = "wallet", WalletPassword = "******"
            };
            var wallet = new Wallet.Wallet()
            {
                Network = this.network
            };
            var milliseconds550MinutesAgo = (uint)Math.Max(this.chain.Tip.Header.Time - TimeSpan.FromMinutes(550).Milliseconds, 0);

            this.AddAccountWithSpendableOutputs(wallet);
            var spendableTransactions = wallet.GetAllSpendableTransactions(CoinType.Stratis, this.chain.Tip.Height, 0).ToList();

            this.walletManager.Setup(w => w.GetSpendableTransactionsInWallet(It.IsAny <string>(), It.IsAny <int>()))
            .Returns(spendableTransactions);

            var fetchedUtxos = spendableTransactions
                               .Select(t => new UnspentOutputs(t.Transaction.Id, new Coins()
            {
                CoinBase  = false,
                CoinStake = false,
                Height    = 0,
                Outputs   = { new TxOut(t.Transaction.Amount ?? Money.Zero, t.Address.ScriptPubKey) },
                Time      = milliseconds550MinutesAgo,
                Version   = 1
            }))
                               .ToArray();
            var fetchCoinsResponse = new FetchCoinsResponse(fetchedUtxos, this.chain.Tip.HashBlock);

            fetchCoinsResponse.UnspentOutputs
            .Where(u => u.Outputs.Any(o => o.Value < this.posMinting.MinimumStakingCoinValue)).Should()
            .NotBeEmpty("otherwise we are not sure the code actually excludes them");
            fetchCoinsResponse.UnspentOutputs
            .Where(u => u.Outputs.Any(o => o.Value >= this.posMinting.MinimumStakingCoinValue)).Should()
            .NotBeEmpty("otherwise we are not sure the code actually includes them");

            this.coinView.Setup(c => c.FetchCoinsAsync(It.IsAny <uint256[]>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(fetchCoinsResponse));

            this.consensusManager.Setup(c => c.Tip).Returns(this.chain.Tip);
            this.dateTimeProvider.Setup(c => c.GetAdjustedTimeAsUnixTimestamp())
            .Returns(this.chain.Tip.Header.Time + 16);
            var ct = CancellationToken.None;
            var utxoStakeDescriptions = await this.posMinting.GetUtxoStakeDescriptionsAsync(walletSecret, ct);


            utxoStakeDescriptions.Select(d => d.TxOut.Value).Where(v => v < this.posMinting.MinimumStakingCoinValue)
            .Should().BeEmpty("small coins should not be included");
            utxoStakeDescriptions.Select(d => d.TxOut.Value).Where(v => v >= this.posMinting.MinimumStakingCoinValue)
            .Should().NotBeEmpty("big enough coins should be included");

            var expectedAmounts = spendableTransactions.Select(s => s.Transaction.Amount)
                                  .Where(a => a >= this.posMinting.MinimumStakingCoinValue).ToArray();

            utxoStakeDescriptions.Count.Should().Be(expectedAmounts.Length);

            utxoStakeDescriptions.Select(d => d.TxOut.Value).Should().Contain(expectedAmounts);
        }
        public void GenerateBlocks_does_not_use_small_coins()
        {
            var walletSecret = new WalletSecret()
            {
                WalletName = "wallet", WalletPassword = "******"
            };
            var wallet = new Wallet.Wallet(this.network);

            var milliseconds550MinutesAgo = (uint)Math.Max(this.chainIndexer.Tip.Header.Time - TimeSpan.FromMinutes(550).Milliseconds, 0);

            this.AddAccountWithSpendableOutputs(wallet);
            var spendableTransactions = wallet.GetAllSpendableTransactions(this.chainIndexer.Tip.Height, 0).ToList();

            this.walletManager.Setup(w => w.GetSpendableTransactionsInWalletForStaking(It.IsAny <string>(), It.IsAny <int>()))
            .Returns(spendableTransactions);

            var fetchedUtxos = spendableTransactions
                               .Select(t =>
                                       new UnspentOutput(
                                           new OutPoint(t.Transaction.Id, 0),
                                           new Utilities.Coins(0, new TxOut(t.Transaction.Amount ?? Money.Zero, t.Address.ScriptPubKey),
                                                               false,
                                                               false)))
                               .ToArray();
            var fetchCoinsResponse = new FetchCoinsResponse();

            foreach (var fetch in fetchedUtxos)
            {
                fetchCoinsResponse.UnspentOutputs.Add(fetch.OutPoint, fetch);
            }

            fetchCoinsResponse.UnspentOutputs
            .Where(u => u.Value.Coins.TxOut.Value < this.posMinting.MinimumStakingCoinValue).Should()
            .NotBeEmpty("otherwise we are not sure the code actually excludes them");
            fetchCoinsResponse.UnspentOutputs
            .Where(u => u.Value.Coins.TxOut.Value >= this.posMinting.MinimumStakingCoinValue).Should()
            .NotBeEmpty("otherwise we are not sure the code actually includes them");

            this.coinView.Setup(c => c.FetchCoins(It.IsAny <OutPoint[]>()))
            .Returns(fetchCoinsResponse);

            this.consensusManager.Setup(c => c.Tip).Returns(this.chainIndexer.Tip);
            this.dateTimeProvider.Setup(c => c.GetAdjustedTimeAsUnixTimestamp())
            .Returns(this.chainIndexer.Tip.Header.Time + 16);
            var ct = CancellationToken.None;
            var utxoStakeDescriptions = this.posMinting.GetUtxoStakeDescriptions(walletSecret, ct);

            utxoStakeDescriptions.Select(d => d.TxOut.Value).Where(v => v < this.posMinting.MinimumStakingCoinValue)
            .Should().BeEmpty("small coins should not be included");
            utxoStakeDescriptions.Select(d => d.TxOut.Value).Where(v => v >= this.posMinting.MinimumStakingCoinValue)
            .Should().NotBeEmpty("big enough coins should be included");

            var expectedAmounts = spendableTransactions.Select(s => s.Transaction.Amount)
                                  .Where(a => a >= this.posMinting.MinimumStakingCoinValue).ToArray();

            utxoStakeDescriptions.Count.Should().Be(expectedAmounts.Length);

            utxoStakeDescriptions.Select(d => d.TxOut.Value).Should().Contain(expectedAmounts);
        }
Beispiel #4
0
        /// <summary>
        /// Gets the spendable transactions associated with cold wallet addresses.
        /// </summary>
        /// <param name="walletName">The name of the wallet.</param>
        /// <param name="isColdWalletAccount">The cold staking account to get the transactions for.</param>
        /// <param name="confirmations">The number of confirmations.</param>
        /// <returns>An enumeration of <see cref="UnspentOutputReference"/> items.</returns>
        public IEnumerable <UnspentOutputReference> GetSpendableTransactionsInColdWallet(string walletName, bool isColdWalletAccount, int confirmations = 0)
        {
            Guard.NotEmpty(walletName, nameof(walletName));

            Wallet.Wallet            wallet = this.GetWalletByName(walletName);
            UnspentOutputReference[] res    = null;
            lock (this.lockObject)
            {
                res = wallet.GetAllSpendableTransactions(this.ChainIndexer.Tip.Height, confirmations,
                                                         a => a.Index == (isColdWalletAccount ? ColdWalletAccountIndex : HotWalletAccountIndex)).ToArray();
            }

            this.logger.LogTrace("(-):*.Count={0}", res.Count());
            return(res);
        }