示例#1
0
        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;
 }
示例#7
0
        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)
 {
 }
示例#12
0
        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;
            }
        }
示例#13
0
 private bool TryToGetRoundInformation(long roundNumber, out Round round)
 {
     round = State.Rounds[roundNumber];
     return(!round.IsEmpty);
 }