Example #1
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);
        }
Example #2
0
        private void CheckState(ContextInformation context, ChainedBlock pindexPrev)
        {
            var block = context.BlockResult.Block;

            if (!BlockStake.IsProofOfStake(block))
            {
                return;
            }

            // verify hash target and signature of coinstake tx
            var prevBlockStake = this.stakeChain.Get(pindexPrev.HashBlock);

            if (prevBlockStake == null)
            {
                ConsensusErrors.PrevStakeNull.Throw();
            }

            context.SetStake();
            this.posConsensusValidator.StakeValidator.CheckProofOfStake(context, pindexPrev, prevBlockStake, block.Transactions[1], block.Header.Bits.ToCompact());

            // Found a solution
            if (block.Header.HashPrevBlock != this.chain.Tip.HashBlock)
            {
                return;
            }

            // validate the block
            this.consensusLoop.AcceptBlock(context);

            if (context.BlockResult.ChainedBlock == null)
            {
                return;                                                       //reorg
            }
            if (context.BlockResult.Error != null)
            {
                return;
            }

            if (context.BlockResult.ChainedBlock.ChainWork <= this.chain.Tip.ChainWork)
            {
                return;
            }

            // similar logic to what's in the full node code
            this.chain.SetTip(context.BlockResult.ChainedBlock);
            this.consensusLoop.Puller.SetLocation(this.consensusLoop.Tip);
            this.chainState.HighestValidatedPoW = this.consensusLoop.Tip;
            this.blockRepository.PutAsync(context.BlockResult.ChainedBlock.HashBlock, new List <Block> {
                block
            }).GetAwaiter().GetResult();
            this.signals.Blocks.Broadcast(block);

            Logs.Mining.LogInformation($"Found new POS block {context.BlockResult.ChainedBlock.HashBlock}");

            // wait for peers to get the block
            Thread.Sleep(1000);

            // ask peers for thier headers
            foreach (var node in this.connection.ConnectedNodes)
            {
                node.Behavior <ChainBehavior>().TrySync();
            }

            // wait for all peers to accept the block
            var retry = 0;

            foreach (var node in this.connection.ConnectedNodes)
            {
                var chainBehaviour = node.Behavior <ChainBehavior>();
                while (++retry < 100 && chainBehaviour.PendingTip != this.chain.Tip)
                {
                    Thread.Sleep(1000);
                }
            }

            if (retry == 100)
            {
                // seems the block was not accepted
                throw new MinerException("Block rejected by peers");
            }
        }