Beispiel #1
0
 public ChainedBlock SetTip(ChainBase otherChain)
 {
     if (otherChain == null)
     {
         throw new ArgumentNullException("otherChain");
     }
     return(SetTip(otherChain.Tip));
 }
Beispiel #2
0
 public ChainedBlock FindFork(ChainBase chain)
 {
     if (chain == null)
     {
         throw new ArgumentNullException("chain");
     }
     return(FindFork(chain.ToEnumerable(true).Select(o => o.HashBlock)));
 }
Beispiel #3
0
 public bool SameTip(ChainBase chain)
 {
     if (chain == null)
     {
         throw new ArgumentNullException("chain");
     }
     return(Tip.HashBlock == chain.Tip.HashBlock);
 }
Beispiel #4
0
        public static bool ComputeStakeModifier(ChainBase chainIndex, ChainedBlock pindex)
        {
            var pindexPrev = pindex.Previous;

            // compute stake modifier
            ulong nStakeModifier;
            bool  fGeneratedStakeModifier;

            if (!ComputeNextStakeModifier(chainIndex, pindexPrev, out nStakeModifier, out fGeneratedStakeModifier))
            {
                return(false);                //error("AddToBlockIndex() : ComputeNextStakeModifier() failed");
            }
            pindex.Header.PosParameters.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
            pindex.Header.PosParameters.StakeModifierV2 = ComputeStakeModifierV2(
                pindexPrev, pindex.Header.PosParameters.IsProofOfWork() ? pindex.HashBlock : pindex.Header.PosParameters.PrevoutStake.Hash);

            return(true);
        }
Beispiel #5
0
        const uint ModifierInterval     = 10 * 60; // time to elapse before new modifier is computed

        public static bool CheckAndComputeStake(IBlockRepository blockStore, ITransactionRepository trasnactionStore, IBlockTransactionMapStore mapStore,
                                                ChainBase chainIndex, ChainedBlock pindex, Block block)
        {
            if (block.GetHash() != pindex.HashBlock)
            {
                throw new ArgumentException();
            }

            var pindexPrev = pindex.Previous;

            uint256 hashProof = null;

            // Verify hash target and signature of coinstake tx
            if (pindex.Header.PosParameters.IsProofOfStake())
            {
                uint256 targetProofOfStake;
                if (!CheckProofOfStake(blockStore, trasnactionStore, mapStore, pindexPrev, block.Transactions[1],
                                       pindex.Header.Bits.ToCompact(), out hashProof, out targetProofOfStake))
                {
                    return(false);                    // error("AcceptBlock() : check proof-of-stake failed for block %s", hash.ToString());
                }
            }

            // PoW is checked in CheckBlock()
            if (pindex.Header.PosParameters.IsProofOfWork())
            {
                hashProof = pindex.Header.GetPoWHash();
            }

            // todo: is this the same as chain work?
            // compute chain trust score
            //pindexNew.nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();

            // compute stake entropy bit for stake modifier
            if (!pindex.Header.PosParameters.SetStakeEntropyBit(pindex.Header.PosParameters.GetStakeEntropyBit()))
            {
                return(false);                //error("AddToBlockIndex() : SetStakeEntropyBit() failed");
            }
            // Record proof hash value
            pindex.Header.PosParameters.HashProof = hashProof;

            // compute stake modifier
            return(ComputeStakeModifier(chainIndex, pindex));
        }
Beispiel #6
0
        // 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.
        private static bool ComputeNextStakeModifier(ChainBase chainIndex, ChainedBlock pindexPrev, out ulong nStakeModifier,
                                                     out bool fGeneratedStakeModifier)
        {
            nStakeModifier          = 0;
            fGeneratedStakeModifier = false;
            if (pindexPrev == null)
            {
                fGeneratedStakeModifier = true;
                return(true);                // 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 (!GetLastStakeModifier(pindexPrev, out nStakeModifier, out nModifierTime))
            {
                return(false);                //error("ComputeNextStakeModifier: unable to get last modifier");
            }
            //LogPrint("stakemodifier", "ComputeNextStakeModifier: prev modifier=0x%016x time=%s\n", nStakeModifier, DateTimeStrFormat(nModifierTime));
            if (nModifierTime / ModifierInterval >= pindexPrev.Header.Time / ModifierInterval)
            {
                return(true);
            }

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

            while (pindex != null && pindex.Header.Time >= nSelectionIntervalStart)
            {
                sortedByTimestamp.Add(pindex.Header.Time, pindex.HashBlock);
                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>();

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

                // select a block from the candidates of current round
                if (!SelectBlockFromCandidates(chainIndex, sortedByTimestamp, mapSelectedBlocks, nSelectionIntervalStop, nStakeModifier, out pindex))
                {
                    return(false);                    // error("ComputeNextStakeModifier: unable to select block at round %d", nRound);
                }
                // write the entropy bit of the selected block
                nStakeModifierNew |= ((ulong)pindex.Header.PosParameters.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()));

            nStakeModifier          = nStakeModifierNew;
            fGeneratedStakeModifier = true;
            return(true);
        }
Beispiel #7
0
        // select a block from the candidate blocks in vSortedByTimestamp, excluding
        // already selected blocks in vSelectedBlocks, and with timestamp up to
        // nSelectionIntervalStop.
        private static bool SelectBlockFromCandidates(ChainBase chainIndex, SortedDictionary <uint, uint256> sortedByTimestamp,
                                                      Dictionary <uint256, ChainedBlock> mapSelectedBlocks,
                                                      long nSelectionIntervalStop, ulong nStakeModifierPrev, out ChainedBlock pindexSelected)
        {
            bool    fSelected = false;
            uint256 hashBest  = 0;

            pindexSelected = null;

            foreach (var item in sortedByTimestamp)
            {
                var pindex = chainIndex.GetBlock(item.Value);
                if (pindex == null)
                {
                    return(false);                    // error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString());
                }
                if (fSelected && pindex.Header.Time > nSelectionIntervalStop)
                {
                    break;
                }

                if (mapSelectedBlocks.Keys.Any(key => key == pindex.HashBlock))
                {
                    continue;
                }

                // compute the selection hash by hashing its proof-hash and the
                // previous proof-of-stake modifier
                uint256 hashSelection;
                using (var ms = new MemoryStream())
                {
                    var serializer = new BitcoinStream(ms, true);
                    serializer.ReadWrite(pindex.Header.PosParameters.HashProof);
                    serializer.ReadWrite(nStakeModifierPrev);

                    hashSelection = Hashes.Hash256(ms.ToArray());
                }

                // the selection hash is divided by 2**32 so that proof-of-stake block
                // is always favored over proof-of-work block. this is to preserve
                // the energy efficiency property
                if (pindex.Header.PosParameters.IsProofOfStake())
                {
                    hashSelection >>= 32;
                }

                if (fSelected && hashSelection < hashBest)
                {
                    hashBest       = hashSelection;
                    pindexSelected = pindex;
                }
                else if (!fSelected)
                {
                    fSelected      = true;
                    hashBest       = hashSelection;
                    pindexSelected = pindex;
                }
            }

            //LogPrint("stakemodifier", "SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString());
            return(fSelected);
        }