public bool CreateCoinStake(ChainedBlock pindexBest, uint bits, long nSearchInterval, long fees, ref Transaction txNew, ref Key key) { var pindexPrev = pindexBest; var bnTargetPerCoinDay = new Target(bits).ToCompact(); txNew.Inputs.Clear(); txNew.Outputs.Clear(); // Mark coin stake transaction txNew.Outputs.Add(new TxOut(Money.Zero, new Script())); // Choose coins to use var nBalance = this.GetBalance().Satoshi; if (nBalance <= this.reserveBalance) { return(false); } List <WalletTx> vwtxPrev = new List <WalletTx>(); List <WalletTx> setCoins; long nValueIn = 0; // Select coins with suitable depth if (!SelectCoinsForStaking(nBalance - this.reserveBalance, txNew.Time, out setCoins, out nValueIn)) { return(false); } // check if coins are already staking // this is different from the c++ implementation // which pushes the new block to the main chain // and removes it when a longer chain is found foreach (var walletTx in setCoins.ToList()) { if (this.minerService.IsStaking(walletTx.Transactionid, walletTx.OutputIndex)) { setCoins.Remove(walletTx); } } if (setCoins.Empty()) { return(false); } long nCredit = 0; Script scriptPubKeyKernel = null; foreach (var coin in setCoins) { int maxStakeSearchInterval = 60; bool fKernelFound = false; for (uint n = 0; n < Math.Min(nSearchInterval, maxStakeSearchInterval) && !fKernelFound && pindexPrev == this.chainIndex.Tip; n++) { var prevoutStake = new OutPoint(coin.Transactionid, coin.OutputIndex); long nBlockTime = 0; if (BlockValidator.CheckKernel(this.chainIndex, this.chainIndex, this.chainIndex, pindexPrev, bits, txNew.Time - n, prevoutStake, ref nBlockTime)) { scriptPubKeyKernel = coin.TxOut.ScriptPubKey; key = null; // calculate the key type if (PayToPubkeyTemplate.Instance.CheckScriptPubKey(scriptPubKeyKernel)) { var outPubKey = scriptPubKeyKernel.GetDestinationAddress(this.Context.Network); key = this.walletStore.GetKey(outPubKey); } else if (PayToPubkeyHashTemplate.Instance.CheckScriptPubKey(scriptPubKeyKernel)) { var outPubKey = scriptPubKeyKernel.GetDestinationAddress(this.Context.Network); key = this.walletStore.GetKey(outPubKey); } else { //LogPrint("coinstake", "CreateCoinStake : no support for kernel type=%d\n", whichType); break; // only support pay to public key and pay to address } // create a pubkey script form the current script var scriptPubKeyOut = PayToPubkeyTemplate.Instance.GenerateScriptPubKey(key.PubKey); //scriptPubKeyKernel; txNew.Time -= n; txNew.AddInput(new TxIn(prevoutStake)); nCredit += coin.TxOut.Value; vwtxPrev.Add(coin); txNew.Outputs.Add(new TxOut(0, scriptPubKeyOut)); //LogPrint("coinstake", "CreateCoinStake : added kernel type=%d\n", whichType); fKernelFound = true; break; } } if (fKernelFound) { break; // if kernel is found stop searching } } if (nCredit == 0 || nCredit > nBalance - this.reserveBalance) { return(false); } foreach (var coin in setCoins) { var cointrx = coin; //var coinIndex = coin.Value; // Attempt to add more inputs // Only add coins of the same key/address as kernel if (txNew.Outputs.Count == 2 && ( cointrx.TxOut.ScriptPubKey == scriptPubKeyKernel || cointrx.TxOut.ScriptPubKey == txNew.Outputs[1].ScriptPubKey ) && cointrx.Transactionid != txNew.Inputs[0].PrevOut.Hash) { long nTimeWeight = BlockValidator.GetWeight((long)cointrx.Transaction.Time, (long)txNew.Time); // Stop adding more inputs if already too many inputs if (txNew.Inputs.Count >= 100) { break; } // Stop adding inputs if reached reserve limit if (nCredit + cointrx.TxOut.Value > nBalance - this.reserveBalance) { break; } // Do not add additional significant input if (cointrx.TxOut.Value >= GetStakeCombineThreshold()) { continue; } // Do not add input that is still too young if (BlockValidator.IsProtocolV3((int)txNew.Time)) { // properly handled by selection function } else { if (nTimeWeight < BlockValidator.StakeMinAge) { continue; } } txNew.Inputs.Add(new TxIn(new OutPoint(cointrx.Transactionid, cointrx.OutputIndex))); nCredit += cointrx.TxOut.Value; vwtxPrev.Add(coin); } } // Calculate coin age reward ulong nCoinAge; if (!BlockValidator.GetCoinAge(this.chainIndex, this.chainIndex, this.chainIndex, txNew, pindexPrev, out nCoinAge)) { return(false); //error("CreateCoinStake : failed to calculate coin age"); } long nReward = BlockValidator.GetProofOfStakeReward(pindexPrev, nCoinAge, fees); if (nReward <= 0) { return(false); } nCredit += nReward; if (nCredit >= GetStakeSplitThreshold()) { txNew.Outputs.Add(new TxOut(0, txNew.Outputs[1].ScriptPubKey)); //split stake } // Set output amount if (txNew.Outputs.Count == 3) { txNew.Outputs[1].Value = (nCredit / 2 / BlockValidator.CENT) * BlockValidator.CENT; txNew.Outputs[2].Value = nCredit - txNew.Outputs[1].Value; } else { txNew.Outputs[1].Value = nCredit; } // Sign int nIn = 0; foreach (var walletTx in vwtxPrev) { if (!SignSignature(new[] { key }, walletTx.Transaction, txNew, nIn++)) { return(false); // error("CreateCoinStake : failed to sign coinstake"); } } // Limit size int nBytes = txNew.GetSerializedSize(ProtocolVersion.PROTOCOL_VERSION, SerializationType.Network); if (nBytes >= MAX_BLOCK_SIZE_GEN / 5) { return(false); // error("CreateCoinStake : exceeded coinstake size limit"); } // Successfully generated coinstake return(true); }