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(); } } }