public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timestamp blockchainStartTimestamp, out Round nextRound) { nextRound = new Round(); var minersMinedCurrentRound = GetMinedMiners(); var minersNotMinedCurrentRound = GetNotMinedMiners(); var minersCount = RealTimeMinersInformation.Count; var miningInterval = GetMiningInterval(); nextRound.RoundNumber = RoundNumber + 1; nextRound.TermNumber = TermNumber; nextRound.BlockchainAge = RoundNumber == 1 ? 1 : (currentBlockTimestamp - blockchainStartTimestamp).Seconds; // Set next round miners' information of miners who successfully mined during this round. foreach (var minerInRound in minersMinedCurrentRound.OrderBy(m => m.FinalOrderOfNextRound)) { var order = minerInRound.FinalOrderOfNextRound; nextRound.RealTimeMinersInformation[minerInRound.Pubkey] = new MinerInRound { Pubkey = minerInRound.Pubkey, Order = order, ExpectedMiningTime = currentBlockTimestamp.AddMilliseconds(miningInterval.Mul(order)), ProducedBlocks = minerInRound.ProducedBlocks, MissedTimeSlots = minerInRound.MissedTimeSlots }; } // Set miners' information of miners missed their time slot in current round. var occupiedOrders = minersMinedCurrentRound.Select(m => m.FinalOrderOfNextRound).ToList(); var ableOrders = Enumerable.Range(1, minersCount).Where(i => !occupiedOrders.Contains(i)).ToList(); for (var i = 0; i < minersNotMinedCurrentRound.Count; i++) { var order = ableOrders[i]; var minerInRound = minersNotMinedCurrentRound[i]; nextRound.RealTimeMinersInformation[minerInRound.Pubkey] = new MinerInRound { Pubkey = minersNotMinedCurrentRound[i].Pubkey, Order = order, ExpectedMiningTime = currentBlockTimestamp .AddMilliseconds(miningInterval.Mul(order)), ProducedBlocks = minerInRound.ProducedBlocks, MissedTimeSlots = minerInRound.MissedTimeSlots + 1 }; } // Calculate extra block producer order and set the producer. var extraBlockProducerOrder = CalculateNextExtraBlockProducerOrder(); var expectedExtraBlockProducer = nextRound.RealTimeMinersInformation.Values.FirstOrDefault(m => m.Order == extraBlockProducerOrder); if (expectedExtraBlockProducer == null) { nextRound.RealTimeMinersInformation.Values.First().IsExtraBlockProducer = true; } else { expectedExtraBlockProducer.IsExtraBlockProducer = true; } BreakContinuousMining(ref nextRound); return(true); }
private AElfConsensusHeaderInformation GetConsensusExtraDataToPublishOutValue(Round currentRound, string pubkey, AElfConsensusTriggerInformation triggerInformation) { currentRound.RealTimeMinersInformation[pubkey].ProducedTinyBlocks = currentRound .RealTimeMinersInformation[pubkey].ProducedTinyBlocks.Add(1); currentRound.RealTimeMinersInformation[pubkey].ProducedBlocks = currentRound.RealTimeMinersInformation[pubkey].ProducedBlocks.Add(1); currentRound.RealTimeMinersInformation[pubkey].ActualMiningTimes .Add(Context.CurrentBlockTime); Assert(triggerInformation.InValue != null, "In value should not be null."); var outValue = HashHelper.ComputeFrom(triggerInformation.InValue); var signature = HashHelper.ConcatAndCompute(outValue, triggerInformation.InValue); // Just initial signature value. var previousInValue = Hash.Empty; // Just initial previous in value. if (TryToGetPreviousRoundInformation(out var previousRound) && !IsFirstRoundOfCurrentTerm(out _)) { if (triggerInformation.PreviousInValue != null && triggerInformation.PreviousInValue != Hash.Empty) { Context.LogDebug( () => $"Previous in value in trigger information: {triggerInformation.PreviousInValue}"); // Self check. if (previousRound.RealTimeMinersInformation.ContainsKey(pubkey) && HashHelper.ComputeFrom(triggerInformation.PreviousInValue) != previousRound.RealTimeMinersInformation[pubkey].OutValue) { Context.LogDebug(() => "Failed to produce block at previous round?"); previousInValue = Hash.Empty; } else { previousInValue = triggerInformation.PreviousInValue; } signature = previousRound.CalculateSignature(triggerInformation.PreviousInValue); } else { var fakePreviousInValue = HashHelper.ComputeFrom(pubkey.Append(Context.CurrentHeight.ToString())); if (previousRound.RealTimeMinersInformation.ContainsKey(pubkey) && previousRound.RoundNumber != 1) { var appointedPreviousInValue = previousRound.RealTimeMinersInformation[pubkey].InValue; if (appointedPreviousInValue != null) { fakePreviousInValue = appointedPreviousInValue; } Context.LogDebug(() => $"TEST:\n{previousRound.ToString(pubkey)}\nInValue: {fakePreviousInValue}"); signature = previousRound.CalculateSignature(fakePreviousInValue); } else { // This miner appears first time in current round, like as a replacement of evil miner. signature = previousRound.CalculateSignature(fakePreviousInValue); } } } var updatedRound = currentRound.ApplyNormalConsensusData(pubkey, previousInValue, outValue, signature); Context.LogDebug( () => $"Previous in value after ApplyNormalConsensusData: " + $"{updatedRound.RealTimeMinersInformation[pubkey].PreviousInValue}"); updatedRound.RealTimeMinersInformation[pubkey].ImpliedIrreversibleBlockHeight = Context.CurrentHeight; // Update secret pieces of latest in value. UpdateLatestSecretPieces(updatedRound, pubkey, triggerInformation); // To publish Out Value. return(new AElfConsensusHeaderInformation { SenderPubkey = ByteStringHelper.FromHexString(pubkey), Round = updatedRound, Behaviour = triggerInformation.Behaviour }); }
private void ProcessNextTerm(Round nextRound) { RecordMinedMinerListOfCurrentRound(); // Count missed time slot of current round. CountMissedTimeSlots(); Assert(TryToGetTermNumber(out var termNumber), "Term number not found."); // Update current term number and current round number. Assert(TryToUpdateTermNumber(nextRound.TermNumber), "Failed to update term number."); Assert(TryToUpdateRoundNumber(nextRound.RoundNumber), "Failed to update round number."); UpdateMinersCountToElectionContract(nextRound); // Reset some fields of first two rounds of next term. foreach (var minerInRound in nextRound.RealTimeMinersInformation.Values) { minerInRound.MissedTimeSlots = 0; minerInRound.ProducedBlocks = 0; } UpdateProducedBlocksNumberOfSender(nextRound); // Update miners list. var miners = new MinerList(); miners.Pubkeys.AddRange(nextRound.RealTimeMinersInformation.Keys.Select(k => k.ToByteString())); if (!SetMinerList(miners, nextRound.TermNumber)) { Assert(false, "Failed to update miner list."); } // Update term number lookup. (Using term number to get first round number of related term.) State.FirstRoundNumberOfEachTerm[nextRound.TermNumber] = nextRound.RoundNumber; // Update rounds information of next two rounds. Assert(TryToAddRoundInformation(nextRound), "Failed to add round information."); if (!TryToGetPreviousRoundInformation(out var previousRound)) { Assert(false, "Failed to get previous round information."); } UpdateCurrentMinerInformationToElectionContract(previousRound); DonateMiningReward(previousRound); State.TreasuryContract.Release.Send(new ReleaseInput { TermNumber = termNumber }); Context.LogDebug(() => $"Released treasury profit for term {termNumber}"); State.ElectionContract.TakeSnapshot.Send(new TakeElectionSnapshotInput { MinedBlocks = previousRound.GetMinedBlocks(), TermNumber = termNumber, RoundNumber = previousRound.RoundNumber }); Context.LogDebug(() => $"Changing term number to {nextRound.TermNumber}"); }
private static void PerformSecretSharing(UpdateValueInput input, MinerInRound minerInRound, Round round, string publicKey) { minerInRound.EncryptedPieces.Add(input.EncryptedPieces); foreach (var decryptedPreviousInValue in input.DecryptedPieces) { round.RealTimeMinersInformation[decryptedPreviousInValue.Key].DecryptedPieces .Add(publicKey, decryptedPreviousInValue.Value); } foreach (var previousInValue in input.MinersPreviousInValues) { round.RealTimeMinersInformation[previousInValue.Key].PreviousInValue = previousInValue.Value; } }
public static Timestamp ArrangeExtraBlockMiningTime(Round round, string pubkey, Timestamp currentBlockTime) { return(round.ArrangeAbnormalMiningTime(pubkey, currentBlockTime)); }
public NormalBlockCommandStrategy(Round currentRound, string pubkey, Timestamp currentBlockTime, long previousRoundId) : base( currentRound, pubkey, currentBlockTime) { _previousRoundId = previousRoundId; }
internal Round GenerateFirstRoundOfNewTerm(int miningInterval, Timestamp currentBlockTime, Round currentRound) { var round = GenerateFirstRoundOfNewTerm(miningInterval, currentBlockTime, currentRound.RoundNumber, currentRound.TermNumber); round.ConfirmedIrreversibleBlockHeight = currentRound.ConfirmedIrreversibleBlockHeight; round.ConfirmedIrreversibleBlockRoundNumber = currentRound.ConfirmedIrreversibleBlockRoundNumber; return(round); }
public static Timestamp ArrangeNormalBlockMiningTime(Round round, string pubkey, Timestamp currentBlockTime) { return(TimestampExtensions.Max(round.GetExpectedMiningTime(pubkey), currentBlockTime)); }
/// <summary> /// AElf Consensus Behaviour is changeable in this method when /// this miner should skip his time slot more precisely. /// </summary> /// <param name="behaviour"></param> /// <param name="currentRound"></param> /// <param name="pubkey"></param> /// <param name="currentBlockTime"></param> /// <returns></returns> private ConsensusCommand GetConsensusCommand(AElfConsensusBehaviour behaviour, Round currentRound, string pubkey, Timestamp currentBlockTime = null) { if (SolitaryMinerDetection(currentRound, pubkey)) { return(ConsensusCommandProvider.InvalidConsensusCommand); } if (currentRound.RoundNumber == 1 && behaviour != AElfConsensusBehaviour.TinyBlock) { return(new ConsensusCommandProvider(new FirstRoundCommandStrategy(currentRound, pubkey, currentBlockTime, behaviour)).GetConsensusCommand()); } Context.LogDebug(() => $"Params to get command: {behaviour}, {pubkey}, {currentBlockTime}"); switch (behaviour) { case AElfConsensusBehaviour.UpdateValue: TryToGetPreviousRoundInformation(out var previousRound); return(new ConsensusCommandProvider(new NormalBlockCommandStrategy(currentRound, pubkey, currentBlockTime, previousRound.RoundId)).GetConsensusCommand()); case AElfConsensusBehaviour.NextRound: case AElfConsensusBehaviour.NextTerm: return(new ConsensusCommandProvider( new TerminateRoundCommandStrategy(currentRound, pubkey, currentBlockTime, behaviour == AElfConsensusBehaviour.NextTerm)) .GetConsensusCommand()); case AElfConsensusBehaviour.TinyBlock: { var consensusCommand = new ConsensusCommandProvider(new TinyBlockCommandStrategy(currentRound, pubkey, currentBlockTime, GetMaximumBlocksCount())).GetConsensusCommand(); if (consensusCommand.Hint == new AElfConsensusHint { Behaviour = AElfConsensusBehaviour.NextRound }.ToByteString()) { Context.LogDebug(() => "Re-arranged behaviour from TinyBlock to NextRound."); } return(consensusCommand); } default: return(ConsensusCommandProvider.InvalidConsensusCommand); } }
public TerminateRoundCommandStrategy(Round currentRound, string pubkey, Timestamp currentBlockTime, bool isNewTerm) : base( currentRound, pubkey, currentBlockTime) { _isNewTerm = isNewTerm; }
public SideChainConsensusBehaviourProvider(Round currentRound, string pubkey, int maximumBlocksCount, Timestamp currentBlockTime) : base(currentRound, pubkey, maximumBlocksCount, currentBlockTime) { }
private void RevealSharedInValues(Round currentRound, string publicKey) { if (!currentRound.RealTimeMinersInformation.ContainsKey(publicKey)) { return; } if (!TryToGetPreviousRoundInformation(out var previousRound)) { return; } var minersCount = currentRound.RealTimeMinersInformation.Count; var minimumCount = minersCount.Mul(2).Div(3); minimumCount = minimumCount == 0 ? 1 : minimumCount; foreach (var pair in previousRound.RealTimeMinersInformation.OrderBy(m => m.Value.Order)) { // Skip himself. if (pair.Key == publicKey) { continue; } if (!currentRound.RealTimeMinersInformation.Keys.Contains(pair.Key)) { continue; } var publicKeyOfAnotherMiner = pair.Key; var anotherMinerInPreviousRound = pair.Value; if (anotherMinerInPreviousRound.EncryptedPieces.Count < minimumCount) { continue; } if (anotherMinerInPreviousRound.DecryptedPieces.Count < minersCount) { continue; } // Reveal another miner's in value for target round: var orders = anotherMinerInPreviousRound.DecryptedPieces.Select((t, i) => previousRound.RealTimeMinersInformation.Values .First(m => m.Pubkey == anotherMinerInPreviousRound.DecryptedPieces.Keys.ToList()[i]).Order) .ToList(); var sharedParts = anotherMinerInPreviousRound.DecryptedPieces.Values.ToList() .Select(s => s.ToByteArray()).ToList(); var revealedInValue = HashHelper.ComputeFrom(SecretSharingHelper.DecodeSecret(sharedParts, orders, minimumCount)); Context.LogDebug(() => $"Revealed in value of {publicKeyOfAnotherMiner} of round {previousRound.RoundNumber}: {revealedInValue}"); currentRound.RealTimeMinersInformation[publicKeyOfAnotherMiner].PreviousInValue = revealedInValue; } }
private bool TryToGetRoundInformation(long roundNumber, out Round round) { round = State.Rounds[roundNumber]; return(!round.IsEmpty); }