Ejemplo n.º 1
0
        /// <summary>
        /// Get the states of all BIP 9 deployments listed in the <see cref="BIP9Deployments"/> enumeration.
        /// </summary>
        /// <param name="pindexPrev">The previous header of the block to determine the states for.</param>
        /// <returns>An array of <see cref="ThresholdState"/> objects.</returns>
        public ThresholdState[] GetStates(ChainedHeader pindexPrev)
        {
            ThresholdState[] array = new ThresholdState[this.consensus.BIP9Deployments.Length];

            for (int i = 0; i < array.Length; i++)
            {
                array[i] = this.GetState(pindexPrev, i);
            }

            return(array);
        }
Ejemplo n.º 2
0
 private void Set(uint256 hash, BIP9Deployments deployment, ThresholdState state)
 {
     if (hash == null)
     {
         return;
     }
     ThresholdState?[] threshold;
     if (!this.cache.TryGetValue(hash, out threshold))
     {
         threshold = new ThresholdState?[ArraySize];
         this.cache.Add(hash, threshold);
     }
     threshold[(int)deployment] = state;
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Computes what the block version of a newly created block should be, given a previous header and the
        /// current set of BIP9 deployments defined in the consensus.
        /// </summary>
        /// <param name="prevChainedHeader">The header of the previous block in the chain.</param>
        /// <remarks>This method is currently used during block creation only. Different nodes may not implement
        /// BIP9, or may disagree about what the current valid set of deployments are. It is therefore not strictly
        /// possible to validate a block version number in anything more than general terms.</remarks>
        public int ComputeBlockVersion(ChainedHeader prevChainedHeader)
        {
            uint version = ThresholdConditionCache.VersionbitsTopBits;
            var  thresholdConditionCache = new ThresholdConditionCache(this.Parent.Network.Consensus);

            for (int deployment = 0; deployment < thresholdConditionCache.ArraySize; deployment++)
            {
                ThresholdState state = thresholdConditionCache.GetState(prevChainedHeader, deployment);
                if ((state == ThresholdState.LockedIn) || (state == ThresholdState.Started))
                {
                    version |= thresholdConditionCache.Mask(deployment);
                }
            }

            return((int)version);
        }
        protected void ProcessThresholdStateChange(ThresholdState actualPositionSignal, IndicatorDataPoint updated)
        {
            if (!Indicator.IsReady)
            {
                _previousIndicatorValue = updated.Value;
                _previousSignal         = actualPositionSignal;
                Signal = _previousSignal;
                return;
            }

            var actualSignal = GetActualSignal(_previousSignal, actualPositionSignal);

            Signal = actualSignal;
            _previousIndicatorValue = updated.Value;
            SurvivalWindow.Add((int)Signal);
            _previousSignal = actualSignal;
        }
Ejemplo n.º 5
0
        private int ComputeBlockVersion(ChainedHeader prevChainedHeader, NBitcoin.Consensus consensus)
        {
            uint version = ThresholdConditionCache.VersionbitsTopBits;
            var  thresholdConditionCache = new ThresholdConditionCache(consensus);

            IEnumerable <BIP9Deployments> deployments = Enum.GetValues(typeof(BIP9Deployments)).OfType <BIP9Deployments>();

            foreach (BIP9Deployments deployment in deployments)
            {
                ThresholdState state = thresholdConditionCache.GetState(prevChainedHeader, deployment);
                if ((state == ThresholdState.LockedIn) || (state == ThresholdState.Started))
                {
                    version |= thresholdConditionCache.Mask(deployment);
                }
            }

            return((int)version);
        }
        // Make sure margin is up-to-date before calling this method.
        private void UpdateThreshold(IEnumerable <ThingGroupSelector> groupSelectors)
        {
            if (!groupSelectors.Any())
            {
                return;
            }

            foreach (ThingGroupSelector groupSelector in groupSelectors)
            {
                if (groupSelector.UseBottomThreshold)
                {
                    if (_bottomThresholdLookup.TryGetValue(groupSelector, out ThresholdState state))
                    {
                        // Only when margin rise from NegBottomThresholdCount to 0 would CanRestock be true,
                        // otherwise, when margin falls from 0 to NegBottomThresholdCount, it is false.
                        if (this.InventoryMargins[groupSelector] >= 0)
                        {
                            state.CanRestock = false;
                            state.NegBottomThresholdCount         = groupSelector.BottomThresoldCount - groupSelector.AllowedStackCount;
                            _bottomThresholdLookup[groupSelector] = state;
                        }
                        else
                        {
                            state.NegBottomThresholdCount = groupSelector.BottomThresoldCount - groupSelector.AllowedStackCount;
                            state.CanRestock = state.NegBottomThresholdCount >= this.InventoryMargins[groupSelector] || state.CanRestock;
                            _bottomThresholdLookup[groupSelector] = state;
                        }
                    }
                    else
                    {
                        state = new ThresholdState
                        {
                            NegBottomThresholdCount = groupSelector.BottomThresoldCount - groupSelector.AllowedStackCount,
                        };
                        state.CanRestock = this.InventoryMargins[groupSelector] <= state.NegBottomThresholdCount;
                        _bottomThresholdLookup[groupSelector] = state;
                    }
                }
                else
                {
                    _bottomThresholdLookup.Remove(groupSelector);
                }
            }
        }
        /// <summary>
        /// Initialize bottom thresholds when pawn is spawned.
        /// </summary>
        /// <param name="groupSelectors"> Selectors from loadout. </param>
        /// <remarks> This function is to make old saves compatible. </remarks>
        private void InitThreshold(IEnumerable <ThingGroupSelector> groupSelectors)
        {
            if (_bottomThresholdLookup.Any())
            {
                return;
            }

            foreach (ThingGroupSelector selector in groupSelectors)
            {
                if (selector.UseBottomThreshold)
                {
                    _bottomThresholdLookup[selector] = new ThresholdState()
                    {
                        NegBottomThresholdCount = selector.BottomThresoldCount - selector.AllowedStackCount,
                        CanRestock = true,
                    };
                }
            }
        }
        private int ComputeBlockVersion(ChainedBlock prevChainedBlock, NBitcoin.Consensus consensus)
        {
            uint nVersion = ThresholdConditionCache.VERSIONBITS_TOP_BITS;
            var  thresholdConditionCache = new ThresholdConditionCache(consensus);

            IEnumerable <BIP9Deployments> deploymensts = Enum.GetValues(typeof(BIP9Deployments))
                                                         .OfType <BIP9Deployments>();

            foreach (BIP9Deployments deployment in deploymensts)
            {
                ThresholdState state = thresholdConditionCache.GetState(prevChainedBlock, deployment);
                if ((state == ThresholdState.LockedIn) || (state == ThresholdState.Started))
                {
                    nVersion |= thresholdConditionCache.Mask(deployment);
                }
            }

            return((int)nVersion);
        }
Ejemplo n.º 9
0
        protected void ProcessThresholdStateChange(ThresholdState actualPositionSignal, IndicatorDataPoint updated)
        {
            if (!Indicator.IsReady)
            {
                _previousIndicatorValue = updated.Value;
                _previousSignal         = actualPositionSignal;
                Signal = _previousSignal;
                return;
            }

            var actualSignal = GetActualSignal(_previousSignal, actualPositionSignal);

            Signal = actualSignal;

            _previousIndicatorValue = updated.Value;
            _lastSignals            = Utils.shiftRight(_lastSignals);
            _lastSignals[0]         = (int)Signal;
            _previousSignal         = actualSignal;
        }
Ejemplo n.º 10
0
        protected override bool TickCore(Entity host, RealmTime time, ref object state)
        {
            if (state == null)
            {
                var stateThresholds = _thresholds.ToList();
                state = new ThresholdState()
                {
                    CurrentThreshold = _thresholds[0],
                    Thresholds       = stateThresholds,
                    SelectedState    = 0
                };
            }

            var tState = state as ThresholdState;

            if (tState.Thresholds == null)
            {
                return(false);
            }

            var hpp = (double)(host as Enemy).HP / host.ObjectDesc.MaxHP;

            if (hpp > tState.CurrentThreshold)
            {
                return(false);
            }

            SelectedState = tState.SelectedState;

            if (tState.Thresholds.Count <= 1)
            {
                tState.Thresholds = null;
            }
            else
            {
                tState.Thresholds.RemoveAt(0);
                tState.CurrentThreshold = tState.Thresholds[0];
                tState.SelectedState++;
            }

            return(true);
        }
Ejemplo n.º 11
0
        private SoftForksBip9 CreateSoftForksBip9(ThresholdStateModel metric, ThresholdState state)
        {
            var softForksBip9 = new SoftForksBip9()
            {
                Status    = metric.ThresholdState.ToLower(),
                Bit       = this.Network.Consensus.BIP9Deployments[metric.DeploymentIndex].Bit,
                StartTime = metric.TimeStart?.ToUnixTimestamp() ?? 0,
                Timeout   = metric.TimeTimeOut?.ToUnixTimestamp() ?? 0,
                Since     = metric.SinceHeight
            };

            if (state == ThresholdState.Started)
            {
                softForksBip9.Statistics = new SoftForksBip9Statistics();

                softForksBip9.Statistics.Period    = metric.ConfirmationPeriod;
                softForksBip9.Statistics.Threshold = metric.Threshold;
                softForksBip9.Statistics.Count     = metric.Blocks;
                softForksBip9.Statistics.Elapsed   = metric.Height - metric.PeriodStartHeight;
                softForksBip9.Statistics.Possible  = (softForksBip9.Statistics.Period - softForksBip9.Statistics.Threshold) >= (softForksBip9.Statistics.Elapsed - softForksBip9.Statistics.Count);
            }

            return(softForksBip9);
        }
Ejemplo n.º 12
0
        public ThresholdState GetState(ChainedBlock pindexPrev, BIP9Deployments deployment)
        {
            int nPeriod      = this.consensus.MinerConfirmationWindow;
            int nThreshold   = this.consensus.RuleChangeActivationThreshold;
            var nTimeStart   = this.consensus.BIP9Deployments[deployment]?.StartTime;
            var nTimeTimeout = this.consensus.BIP9Deployments[deployment]?.Timeout;

            // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
            if (pindexPrev != null)
            {
                pindexPrev = pindexPrev.GetAncestor(pindexPrev.Height - ((pindexPrev.Height + 1) % nPeriod));
            }

            // Walk backwards in steps of nPeriod to find a pindexPrev whose information is known
            List <ChainedBlock> vToCompute = new List <ChainedBlock>();

            while (!this.ContainsKey(pindexPrev?.HashBlock, deployment))
            {
                if (pindexPrev.GetMedianTimePast() < nTimeStart)
                {
                    // Optimization: don't recompute down further, as we know every earlier block will be before the start time
                    this.Set(pindexPrev?.HashBlock, deployment, ThresholdState.Defined);
                    break;
                }
                vToCompute.Add(pindexPrev);
                pindexPrev = pindexPrev.GetAncestor(pindexPrev.Height - nPeriod);
            }
            // At this point, cache[pindexPrev] is known
            this.Assert(this.ContainsKey(pindexPrev?.HashBlock, deployment));
            ThresholdState state = this.Get(pindexPrev?.HashBlock, deployment);

            // Now walk forward and compute the state of descendants of pindexPrev
            while (vToCompute.Count != 0)
            {
                ThresholdState stateNext = state;
                pindexPrev = vToCompute[vToCompute.Count - 1];
                vToCompute.RemoveAt(vToCompute.Count - 1);

                switch (state)
                {
                case ThresholdState.Defined:
                {
                    if (pindexPrev.GetMedianTimePast() >= nTimeTimeout)
                    {
                        stateNext = ThresholdState.Failed;
                    }
                    else if (pindexPrev.GetMedianTimePast() >= nTimeStart)
                    {
                        stateNext = ThresholdState.Started;
                    }
                    break;
                }

                case ThresholdState.Started:
                {
                    if (pindexPrev.GetMedianTimePast() >= nTimeTimeout)
                    {
                        stateNext = ThresholdState.Failed;
                        break;
                    }
                    // We need to count
                    ChainedBlock pindexCount = pindexPrev;
                    int          count       = 0;
                    for (int i = 0; i < nPeriod; i++)
                    {
                        if (this.Condition(pindexCount, deployment))
                        {
                            count++;
                        }
                        pindexCount = pindexCount.Previous;
                    }
                    if (count >= nThreshold)
                    {
                        stateNext = ThresholdState.LockedIn;
                    }
                    break;
                }

                case ThresholdState.LockedIn:
                {
                    // Always progresses into ACTIVE.
                    stateNext = ThresholdState.Active;
                    break;
                }

                case ThresholdState.Failed:
                case ThresholdState.Active:
                {
                    // Nothing happens, these are terminal states.
                    break;
                }
                }
                this.Set(pindexPrev?.HashBlock, deployment, state = stateNext);
            }

            return(state);
        }
Ejemplo n.º 13
0
        /// <summary>
        ///     Computes the metrics of all BIP9 deployments for a given block.
        /// </summary>
        /// <param name="indexPrev">The block at which to compute the metrics.</param>
        /// <param name="thresholdStates">The current state of each BIP9 deployment.</param>
        /// <returns>A <see cref="ThresholdStateModel" /> object containg the metrics.</returns>
        public List <ThresholdStateModel> GetThresholdStateMetrics(ChainedHeader indexPrev,
                                                                   ThresholdState[] thresholdStates)
        {
            var thresholdStateModels = new List <ThresholdStateModel>();
            var array = new ThresholdState[this.consensus.BIP9Deployments.Length];

            for (var deploymentIndex = 0; deploymentIndex < array.Length; deploymentIndex++)
            {
                if (this.consensus.BIP9Deployments[deploymentIndex] == null)
                {
                    continue;
                }

                var deploymentName = this.consensus.BIP9Deployments[deploymentIndex]?.Name;

                var timeStart   = this.consensus.BIP9Deployments[deploymentIndex]?.StartTime.Date;
                var timeTimeout = this.consensus.BIP9Deployments[deploymentIndex]?.Timeout.Date;
                var threshold   = this.consensus.BIP9Deployments[deploymentIndex].Threshold;

                var votes         = 0;
                var currentHeight = indexPrev.Height + 1;
                var period        = this.consensus.MinerConfirmationWindow;

                // First ancestor outside last confirmation window. If we haven't reached block height 2016 yet this will be the genesis block.
                var periodStart = indexPrev.Height - currentHeight % period > 0
                    ? indexPrev.Height - currentHeight % period
                    : 0;
                var periodStartsHeader = indexPrev.GetAncestor(periodStart);

                var periodEndsHeight = periodStartsHeader.Height + period;

                var hexVersions = new Dictionary <string, int>();
                var totalBlocks = 0;

                var headerTemp = indexPrev;

                while (headerTemp != periodStartsHeader)
                {
                    if (Condition(headerTemp, deploymentIndex))
                    {
                        votes++;
                    }

                    totalBlocks++;

                    var hexVersion = headerTemp.Header.Version.ToString("X8");

                    if (!hexVersions.TryGetValue(hexVersion, out var count))
                    {
                        count = 0;
                    }

                    hexVersions[hexVersion] = count + 1;

                    headerTemp = headerTemp.Previous;
                }

                // look in the cache for the hash of the first block an item was deployed

                var firstSeenHash = this.cache.FirstOrDefault(c => c.Value[deploymentIndex] == ThresholdState.Started);
                var sinceHeight   = 0;

                if (firstSeenHash.Key != null)
                {
                    sinceHeight = indexPrev.FindAncestorOrSelf(firstSeenHash.Key).Height;
                }

                thresholdStateModels.Add(new ThresholdStateModel
                {
                    DeploymentName     = deploymentName,
                    DeploymentIndex    = deploymentIndex,
                    ConfirmationPeriod = period,
                    Blocks             = totalBlocks,
                    Votes             = votes,
                    HexVersions       = hexVersions,
                    TimeStart         = timeStart,
                    TimeTimeOut       = timeTimeout,
                    Threshold         = threshold,
                    Height            = currentHeight,
                    SinceHeight       = sinceHeight,
                    PeriodStartHeight = periodStartsHeader.Height,
                    PeriodEndHeight   = periodEndsHeight,
                    StateValue        = thresholdStates[deploymentIndex],
                    ThresholdState    = thresholdStates[deploymentIndex].ToString()
                });
            }

            return(thresholdStateModels);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Computes the metrics of all BIP9 deployments for a given block.
        /// </summary>
        /// <param name="indexPrev">The block at which to compute the metrics.</param>
        /// <param name="thresholdStates">The current state of each BIP9 deployment.</param>
        /// <returns>A <see cref="ThresholdStateModel" /> object containg the metrics.</returns>
        public List <ThresholdStateModel> GetThresholdStateMetrics(ChainedHeader indexPrev, ThresholdState[] thresholdStates)
        {
            var thresholdStateModels = new List <ThresholdStateModel>();

            ThresholdState[] array = new ThresholdState[this.consensus.BIP9Deployments.Length];

            for (int deploymentIndex = 0; deploymentIndex < array.Length; deploymentIndex++)
            {
                if (this.consensus.BIP9Deployments[deploymentIndex] == null)
                {
                    continue;
                }

                DateTime?timeStart   = this.consensus.BIP9Deployments[deploymentIndex]?.StartTime.Date;
                DateTime?timeTimeout = this.consensus.BIP9Deployments[deploymentIndex]?.Timeout.Date;
                int      threshold   = this.consensus.RuleChangeActivationThreshold;

                int votes         = 0;
                int currentHeight = indexPrev.Height + 1;
                int period        = this.consensus.MinerConfirmationWindow;

                // First ancestor outside last confirmation window.
                ChainedHeader periodStartsHeader = indexPrev.GetAncestor(indexPrev.Height - (currentHeight % period));
                int           periodEndsHeight   = periodStartsHeader.Height + period;

                var hexVersions = new Dictionary <string, int>();
                int totalBlocks = 0;

                while (indexPrev != periodStartsHeader)
                {
                    if (this.Condition(indexPrev, deploymentIndex))
                    {
                        votes++;
                    }

                    totalBlocks++;

                    string hexVersion = indexPrev.Header.Version.ToString("X8");

                    if (!hexVersions.TryGetValue(hexVersion, out int count))
                    {
                        count = 0;
                    }

                    hexVersions[hexVersion] = count + 1;

                    indexPrev = indexPrev.Previous;
                }

                thresholdStateModels.Add(new ThresholdStateModel()
                {
                    DeploymentIndex    = deploymentIndex,
                    ConfirmationPeriod = period,
                    Blocks             = totalBlocks,
                    Votes             = votes,
                    HexVersions       = hexVersions,
                    TimeStart         = timeStart,
                    TimeTimeOut       = timeTimeout,
                    Threshold         = threshold,
                    Height            = currentHeight,
                    PeriodStartHeight = periodStartsHeader.Height,
                    PeriodEndHeight   = periodEndsHeight,
                    StateValue        = thresholdStates[deploymentIndex],
                    ThresholdState    = ((ThresholdState)thresholdStates[deploymentIndex]).ToString()
                });
            }

            return(thresholdStateModels);
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Determines the state of a BIP from the cache and/or the chain header history and the corresponding version bits.
        /// </summary>
        /// <param name="indexPrev">The previous header of the chain header to determine the states for.</param>
        /// <param name="deployment">The deployment to check the state of.</param>
        /// <returns>The current state of the deployment.</returns>
        public ThresholdState GetState(ChainedHeader indexPrev, int deployment)
        {
            int            period      = this.consensus.MinerConfirmationWindow;
            int            threshold   = this.consensus.RuleChangeActivationThreshold;
            DateTimeOffset?timeStart   = this.consensus.BIP9Deployments[deployment]?.StartTime;
            DateTimeOffset?timeTimeout = this.consensus.BIP9Deployments[deployment]?.Timeout;

            // Check if this deployment is always active.
            if (timeStart == Utils.UnixTimeToDateTime(BIP9DeploymentsParameters.AlwaysActive))
            {
                return(ThresholdState.Active);
            }

            // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
            if (indexPrev != null)
            {
                indexPrev = indexPrev.GetAncestor(indexPrev.Height - ((indexPrev.Height + 1) % period));
            }

            // Walk backwards in steps of nPeriod to find a pindexPrev whose information is known.
            var vToCompute = new List <ChainedHeader>();

            while (!this.ContainsKey(indexPrev?.HashBlock, deployment))
            {
                if (indexPrev.GetMedianTimePast() < timeStart)
                {
                    // Optimization: don't recompute down further, as we know every earlier block will be before the start time.
                    this.Set(indexPrev?.HashBlock, deployment, ThresholdState.Defined);
                    break;
                }

                vToCompute.Add(indexPrev);
                indexPrev = indexPrev.GetAncestor(indexPrev.Height - period);
            }

            // At this point, cache[pindexPrev] is known.
            this.Assert(this.ContainsKey(indexPrev?.HashBlock, deployment));
            ThresholdState state = this.Get(indexPrev?.HashBlock, deployment);

            // Now walk forward and compute the state of descendants of pindexPrev.
            while (vToCompute.Count != 0)
            {
                ThresholdState stateNext = state;
                indexPrev = vToCompute[vToCompute.Count - 1];
                vToCompute.RemoveAt(vToCompute.Count - 1);

                switch (state)
                {
                case ThresholdState.Defined:
                {
                    if (indexPrev.GetMedianTimePast() >= timeTimeout)
                    {
                        stateNext = ThresholdState.Failed;
                    }
                    else if (indexPrev.GetMedianTimePast() >= timeStart)
                    {
                        stateNext = ThresholdState.Started;
                    }

                    break;
                }

                case ThresholdState.Started:
                {
                    if (indexPrev.GetMedianTimePast() >= timeTimeout)
                    {
                        stateNext = ThresholdState.Failed;
                        break;
                    }

                    // Counts the "votes" in the confirmation window to determine
                    // whether the rule change activation threshold has been met.
                    ChainedHeader pindexCount = indexPrev;
                    int           count       = 0;
                    for (int i = 0; i < period; i++)
                    {
                        if (this.Condition(pindexCount, deployment))
                        {
                            count++;
                        }

                        pindexCount = pindexCount.Previous;
                    }

                    // If the threshold has been met then lock in the BIP activation.
                    if (count >= threshold)
                    {
                        stateNext = ThresholdState.LockedIn;
                    }

                    break;
                }

                case ThresholdState.LockedIn:
                {
                    // Always progresses into ACTIVE.
                    stateNext = ThresholdState.Active;
                    break;
                }

                case ThresholdState.Failed:
                case ThresholdState.Active:
                {
                    // Nothing happens, these are terminal states.
                    break;
                }
                }

                this.Set(indexPrev?.HashBlock, deployment, state = stateNext);
            }

            return(state);
        }
Ejemplo n.º 16
0
        public override sealed void UpdateExtension(TargetConstraint t)
        {
            if (!t.joint)
            {
                Debug.LogWarning("Angular limit cannot be applied. Missing joint");
                return;
            }

            if (!t.axis)
            {
                Debug.LogWarning("Angular limit cannot be applied. Missing axis");
                return;
            }

            // Prevent modification
            t.joint.angularXMotion = ConfigurableJointMotion.Locked;
            t.joint.angularYMotion = ConfigurableJointMotion.Locked;
            t.joint.angularZMotion = angularZMotion;

            // Rotation
            localRotZ = GetLocalRotZ(t.joint.transform);

            // Threshold detection
            if (detectThreshold)
            {
                if (togglingThresholdDetectionIn <= 0.0f)
                {
                    if (localRotZ > threshold && thresholdState != ThresholdState.Over)
                    {
                        togglingThresholdDetectionIn = minTimeBetweenDetections;
                        thresholdState = ThresholdState.Over;
                        onOverThreshold.Invoke();
                    }
                    else if (localRotZ < threshold && thresholdState != ThresholdState.Under)
                    {
                        togglingThresholdDetectionIn = minTimeBetweenDetections;
                        thresholdState = ThresholdState.Under;
                        onUnderThreshold.Invoke();
                    }
                }
                else
                {
                    togglingThresholdDetectionIn -= Time.deltaTime;
                }
            }

            // Stucking
            if (canStuck)
            {
                if (breakableJoint == null && stucked)
                {
                    SetStuck(false, t);
                    stuckingIn = Random.Range(minRandomDuration, maxRandomDuration);
                }

                if (!stucked && stuckingIn <= 0.0f)
                {
                    SetStuck(true, t);
                }
                else
                {
                    stuckingIn -= Time.deltaTime;
                }
            }
            else if (breakableJoint != null)
            {
                SetStuck(false, t);
            }
        }
        /// <summary>
        /// Computes the metrics of all BIP9 deployments for a given block.
        /// </summary>
        /// <param name="indexPrev">The block at which to compute the metrics.</param>
        /// <param name="thresholdStates">The current state of each BIP9 deployment.</param>
        /// <returns>A <see cref="ThresholdStateModel" /> object containg the metrics.</returns>
        public List <ThresholdStateModel> GetThresholdStateMetrics(ChainedHeader indexPrev, ThresholdState[] thresholdStates)
        {
            var thresholdStateModels = new List <ThresholdStateModel>();

            ThresholdState[] array = new ThresholdState[this.consensus.BIP9Deployments.Length];

            for (int deploymentIndex = 0; deploymentIndex < array.Length; deploymentIndex++)
            {
                if (this.consensus.BIP9Deployments[deploymentIndex] == null)
                {
                    continue;
                }

                string deploymentName = this.consensus.BIP9Deployments[deploymentIndex]?.Name;

                DateTime?timeStart   = this.consensus.BIP9Deployments[deploymentIndex]?.StartTime.Date;
                DateTime?timeTimeout = this.consensus.BIP9Deployments[deploymentIndex]?.Timeout.Date;
                int      threshold   = this.consensus.RuleChangeActivationThreshold;

                int votes         = 0;
                int currentHeight = indexPrev.Height + 1;
                int period        = this.consensus.MinerConfirmationWindow;

                // First ancestor outside last confirmation window.
                ChainedHeader periodStartsHeader = indexPrev.GetAncestor(indexPrev.Height - (currentHeight % period));
                int           periodEndsHeight   = periodStartsHeader.Height + period;

                var hexVersions = new Dictionary <string, int>();
                int totalBlocks = 0;

                ChainedHeader headerTemp = indexPrev;

                while (headerTemp != periodStartsHeader)
                {
                    if (this.Condition(headerTemp, deploymentIndex))
                    {
                        votes++;
                    }

                    totalBlocks++;

                    string hexVersion = headerTemp.Header.Version.ToString("X8");

                    if (!hexVersions.TryGetValue(hexVersion, out int count))
                    {
                        count = 0;
                    }

                    hexVersions[hexVersion] = count + 1;

                    headerTemp = headerTemp.Previous;
                }

                // look in the cache for the hash of the first block an item was deployed

                var firstSeenHash = this.cache.FirstOrDefault(c => c.Value[deploymentIndex] == ThresholdState.Started);
                int sinceHeight   = 0;

                if (firstSeenHash.Key != null)
                {
                    sinceHeight = indexPrev.FindAncestorOrSelf(firstSeenHash.Key).Height;
                }

                thresholdStateModels.Add(new ThresholdStateModel()
                {
                    DeploymentName     = deploymentName,
                    DeploymentIndex    = deploymentIndex,
                    ConfirmationPeriod = period,
                    Blocks             = totalBlocks,
                    Votes             = votes,
                    HexVersions       = hexVersions,
                    TimeStart         = timeStart,
                    TimeTimeOut       = timeTimeout,
                    Threshold         = threshold,
                    Height            = currentHeight,
                    SinceHeight       = sinceHeight,
                    PeriodStartHeight = periodStartsHeader.Height,
                    PeriodEndHeight   = periodEndsHeight,
                    StateValue        = thresholdStates[deploymentIndex],
                    ThresholdState    = ((ThresholdState)thresholdStates[deploymentIndex]).ToString()
                });
            }

            return(thresholdStateModels);
        }