private void AdjustLimitMillisecondsOfMiningBlock(Round currentRound, string publicKey,
                                                          int nextBlockMiningLeftMilliseconds, out int limitMillisecondsOfMiningBlock)
        {
            var minerInRound   = currentRound.RealTimeMinersInformation[publicKey];
            var miningInterval = currentRound.GetMiningInterval();
            var offset         = 0;

            if (nextBlockMiningLeftMilliseconds < 0)
            {
                Context.LogDebug(() => "Next block mining left milliseconds is less than 0.");
                offset = nextBlockMiningLeftMilliseconds;
            }

            limitMillisecondsOfMiningBlock = miningInterval.Div(AEDPoSContractConstants.TotalTinySlots).Add(offset);
            limitMillisecondsOfMiningBlock = limitMillisecondsOfMiningBlock < 0 ? 0 : limitMillisecondsOfMiningBlock;

            var currentRoundStartTime = currentRound.GetStartTime();
            var producedTinyBlocksForPreviousRound =
                minerInRound.ActualMiningTimes.Count(t => t < currentRoundStartTime);

            if (minerInRound.ProducedTinyBlocks == AEDPoSContractConstants.TinyBlocksNumber ||
                minerInRound.ProducedTinyBlocks ==
                AEDPoSContractConstants.TinyBlocksNumber.Add(producedTinyBlocksForPreviousRound))
            {
                limitMillisecondsOfMiningBlock = limitMillisecondsOfMiningBlock.Div(2);
            }
            else
            {
                limitMillisecondsOfMiningBlock = limitMillisecondsOfMiningBlock
                                                 .Mul(AEDPoSContractConstants.LimitBlockExecutionTimeWeight)
                                                 .Div(AEDPoSContractConstants.LimitBlockExecutionTimeTotalShares);
            }
        }
コード例 #2
0
        public override Empty FirstRound(Round input)
        {
            if (State.CurrentRoundNumber.Value != 0)
            {
                return(new Empty());
            }
            Assert(input.RoundNumber == 1, "Invalid round number.");
            Assert(input.RealTimeMinersInformation.Any(), "No miner in input data.");

            State.CurrentTermNumber.Value       = 1;
            State.CurrentRoundNumber.Value      = 1;
            State.FirstRoundNumberOfEachTerm[1] = 1L;
            SetBlockchainStartTimestamp(input.GetStartTime());
            State.MiningInterval.Value = input.GetMiningInterval();

            var minerList = new MinerList
            {
                Pubkeys = { input.RealTimeMinersInformation.Keys.Select(k => k.ToByteString()) }
            };

            State.MainChainCurrentMinerList.Value = minerList;

            SetMinerList(minerList, 1);

            Assert(TryToAddRoundInformation(input), "Failed to add round information.");

            Context.LogDebug(() =>
                             $"Initial Miners: {input.RealTimeMinersInformation.Keys.Aggregate("\n", (key1, key2) => key1 + "\n" + key2)}");

            return(new Empty());
        }
コード例 #3
0
        /// <summary>
        /// The transaction with this method will generate on every node
        /// and executed with the same result.
        /// Otherwise, the block hash of the genesis block won't be equal.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override Empty FirstRound(Round input)
        {
            /* Basic checks. */

            // Ensure the execution of the current method only happened
            // at the very beginning of the consensus process.
            if (State.CurrentRoundNumber.Value != 0)
            {
                return(new Empty());
            }

            /* Initial settings. */
            State.CurrentTermNumber.Value       = 1;
            State.CurrentRoundNumber.Value      = 1;
            State.FirstRoundNumberOfEachTerm[1] = 1;
            State.MiningInterval.Value          = input.GetMiningInterval();
            SetMinerList(input.GetMinerList(), 1);

            if (!TryToAddRoundInformation(input))
            {
                Assert(false, "Failed to add round information.");
            }

            Context.LogDebug(() =>
                             $"Initial Miners: {input.RealTimeMinersInformation.Keys.Aggregate("\n", (key1, key2) => key1 + "\n" + key2)}");

            return(new Empty());
        }
コード例 #4
0
        private bool CheckMinerTimeSlot(Round round, string publicKey)
        {
            if (IsFirstRoundOfCurrentTerm(out _))
            {
                return(true);
            }
            var minerInRound           = round.RealTimeMinersInformation[publicKey];
            var latestActualMiningTime = minerInRound.ActualMiningTimes.OrderBy(t => t).LastOrDefault();

            if (latestActualMiningTime == null)
            {
                return(true);
            }
            var expectedMiningTime    = minerInRound.ExpectedMiningTime;
            var endOfExpectedTimeSlot = expectedMiningTime.AddMilliseconds(round.GetMiningInterval());

            if (latestActualMiningTime < expectedMiningTime)
            {
                // Which means this miner is producing tiny blocks for previous extra block slot.
                Context.LogDebug(() =>
                                 $"latest actual mining time: {latestActualMiningTime}, round start time: {round.GetStartTime()}");
                return(latestActualMiningTime < round.GetStartTime());
            }

            Context.LogDebug(() =>
                             $"latest actual mining time: {latestActualMiningTime}, end of expected mining time: {endOfExpectedTimeSlot}");
            return(latestActualMiningTime < endOfExpectedTimeSlot);
        }
        /// <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();
     }
 }
コード例 #7
0
        /// <summary>
        /// The transaction with this method will generate on every node
        /// and executed with the same result.
        /// Otherwise, the block hash of the genesis block won't be equal.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override Empty FirstRound(Round input)
        {
            /* Basic checks. */
            Assert(State.CurrentRoundNumber.Value == 0, "Already initialized.");

            /* Initial settings. */
            State.CurrentTermNumber.Value       = 1;
            State.CurrentRoundNumber.Value      = 1;
            State.FirstRoundNumberOfEachTerm[1] = 1;
            State.MiningInterval.Value          = input.GetMiningInterval();
            SetMinerList(input.GetMinerList(), 1);

            AddRoundInformation(input);

            Context.LogDebug(() =>
                             $"Initial Miners: {input.RealTimeMinersInformation.Keys.Aggregate("\n", (key1, key2) => key1 + "\n" + key2)}");

            return(new Empty());
        }
        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()
                });
            }
        }