// Get the last stake modifier and its generation time from a given block
        private bool GetLastStakeModifier(ChainedBlock pindex, StakeValidator.StakeModifierContext stakeModifier)
        {
            stakeModifier.StakeModifier = 0;
            stakeModifier.ModifierTime  = 0;

            if (pindex == null)
            {
                return(false);
            }

            var blockStake = this.stakeChain.Get(pindex.HashBlock);

            while (pindex != null && pindex.Previous != null && !blockStake.GeneratedStakeModifier())
            {
                pindex     = pindex.Previous;
                blockStake = this.stakeChain.Get(pindex.HashBlock);
            }

            if (!blockStake.GeneratedStakeModifier())
            {
                return(false);                // error("GetLastStakeModifier: no generation at genesis block");
            }
            stakeModifier.StakeModifier = blockStake.StakeModifier;
            stakeModifier.ModifierTime  = pindex.Header.Time;

            return(true);
        }
        // Stake Modifier (hash modifier of proof-of-stake):
        // The purpose of stake modifier is to prevent a txout (coin) owner from
        // computing future proof-of-stake generated by this txout at the time
        // of transaction confirmation. To meet kernel protocol, the txout
        // must hash with a future stake modifier to generate the proof.
        // Stake modifier consists of bits each of which is contributed from a
        // selected block of a given block group in the past.
        // The selection of a block is based on a hash of the block's proof-hash and
        // the previous stake modifier.
        // Stake modifier is recomputed at a fixed time interval instead of every
        // block. This is to make it difficult for an attacker to gain control of
        // additional bits in the stake modifier, even after generating a chain of
        // blocks.
        public void ComputeNextStakeModifier(ChainBase chainIndex, ChainedBlock pindexPrev, StakeValidator.StakeModifierContext stakeModifier)
        {
            stakeModifier.StakeModifier          = 0;
            stakeModifier.GeneratedStakeModifier = false;
            if (pindexPrev == null)
            {
                stakeModifier.GeneratedStakeModifier = true;
                return;                 // genesis block's modifier is 0
            }

            // First find current stake modifier and its generation block time
            // if it's not old enough, return the same stake modifier
            long nModifierTime = 0;

            if (!this.GetLastStakeModifier(pindexPrev, stakeModifier))
            {
                ConsensusErrors.ModifierNotFound.Throw();
            }

            if (nModifierTime / this.consensusOptions.StakeModifierInterval >= pindexPrev.Header.Time / this.consensusOptions.StakeModifierInterval)
            {
                return;
            }

            // Sort candidate blocks by timestamp
            var  sortedByTimestamp       = new SortedDictionary <uint, ChainedBlock>();
            long nSelectionInterval      = GetStakeModifierSelectionInterval();
            long nSelectionIntervalStart = (pindexPrev.Header.Time / this.consensusOptions.StakeModifierInterval) * this.consensusOptions.StakeModifierInterval - nSelectionInterval;
            var  pindex = pindexPrev;

            while (pindex != null && pindex.Header.Time >= nSelectionIntervalStart)
            {
                sortedByTimestamp.Add(pindex.Header.Time, pindex);
                pindex = pindex.Previous;
            }
            //int nHeightFirstCandidate = pindex?.Height + 1 ?? 0;

            // Select 64 blocks from candidate blocks to generate stake modifier
            ulong nStakeModifierNew      = 0;
            long  nSelectionIntervalStop = nSelectionIntervalStart;
            var   mapSelectedBlocks      = new Dictionary <uint256, ChainedBlock>();
            var   counter = sortedByTimestamp.Count;
            var   sorted  = sortedByTimestamp.Values.ToArray();

            for (int nRound = 0; nRound < Math.Min(64, counter); nRound++)
            {
                // add an interval section to the current selection round
                nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound);

                // select a block from the candidates of current round
                BlockStake blockStake;
                if (!this.SelectBlockFromCandidates(sorted, mapSelectedBlocks, nSelectionIntervalStop, stakeModifier.StakeModifier, out pindex, out blockStake))
                {
                    ConsensusErrors.FailedSelectBlock.Throw();
                }

                // write the entropy bit of the selected block
                nStakeModifierNew |= ((ulong)blockStake.GetStakeEntropyBit() << nRound);

                // add the selected block from candidates to selected list
                mapSelectedBlocks.Add(pindex.HashBlock, pindex);

                //LogPrint("stakemodifier", "ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n", nRound, DateTimeStrFormat(nSelectionIntervalStop), pindex->nHeight, pindex->GetStakeEntropyBit());
            }

            //  // Print selection map for visualization of the selected blocks
            //  if (LogAcceptCategory("stakemodifier"))
            //  {
            //      string strSelectionMap = "";
            //      '-' indicates proof-of-work blocks not selected
            //      strSelectionMap.insert(0, pindexPrev->nHeight - nHeightFirstCandidate + 1, '-');
            //      pindex = pindexPrev;
            //      while (pindex && pindex->nHeight >= nHeightFirstCandidate)
            //      {
            //          // '=' indicates proof-of-stake blocks not selected
            //          if (pindex->IsProofOfStake())
            //              strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "=");
            //          pindex = pindex->pprev;
            //      }

            //      BOOST_FOREACH(const PAIRTYPE(uint256, const CBlockIndex*)& item, mapSelectedBlocks)
            //      {
            //          // 'S' indicates selected proof-of-stake blocks
            //          // 'W' indicates selected proof-of-work blocks
            //          strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W");
            //      }

            //      LogPrintf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap);
            //  }

            //LogPrint("stakemodifier", "ComputeNextStakeModifier: new modifier=0x%016x time=%s\n", nStakeModifierNew, DateTimeStrFormat(pindexPrev->GetBlockTime()));

            stakeModifier.StakeModifier          = nStakeModifierNew;
            stakeModifier.GeneratedStakeModifier = true;
        }