/// <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 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);
            }
        }
コード例 #3
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());
        }
コード例 #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);
        }
コード例 #5
0
        /// <summary>
        /// Get consensus behaviour if miner didn't mine block for current round.
        /// </summary>
        /// <param name="currentRound"></param>
        /// <param name="publicKey"></param>
        /// <param name="isFirstRoundOfCurrentTerm"></param>
        /// <returns></returns>
        private AElfConsensusBehaviour GetBehaviourIfMinerDidNotMineBlockForCurrentRound(Round currentRound,
                                                                                         string publicKey, bool isFirstRoundOfCurrentTerm)
        {
            var minerInRound = currentRound.RealTimeMinersInformation[publicKey];

            if (currentRound.RoundNumber == 1 &&           // For first round, the expected mining time is incorrect,
                minerInRound.Order != 1 &&                 // so we'd better prevent miners' ain't first order (meanwhile isn't boot miner) from mining fork blocks
                currentRound.FirstMiner().OutValue == null // by postpone their mining time
                )
            {
                return(AElfConsensusBehaviour.NextRound);
            }

            if (currentRound.ExtraBlockProducerOfPreviousRound == publicKey &&             // If this miner is extra block producer of previous round,
                Context.CurrentBlockTime < currentRound.GetStartTime() &&                  // and currently the time is ahead of current round,
                minerInRound.ProducedTinyBlocks < AEDPoSContractConstants.TinyBlocksNumber // make this miner produce some tiny blocks.
                )
            {
                return(AElfConsensusBehaviour.TinyBlock);
            }

            return(isFirstRoundOfCurrentTerm
                ? AElfConsensusBehaviour.UpdateValueWithoutPreviousInValue
                : AElfConsensusBehaviour.Nothing);
        }
コード例 #6
0
        public override Empty NextRound(Round input)
        {
            if (TryToGetCurrentRoundInformation(out var currentRound))
            {
                var publicKey = Context.RecoverPublicKey().ToHex();
                if (!currentRound.RealTimeMinersInformation.Keys.Contains(publicKey))
                {
                    return(new Empty());
                }
                Assert(currentRound.RoundNumber < input.RoundNumber, "Incorrect round number for next round.");
            }

            if (currentRound.RoundNumber == 1)
            {
                var actualBlockchainStartTimestamp = input.GetStartTime();
                SetBlockchainStartTimestamp(actualBlockchainStartTimestamp);
                if (State.IsMainChain.Value)
                {
                    var minersCount = GetMinersCount(input);
                    if (minersCount != 0 && State.ElectionContract.Value != null)
                    {
                        State.ElectionContract.UpdateMinersCount.Send(new UpdateMinersCountInput
                        {
                            MinersCount = minersCount
                        });
                    }
                }
            }

            Assert(TryToGetCurrentRoundInformation(out _), "Failed to get current round information.");
            Assert(TryToAddRoundInformation(input), "Failed to add round information.");
            Assert(TryToUpdateRoundNumber(input.RoundNumber), "Failed to update round number.");

            ClearExpiredRandomNumberTokens();

            return(new Empty());
        }