private void GetScheduleForNextRound(Round currentRound, string publicKey,
                                             out int nextBlockMiningLeftMilliseconds, out Timestamp expectedMiningTime)
        {
            var minerInRound = currentRound.RealTimeMinersInformation[publicKey];

            if (currentRound.RoundNumber == 1)
            {
                nextBlockMiningLeftMilliseconds = minerInRound.Order.Add(currentRound.RealTimeMinersInformation.Count)
                                                  .Sub(1)
                                                  .Mul(currentRound.GetMiningInterval());
                expectedMiningTime = Context.CurrentBlockTime.AddMilliseconds(nextBlockMiningLeftMilliseconds);
            }
            else
            {
                expectedMiningTime =
                    currentRound.ArrangeAbnormalMiningTime(minerInRound.Pubkey, Context.CurrentBlockTime);
                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()
                });
            }
        }
 public static Timestamp ArrangeExtraBlockMiningTime(Round round, string pubkey, Timestamp currentBlockTime)
 {
     return(round.ArrangeAbnormalMiningTime(pubkey, currentBlockTime));
 }