public uint GetNextWorkRequired(HeaderNode previousHeaderNode, BlockHeader header) { if (previousHeaderNode == null) { ThrowHelper.ThrowArgumentNullException(nameof(previousHeaderNode)); } if (!_blockHeaderRepository.TryGet(previousHeaderNode.Hash, out BlockHeader? previousHeader)) { //this should never happens, if it happens means we have consistency problem (we lost an header) ThrowHelper.ThrowArgumentNullException(nameof(previousHeaderNode)); } uint proofOfWorkLimit = _consensusParameters.PowLimit.ToCompact(); int difficultyAdjustmentInterval = (int)GetDifficultyAdjustmentInterval(); // Only change once per difficulty adjustment interval if ((previousHeaderNode.Height + 1) % difficultyAdjustmentInterval != 0) { if (_consensusParameters.PowAllowMinDifficultyBlocks) { /// Special difficulty rule for test networks: /// if the new block's timestamp is more than 2 times the PoWTargetSpacing then allow mining of a min-difficulty block. if (header.TimeStamp > (previousHeader.TimeStamp + (_consensusParameters.PowTargetSpacing * 2))) { return(proofOfWorkLimit); } else { // Return the last non-special-min-difficulty-rules-block. HeaderNode currentHeaderNode = previousHeaderNode; BlockHeader currentHeader = previousHeader; while (currentHeaderNode.Previous != null && (currentHeaderNode.Height % difficultyAdjustmentInterval) != 0 && _blockHeaderRepository.TryGet(currentHeaderNode.Hash, out currentHeader !) && currentHeader.Bits == proofOfWorkLimit ) { currentHeaderNode = currentHeaderNode.Previous; } return(currentHeader.Bits); } } return(previousHeader.Bits); } // Go back by what we want to be 14 days worth of blocks int heightReference = previousHeaderNode.Height - (difficultyAdjustmentInterval - 1); HeaderNode?headerNodeReference = previousHeaderNode.GetAncestor(heightReference); BlockHeader?headerReference = null; if (headerNodeReference == null || !_blockHeaderRepository.TryGet(headerNodeReference.Hash, out headerReference)) { ThrowHelper.ThrowNotSupportedException("Header ancestor not found, PoW required work computation requires a full chain."); } return(CalculateNextWorkRequired(previousHeader, headerReference.TimeStamp)); }
public BlockHeader GetTipHeader() { using Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.Releaser readMainLock = GlobalLocks.ReadOnMainAsync().GetAwaiter().GetResult(); if (!_blockHeaderRepository.TryGet(ChainTip.Hash, out BlockHeader? header)) { ThrowHelper.ThrowBlockHeaderRepositoryException($"Unexpected error, cannot fetch the tip at height {ChainTip.Height}."); } return(header !); }
/// <summary> /// Calculate (backward) the median block time over <see cref="MEDIAN_TIMESPAN" /> window from this entry in the chain. /// </summary> /// <param name="startingBlockHash">The starting block hash.</param> /// <param name="startingBlockHeight">Height of the starting block.</param> /// <returns> /// The median block time. /// </returns> public uint Calculate(UInt256 startingBlockHash, int startingBlockHeight) { if (startingBlockHeight < 0) { ThrowHelper.ThrowArgumentException("{startingBlockHeight} must be greater or equal than 0"); } int samplesLenght = startingBlockHeight > MEDIAN_TIMESPAN ? MEDIAN_TIMESPAN : startingBlockHeight + 1; uint[]? median = new uint[samplesLenght]; if (!_blockHeaderRepository.TryGet(startingBlockHash, out BlockHeader? currentHeader)) { ThrowHelper.ThrowNotSupportedException("Fatal exception, shouldn't happen, repository may be corrupted."); } for (int i = samplesLenght - 1; i > 0; i--) { median[i] = currentHeader.TimeStamp; if (!_blockHeaderRepository.TryGet(currentHeader.PreviousBlockHash !, out currentHeader)) { ThrowHelper.ThrowNotSupportedException("Fatal exception, shouldn't happen, repository may be corrupted."); } } median[0] = currentHeader.TimeStamp; Array.Sort(median); return(median[samplesLenght / 2]); }