예제 #1
0
        public ulong GetStakeWeight()
        {
            // Choose coins to use
            var nBalance = this.GetBalance();

            if (nBalance <= this.reserveBalance)
            {
                return(0);
            }

            List <WalletTx> vwtxPrev = new List <WalletTx>();

            List <WalletTx> setCoins;
            long            nValueIn = 0;

            if (!SelectCoinsForStaking(nBalance - this.reserveBalance, (uint)DateTime.UtcNow.ToUnixTimestamp(), out setCoins, out nValueIn))
            {
                return(0);
            }

            if (setCoins.Empty())
            {
                return(0);
            }

            ulong nWeight = 0;

            var nCurrentTime = (uint)DateTime.UtcNow.ToUnixTimestamp();

            foreach (var pcoin in setCoins)
            {
                if (BlockValidator.IsProtocolV3((int)nCurrentTime))
                {
                    if (this.GetDepthInMainChain(pcoin) >= BlockValidator.StakeMinConfirmations)
                    {
                        nWeight += pcoin.TxOut.Value;
                    }
                }
                else
                {
                    if (nCurrentTime - pcoin.Transaction.Time > BlockValidator.StakeMinAge)
                    {
                        nWeight += pcoin.TxOut.Value;
                    }
                }
            }

            return(nWeight);
        }
예제 #2
0
        private List <Output> AvailableCoinsForStaking(uint nSpendTime)
        {
            var vCoins = new List <Output>();

            foreach (var pcoin in this.walletStore.Wallet.WalletsList.Values)
            {
                int nDepth = this.GetDepthInMainChain(pcoin);
                if (nDepth < 1)
                {
                    continue;
                }

                if (BlockValidator.IsProtocolV3((int)nSpendTime))
                {
                    if (nDepth < BlockValidator.StakeMinConfirmations)
                    {
                        continue;
                    }
                }
                else
                {
                    // Filtering by tx timestamp instead of block timestamp may give false positives but never false negatives
                    if (pcoin.Transaction.Time + BlockValidator.StakeMinAge > nSpendTime)
                    {
                        continue;
                    }
                }

                if (this.GetBlocksToMaturity(pcoin) > 0)
                {
                    continue;
                }

                //for (int i = 0; i < pcoin.Transaction.Outputs.Count; i++)
                if (pcoin.SpentTransactionid == uint256.Zero && pcoin.TxOut.Value >= this.minimumInputValue)
                {
                    // check if the coin is already staking
                    //if (!this.minerService.IsStaking(pcoin.Transactionid, pcoin.OutputIndex))
                    vCoins.Add(new Output {
                        Depth = nDepth, WalletTx = pcoin
                    });
                }
            }

            return(vCoins);
        }
예제 #3
0
        private List <StakeOutput> AvailableCoinsForStaking(List <StakeTx> stakeTxes, uint nSpendTime)
        {
            var vCoins = new List <StakeOutput>();

            foreach (var pcoin in stakeTxes)
            {
                int nDepth = this.GetDepthInMainChain(pcoin);
                if (nDepth < 1)
                {
                    continue;
                }

                if (BlockValidator.IsProtocolV3((int)nSpendTime))
                {
                    if (nDepth < this.network.Consensus.Option <PosConsensusOptions>().StakeMinConfirmations)
                    {
                        continue;
                    }
                }
                else
                {
                    // Filtering by tx timestamp instead of block timestamp may give false positives but never false negatives
                    if (pcoin.UtxoSet.Time + this.network.Consensus.Option <PosConsensusOptions>().StakeMinAge > nSpendTime)
                    {
                        continue;
                    }
                }

                if (this.GetBlocksToMaturity(pcoin) > 0)
                {
                    continue;
                }

                if (pcoin.TxOut.Value >= this.minimumInputValue)
                {
                    // check if the coin is already staking
                    vCoins.Add(new StakeOutput {
                        Depth = nDepth, StakeTx = pcoin
                    });
                }
            }

            return(vCoins);
        }
예제 #4
0
        public bool CreateCoinStake(List <StakeTx> stakeTxes, ChainedBlock pindexBest, Block block, long nSearchInterval,
                                    long fees, ref Transaction txNew, ref Key key)
        {
            var pindexPrev         = pindexBest;
            var bnTargetPerCoinDay = new Target(block.Header.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(stakeTxes).Satoshi;

            if (nBalance <= this.reserveBalance)
            {
                return(false);
            }

            List <StakeTx> vwtxPrev = new List <StakeTx>();

            List <StakeTx> setCoins;
            long           nValueIn = 0;

            // Select coins with suitable depth
            if (!SelectCoinsForStaking(stakeTxes, 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.TransactionHash, walletTx.OutputIndex))
            //		setCoins.Remove(walletTx);

            if (!setCoins.Any())
            {
                return(false);
            }

            long   nCredit            = 0;
            Script scriptPubKeyKernel = null;

            // Note: I would expect to see coins sorted by weight on the original implementation
            // sort the coins from heighest weight
            setCoins = setCoins.OrderByDescending(o => o.TxOut.Value).ToList();

            foreach (var coin in setCoins)
            {
                int  maxStakeSearchInterval = 60;
                bool fKernelFound           = false;

                for (uint n = 0; n < Math.Min(nSearchInterval, maxStakeSearchInterval) && !fKernelFound && pindexPrev == this.chain.Tip; n++)
                {
                    try
                    {
                        var  prevoutStake = new OutPoint(coin.UtxoSet.TransactionId, coin.OutputIndex);
                        long nBlockTime   = 0;

                        var context = new ContextInformation(new BlockResult {
                            Block = block
                        }, network.Consensus);
                        context.SetStake();
                        this.posConsensusValidator.StakeValidator.CheckKernel(context, pindexPrev, block.Header.Bits, txNew.Time - n, prevoutStake, ref nBlockTime);

                        var timemaskceck = txNew.Time - n;

                        if ((timemaskceck & PosConsensusValidator.STAKE_TIMESTAMP_MASK) != 0)
                        {
                            continue;
                        }

                        if (context.Stake.HashProofOfStake != null)
                        {
                            scriptPubKeyKernel = coin.TxOut.ScriptPubKey;

                            key = null;
                            // calculate the key type
                            if (PayToPubkeyTemplate.Instance.CheckScriptPubKey(scriptPubKeyKernel))
                            {
                                var outPubKey = scriptPubKeyKernel.GetDestinationAddress(this.network);
                                key = coin.PrvKey;
                            }
                            else if (PayToPubkeyHashTemplate.Instance.CheckScriptPubKey(scriptPubKeyKernel))
                            {
                                var outPubKey = scriptPubKeyKernel.GetDestinationAddress(this.network);
                                key = coin.PrvKey;
                            }
                            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;
                        }
                    }
                    catch (ConsensusErrorException cex)
                    {
                        if (cex.ConsensusError != ConsensusErrors.StakeHashInvalidTarget)
                        {
                            throw;
                        }
                    }
                }

                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.UtxoSet.TransactionId != txNew.Inputs[0].PrevOut.Hash)
                {
                    long nTimeWeight = BlockValidator.GetWeight((long)cointrx.UtxoSet.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.UtxoSet.TransactionId, cointrx.OutputIndex)));

                    nCredit += cointrx.TxOut.Value;
                    vwtxPrev.Add(coin);
                }
            }

            // Calculate coin age reward
            ulong nCoinAge;

            if (!this.posConsensusValidator.StakeValidator.GetCoinAge(this.chain, this.coinView, txNew, pindexPrev, out nCoinAge))
            {
                return(false);                //error("CreateCoinStake : failed to calculate coin age");
            }
            long nReward = fees + this.posConsensusValidator.GetProofOfStakeReward(pindexPrev.Height);

            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
            foreach (var walletTx in vwtxPrev)
            {
                if (!SignSignature(walletTx, txNew))
                {
                    return(false);                    // error("CreateCoinStake : failed to sign coinstake");
                }
            }

            // Limit size
            int nBytes = txNew.GetSerializedSize(ProtocolVersion.ALT_PROTOCOL_VERSION, SerializationType.Network);

            if (nBytes >= MAX_BLOCK_SIZE_GEN / 5)
            {
                return(false);                // error("CreateCoinStake : exceeded coinstake size limit");
            }
            // Successfully generated coinstake
            return(true);
        }