示例#1
0
        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.GetSpendableTransactionsInWalletForStaking(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.FetchCoins(It.IsAny <uint256[]>(), It.IsAny <CancellationToken>()))
            .Returns(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);
        }
        public Task Mine(WalletSecret walletSecret)
        {
            if (this.mining != null)
            {
                return(this.mining);                // already mining
            }
            this.mining = this.asyncLoopFactory.Run("PosMining.Mine", token =>
            {
                this.GenerateBlocks(walletSecret);
                return(Task.CompletedTask);
            },
                                                    this.nodeLifetime.ApplicationStopping,
                                                    repeatEvery: TimeSpan.FromMilliseconds(this.minerSleep),
                                                    startAfter: TimeSpans.TenSeconds);

            return(this.mining);
        }
        public Task Mine(WalletSecret walletSecret)
        {
            if (this.mining != null)
            {
                return(this.mining);                // already mining
            }
            this.mining = AsyncLoop.Run("PosMining.Mine", token =>
            {
                this.GenerateBlocks(walletSecret);
                return(Task.CompletedTask);
            },
                                        this.cancellationProvider.Cancellation.Token,
                                        repeatEvery: TimeSpan.FromMilliseconds(this.minerSleep),
                                        startAfter: TimeSpans.TenSeconds);

            return(this.mining);
        }
        public void GenerateBlocks(WalletSecret walletSecret)
        {
            this.LastCoinStakeSearchInterval = 0;

            BlockTemplate pblocktemplate = null;
            bool          tryToSync      = true;

            while (true)
            {
                if (this.chain.Tip != this.consensusLoop.Tip)
                {
                    return;
                }

                while (!this.connection.ConnectedNodes.Any() || this.chainState.IsInitialBlockDownload)
                {
                    this.LastCoinStakeSearchInterval = 0;
                    tryToSync = true;
                    Task.Delay(TimeSpan.FromMilliseconds(this.minerSleep), this.nodeLifetime.ApplicationStopping).GetAwaiter().GetResult();
                }

                if (tryToSync)
                {
                    tryToSync = false;
                    if (this.connection.ConnectedNodes.Count() < 3 ||
                        this.chain.Tip.Header.Time < this.dateTimeProvider.GetTime() - 10 * 60)
                    {
                        //this.cancellationProvider.Cancellation.Token.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(60000));
                        continue;
                    }
                }

                if (pblocktemplate == null)
                {
                    pblocktemplate = this.blockAssemblerFactory.Create(new AssemblerOptions()
                    {
                        IsProofOfStake = true
                    }).CreateNewBlock(new Script());
                }


                var pblock     = pblocktemplate.Block;
                var pindexPrev = this.consensusLoop.Tip;

                var stakeTxes = new List <StakeTx>();
                var spendable = this.wallet.GetSpendableTransactions(1);

                var coinset = this.coinView.FetchCoinsAsync(spendable.SelectMany(s => s.Transactions.Select(t => t.Id)).ToArray()).GetAwaiter().GetResult();

                foreach (var unspentInfo in spendable)
                {
                    foreach (var infoTransaction in unspentInfo.Transactions)
                    {
                        var set  = coinset.UnspentOutputs.FirstOrDefault(f => f?.TransactionId == infoTransaction.Id);
                        var utxo = set?._Outputs[infoTransaction.Index.Value];

                        if (utxo != null && utxo.Value > Money.Zero)
                        {
                            var stakeTx = new StakeTx();

                            stakeTx.TxOut       = utxo;
                            stakeTx.OutPoint    = new OutPoint(set.TransactionId, infoTransaction.Index.Value);
                            stakeTx.Address     = unspentInfo.Address;
                            stakeTx.OutputIndex = infoTransaction.Index.Value;
                            stakeTx.HashBlock   = this.chain.GetBlock((int)set.Height).HashBlock;
                            stakeTx.UtxoSet     = set;
                            stakeTx.Secret      = walletSecret;                        //temporary
                            stakeTxes.Add(stakeTx);
                        }
                    }
                }

                // Trying to sign a block
                if (this.SignBlock(stakeTxes, pblock, pindexPrev, pblocktemplate.TotalFee))
                {
                    var blockResult = new BlockResult {
                        Block = pblock
                    };
                    this.CheckState(new ContextInformation(blockResult, this.network.Consensus), pindexPrev);

                    pblocktemplate = null;
                }
                else
                {
                    Task.Delay(TimeSpan.FromMilliseconds(this.minerSleep), this.nodeLifetime.ApplicationStopping).GetAwaiter().GetResult();
                }
            }
        }