/// <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() }); } }
public static Timestamp ArrangeNormalBlockMiningTime(Round round, string pubkey, Timestamp currentBlockTime) { var miningTime = round.GetExpectedMiningTime(pubkey); return(miningTime > currentBlockTime ? miningTime : currentBlockTime); }
public static Timestamp ArrangeNormalBlockMiningTime(Round round, string pubkey, Timestamp currentBlockTime) { return(TimestampExtensions.Max(round.GetExpectedMiningTime(pubkey), currentBlockTime)); }