/// <summary>
        /// We have 2 cases of producing tiny blocks:
        /// 1. After generating information of next round (producing extra block)
        /// 2. After publishing out value (producing normal block)
        /// </summary>
        /// <param name="currentRound"></param>
        /// <param name="publicKey"></param>
        /// <param name="nextBlockMiningLeftMilliseconds"></param>
        /// <param name="expectedMiningTime"></param>
        private void GetScheduleForTinyBlock(Round currentRound, string publicKey,
                                             out int nextBlockMiningLeftMilliseconds, out Timestamp expectedMiningTime)
        {
            var minerInRound          = currentRound.RealTimeMinersInformation[publicKey];
            var producedTinyBlocks    = minerInRound.ProducedTinyBlocks;
            var currentRoundStartTime = currentRound.GetStartTime();
            var producedTinyBlocksForPreviousRound =
                minerInRound.ActualMiningTimes.Count(t => t < currentRoundStartTime);
            var miningInterval   = currentRound.GetMiningInterval();
            var timeForEachBlock = miningInterval.Div(AEDPoSContractConstants.TotalTinySlots);

            expectedMiningTime = currentRound.GetExpectedMiningTime(publicKey);

            if (minerInRound.IsMinedBlockForCurrentRound())
            {
                // After publishing out value (producing normal block)
                expectedMiningTime = expectedMiningTime.AddMilliseconds(
                    currentRound.ExtraBlockProducerOfPreviousRound != publicKey
                        ? producedTinyBlocks.Mul(timeForEachBlock)
                    // Previous extra block producer can produce double tiny blocks at most.
                        : producedTinyBlocks.Sub(producedTinyBlocksForPreviousRound).Mul(timeForEachBlock));
            }
            else if (TryToGetPreviousRoundInformation(out _))
            {
                // After generating information of next round (producing extra block)
                expectedMiningTime = currentRound.GetStartTime().AddMilliseconds(-miningInterval)
                                     .AddMilliseconds(producedTinyBlocks.Mul(timeForEachBlock));
            }

            if (currentRound.RoundNumber == 1 ||
                currentRound.RoundNumber == 2 && !minerInRound.IsMinedBlockForCurrentRound())
            {
                nextBlockMiningLeftMilliseconds =
                    GetNextBlockMiningLeftMillisecondsForFirstRound(minerInRound, miningInterval);
            }
            else
            {
                TuneExpectedMiningTimeForTinyBlock(miningInterval,
                                                   currentRound.GetExpectedMiningTime(publicKey),
                                                   ref expectedMiningTime);

                nextBlockMiningLeftMilliseconds = (int)(expectedMiningTime - Context.CurrentBlockTime).Milliseconds();

                var toPrint = expectedMiningTime;
                Context.LogDebug(() =>
                                 $"expected mining time: {toPrint}, current block time: {Context.CurrentBlockTime}. " +
                                 $"next: {(int) (toPrint - Context.CurrentBlockTime).Milliseconds()}");
            }
        }
 private void GetScheduleForUpdateValueWithoutPreviousInValue(Round currentRound,
                                                              string publicKey, out int nextBlockMiningLeftMilliseconds, out Timestamp expectedMiningTime)
 {
     if (currentRound.RoundNumber == 1)
     {
         // To avoid initial miners fork so fast at the very beginning of current chain.
         nextBlockMiningLeftMilliseconds =
             currentRound.GetMiningOrder(publicKey).Mul(currentRound.GetMiningInterval());
         expectedMiningTime = Context.CurrentBlockTime.AddMilliseconds(nextBlockMiningLeftMilliseconds);
     }
     else
     {
         // As normal as case AElfConsensusBehaviour.UpdateValue.
         expectedMiningTime = currentRound.GetExpectedMiningTime(publicKey);
         nextBlockMiningLeftMilliseconds = (int)(expectedMiningTime - Context.CurrentBlockTime).Milliseconds();
     }
 }
        /// <summary>
        /// AElf Consensus Behaviour is changeable in this method.
        /// It's the situation this miner should skip his time slot more precisely.
        /// </summary>
        /// <param name="behaviour"></param>
        /// <param name="currentRound"></param>
        /// <param name="publicKey"></param>
        /// <returns></returns>
        private ConsensusCommand GetConsensusCommand(AElfConsensusBehaviour behaviour, Round currentRound,
                                                     string publicKey)
        {
            var miningInterval = currentRound.GetMiningInterval();

            while (true)
            {
                var isAlone = CheckLonelyMiner(publicKey);
                if (behaviour == AElfConsensusBehaviour.TinyBlock && isAlone &&
                    currentRound.RealTimeMinersInformation.Count >
                    2 // There are more than 1 miner possible to save him.
                    )
                {
                    behaviour = AElfConsensusBehaviour.Nothing;
                }

                var       currentBlockTime = Context.CurrentBlockTime;
                Timestamp expectedMiningTime;
                int       nextBlockMiningLeftMilliseconds;

                switch (behaviour)
                {
                case AElfConsensusBehaviour.UpdateValueWithoutPreviousInValue:
                    GetScheduleForUpdateValueWithoutPreviousInValue(currentRound, publicKey,
                                                                    out nextBlockMiningLeftMilliseconds, out expectedMiningTime);
                    break;

                case AElfConsensusBehaviour.UpdateValue:
                    expectedMiningTime = currentRound.GetExpectedMiningTime(publicKey);
                    nextBlockMiningLeftMilliseconds = (int)(expectedMiningTime - currentBlockTime).Milliseconds();
                    break;

                case AElfConsensusBehaviour.TinyBlock:
                    GetScheduleForTinyBlock(currentRound, publicKey,
                                            out nextBlockMiningLeftMilliseconds, out expectedMiningTime);
                    if (nextBlockMiningLeftMilliseconds < 0)
                    {
                        Context.LogDebug(() =>
                                         "Next block mining left milliseconds is less than 0 for tiny block.");
                        behaviour = AElfConsensusBehaviour.NextRound;
                        continue;
                    }

                    break;

                case AElfConsensusBehaviour.NextRound:
                    GetScheduleForNextRound(currentRound, publicKey,
                                            out nextBlockMiningLeftMilliseconds, out expectedMiningTime);
                    break;

                case AElfConsensusBehaviour.NextTerm:
                    expectedMiningTime = currentRound.ArrangeAbnormalMiningTime(publicKey, currentBlockTime);
                    if (currentRound.RealTimeMinersInformation.Single(m => m.Value.IsExtraBlockProducer).Key !=
                        publicKey)
                    {
                        expectedMiningTime.AddMilliseconds(miningInterval);
                    }

                    nextBlockMiningLeftMilliseconds = (int)(expectedMiningTime - currentBlockTime).Milliseconds();
                    break;

                case AElfConsensusBehaviour.Nothing:
                    return(GetInvalidConsensusCommand());

                default:
                    return(GetInvalidConsensusCommand());
                }

                AdjustLimitMillisecondsOfMiningBlock(currentRound, publicKey, nextBlockMiningLeftMilliseconds,
                                                     out var limitMillisecondsOfMiningBlock);

                var milliseconds = nextBlockMiningLeftMilliseconds;
                Context.LogDebug(() => $"NextBlockMiningLeftMilliseconds: {milliseconds}");

                // Produce tiny blocks as fast as one can.
                if (behaviour == AElfConsensusBehaviour.TinyBlock)
                {
                    nextBlockMiningLeftMilliseconds = AEDPoSContractConstants.MinimumIntervalOfProducingBlocks;
                }

                return(new ConsensusCommand
                {
                    ExpectedMiningTime = expectedMiningTime,
                    NextBlockMiningLeftMilliseconds =
                        nextBlockMiningLeftMilliseconds < 0 ? 0 : nextBlockMiningLeftMilliseconds,
                    LimitMillisecondsOfMiningBlock = isAlone
                        ? currentRound.GetMiningInterval()
                        : behaviour == AElfConsensusBehaviour.NextTerm
                            ? miningInterval
                            : limitMillisecondsOfMiningBlock,
                    Hint = new AElfConsensusHint {
                        Behaviour = behaviour
                    }.ToByteString()
                });
            }
        }
예제 #4
0
            public static Timestamp ArrangeNormalBlockMiningTime(Round round, string pubkey, Timestamp currentBlockTime)
            {
                var miningTime = round.GetExpectedMiningTime(pubkey);

                return(miningTime > currentBlockTime ? miningTime : currentBlockTime);
            }
예제 #5
0
 public static Timestamp ArrangeNormalBlockMiningTime(Round round, string pubkey, Timestamp currentBlockTime)
 {
     return(TimestampExtensions.Max(round.GetExpectedMiningTime(pubkey), currentBlockTime));
 }