public ChainedBlock SetTip(ChainBase otherChain) { if (otherChain == null) { throw new ArgumentNullException("otherChain"); } return(SetTip(otherChain.Tip)); }
public ChainedBlock FindFork(ChainBase chain) { if (chain == null) { throw new ArgumentNullException("chain"); } return(FindFork(chain.ToEnumerable(true).Select(o => o.HashBlock))); }
public bool SameTip(ChainBase chain) { if (chain == null) { throw new ArgumentNullException("chain"); } return(Tip.HashBlock == chain.Tip.HashBlock); }
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); }
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)); }
// 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); }
// 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); }