예제 #1
0
        // Get the last stake modifier and its generation time from a given block
        private static bool GetLastStakeModifier(StakeChain stakeChain, ChainedBlock pindex, out ulong stakeModifier, out long modifierTime)
        {
            stakeModifier = 0;
            modifierTime  = 0;

            if (pindex == null)
            {
                return(false);                // error("GetLastStakeModifier: null pindex");
            }
            var blockStake = stakeChain.Get(pindex.HashBlock);

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

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

            return(true);
        }
예제 #2
0
        // find last block index up to index
        public static ChainedBlock GetLastBlockIndex(StakeChain stakeChain, ChainedBlock index, bool proofOfStake)
        {
            if (index == null)
            {
                throw new ArgumentNullException(nameof(index));
            }
            var blockStake = stakeChain.Get(index.HashBlock);

            while (index.Previous != null && (blockStake.IsProofOfStake() != proofOfStake))
            {
                index      = index.Previous;
                blockStake = stakeChain.Get(index.HashBlock);
            }

            return(index);
        }
예제 #3
0
        public static bool CheckKernel(IBlockRepository blockStore, ITransactionRepository trasnactionStore,
                                       IBlockTransactionMapStore mapStore, StakeChain stakeChain,
                                       ChainedBlock pindexPrev, uint nBits, long nTime, OutPoint prevout, ref long pBlockTime)
        {
            uint256 hashProofOfStake = null, targetProofOfStake = null;

            var txPrev = trasnactionStore.Get(prevout.Hash);

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

            // Read block header
            var blockHashPrev = mapStore.GetBlockHash(prevout.Hash);
            var block  = blockHashPrev == null ? null : blockStore.GetBlock(blockHashPrev);

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

            if (IsProtocolV3((int)nTime))
            {
                int nDepth = 0;
                if (IsConfirmedInNPrevBlocks(blockStore, txPrev, pindexPrev, StakeMinConfirmations - 1, ref nDepth))
                {
                    return(false);                    // tx.DoS(100, error("CheckProofOfStake() : tried to stake at depth %d", nDepth + 1));
                }
            }
            else
            {
                var nTimeBlockFrom = block.Header.Time;
                if (nTimeBlockFrom + StakeMinAge > nTime)
                {
                    return(false);                    // error("CheckProofOfStake() : min age violation");
                }
            }

            var prevBlockStake = stakeChain.Get(pindexPrev.HashBlock);

            if (prevBlockStake == null)
            {
                return(false);                // the stake proof of the previous block is not set
            }
            // todo: check this unclear logic
            //if (pBlockTime)
            //	pBlockTime = block.Header.Time;

            return(CheckStakeKernelHash(pindexPrev, nBits, block, txPrev, prevBlockStake, prevout, (uint)nTime, out hashProofOfStake, out targetProofOfStake, false));
        }
예제 #4
0
        public const uint ModifierInterval     = 10 * 60; // time to elapse before new modifier is computed

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

            blockStake = new BlockStake(block);

            uint256 hashProof = null;

            // Verify hash target and signature of coinstake tx
            if (BlockStake.IsProofOfStake(block))
            {
                var pindexPrev = pindex.Previous;

                var prevBlockStake = stakeChain.Get(pindexPrev.HashBlock);
                if (prevBlockStake == null)
                {
                    return(false);                    // the stake proof of the previous block is not set
                }
                uint256 targetProofOfStake;
                if (!CheckProofOfStake(blockStore, trasnactionStore, mapStore, pindexPrev, prevBlockStake, 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 (BlockStake.IsProofOfWork(block))
            {
                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 (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit()))
            {
                return(false);                //error("AddToBlockIndex() : SetStakeEntropyBit() failed");
            }
            // Record proof hash value
            blockStake.HashProof = hashProof;

            // compute stake modifier
            return(ComputeStakeModifier(chainIndex, pindex, blockStake, stakeChain));
        }
예제 #5
0
        public static bool ComputeStakeModifier(ChainBase chainIndex, ChainedBlock pindex, BlockStake blockStake, StakeChain stakeChain)
        {
            var pindexPrev     = pindex.Previous;
            var blockStakePrev = pindexPrev == null ? null : stakeChain.Get(pindexPrev.HashBlock);

            // compute stake modifier
            ulong nStakeModifier;
            bool  fGeneratedStakeModifier;

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

            return(true);
        }
예제 #6
0
        /// <summary>
        /// Check PoW and that the blocks connect correctly
        /// </summary>
        /// <param name="network">The network being used</param>
        /// <returns>True if PoW is correct</returns>
        public static bool Validate(Network network, ChainedBlock chainedBlock, StakeChain stakeChain = null)
        {
            if (network == null)
            {
                throw new ArgumentNullException("network");
            }
            if (chainedBlock.Height != 0 && chainedBlock.Previous == null)
            {
                return(false);
            }
            var heightCorrect   = chainedBlock.Height == 0 || chainedBlock.Height == chainedBlock.Previous.Height + 1;
            var genesisCorrect  = chainedBlock.Height != 0 || chainedBlock.HashBlock == network.GetGenesis().GetHash();
            var hashPrevCorrect = chainedBlock.Height == 0 || chainedBlock.Header.HashPrevBlock == chainedBlock.Previous.HashBlock;
            var hashCorrect     = chainedBlock.HashBlock == chainedBlock.Header.GetHash();

            if (stakeChain == null)
            {
                return(heightCorrect && genesisCorrect && hashPrevCorrect && hashCorrect);
            }

            var workCorrect = stakeChain.CheckPowPosAndTarget(chainedBlock, stakeChain.Get(chainedBlock.HashBlock), network);

            return(heightCorrect && genesisCorrect && hashPrevCorrect && hashCorrect && workCorrect);
        }
예제 #7
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(StakeChain stakeChain, 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(stakeChain, 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(stakeChain, pindexPrev, 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
                var blockStake = stakeChain.Get(pindex.HashBlock);
                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()));

            nStakeModifier          = nStakeModifierNew;
            fGeneratedStakeModifier = true;
            return(true);
        }
예제 #8
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(StakeChain stakeChain, ChainedBlock 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.FindAncestorOrSelf(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;
                }

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

                // 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(blockStake.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 (blockStake.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);
        }