コード例 #1
0
ファイル: InValueRecoveryTest.cs プロジェクト: zhxymh/AElf
        /// <summary>
        /// Generate encrypted messages and put them to round information.
        /// </summary>
        /// <returns></returns>
        private async Task <Dictionary <string, AElfConsensusTriggerInformation> > GenerateEncryptedMessagesAsync()
        {
            var firstRound = await AEDPoSContractStub.GetCurrentRoundInformation.CallAsync(new Empty());

            var randomHashes = Enumerable.Range(0, EconomicContractsTestConstants.InitialCoreDataCenterCount)
                               .Select(_ => Hash.FromString("randomHashes")).ToList();
            var triggers = Enumerable.Range(0, EconomicContractsTestConstants.InitialCoreDataCenterCount).Select(i =>
                                                                                                                 new AElfConsensusTriggerInformation
            {
                Pubkey  = ByteString.CopyFrom(InitialCoreDataCenterKeyPairs[i].PublicKey),
                InValue = randomHashes[i]
            }).ToDictionary(t => t.Pubkey.ToHex(), t => t);

            foreach (var minerInRound in firstRound.RealTimeMinersInformation.Values.OrderBy(m => m.Order))
            {
                var currentKeyPair = InitialCoreDataCenterKeyPairs.First(p => p.PublicKey.ToHex() == minerInRound.Pubkey);

                KeyPairProvider.SetKeyPair(currentKeyPair);

                BlockTimeProvider.SetBlockTime(minerInRound.ExpectedMiningTime);

                var tester            = GetAEDPoSContractStub(currentKeyPair);
                var headerInformation = new AElfConsensusHeaderInformation();
                headerInformation.MergeFrom(
                    (await AEDPoSContractStub.GetConsensusExtraData.CallAsync(triggers[minerInRound.Pubkey]
                                                                              .ToBytesValue())).Value);

                // Update consensus information.
                var toUpdate = headerInformation.Round.ExtractInformationToUpdateConsensus(minerInRound.Pubkey);
                await tester.UpdateValue.SendAsync(toUpdate);
            }

            return(triggers);
        }
コード例 #2
0
        internal static AElfConsensusHeaderInformation ToConsensusHeaderInformation(this BytesValue bytesValue)
        {
            var headerInformation = new AElfConsensusHeaderInformation();

            headerInformation.MergeFrom(bytesValue.Value);
            return(headerInformation);
        }
コード例 #3
0
        private ValidationResult ValidationForUpdateValue(AElfConsensusHeaderInformation extraData)
        {
            // Need to check round id when updating current round information.
            if (!IsRoundIdMatched(extraData.Round))
            {
                return(new ValidationResult {
                    Message = "Round Id not match."
                });
            }

            // Only one Out Value should be filled.
            if (!NewOutValueFilled(extraData.Round.RealTimeMinersInformation.Values))
            {
                return(new ValidationResult {
                    Message = "Incorrect new Out Value."
                });
            }

            if (!ValidatePreviousInValue(extraData))
            {
                return(new ValidationResult {
                    Message = "Incorrect previous in value."
                });
            }

            return(new ValidationResult {
                Success = true
            });
        }
コード例 #4
0
        private bool ValidatePreviousInValue(AElfConsensusHeaderInformation extraData)
        {
            var publicKey = extraData.SenderPubkey.ToHex();

            if (!TryToGetPreviousRoundInformation(out var previousRound))
            {
                return(true);
            }

            if (!previousRound.RealTimeMinersInformation.ContainsKey(publicKey))
            {
                return(true);
            }

            if (extraData.Round.RealTimeMinersInformation[publicKey].PreviousInValue == null)
            {
                return(true);
            }

            var previousOutValue = previousRound.RealTimeMinersInformation[publicKey].OutValue;
            var previousInValue  = extraData.Round.RealTimeMinersInformation[publicKey].PreviousInValue;

            if (previousInValue == Hash.Empty)
            {
                return(true);
            }

            return(Hash.FromMessage(previousInValue) == previousOutValue);
        }
コード例 #5
0
        private TransactionList GenerateTransactionListByExtraData(AElfConsensusHeaderInformation consensusInformation,
                                                                   ByteString pubkey)
        {
            var round     = consensusInformation.Round;
            var behaviour = consensusInformation.Behaviour;

            switch (behaviour)
            {
            case AElfConsensusBehaviour.UpdateValue:
                Context.LogDebug(() => $"Previous in value in extra data:{round.RealTimeMinersInformation[pubkey.ToHex()].PreviousInValue}");
                return(new TransactionList
                {
                    Transactions =
                    {
                        GenerateTransaction(nameof(UpdateValue),
                                            round.ExtractInformationToUpdateConsensus(pubkey.ToHex()))
                    }
                });

            case AElfConsensusBehaviour.TinyBlock:
                var minerInRound = round.RealTimeMinersInformation[pubkey.ToHex()];
                return(new TransactionList
                {
                    Transactions =
                    {
                        GenerateTransaction(nameof(UpdateTinyBlockInformation),
                                            new TinyBlockInput
                        {
                            ActualMiningTime = minerInRound.ActualMiningTimes.Last(),
                            ProducedBlocks = minerInRound.ProducedBlocks,
                            RoundId = round.RoundIdForValidation
                        })
                    }
                });

            case AElfConsensusBehaviour.NextRound:
                return(new TransactionList
                {
                    Transactions =
                    {
                        GenerateTransaction(nameof(NextRound), round)
                    }
                });

            case AElfConsensusBehaviour.NextTerm:
                return(new TransactionList
                {
                    Transactions =
                    {
                        GenerateTransaction(nameof(NextTerm), round)
                    }
                });

            default:
                return(new TransactionList());
            }
        }
コード例 #6
0
        private ValidationResult ValidateBeforeExecution(AElfConsensusHeaderInformation extraData)
        {
            var publicKey    = extraData.SenderPubkey.ToHex();
            var updatedRound = extraData.Round;

            // Validate the sender.
            if (TryToGetCurrentRoundInformation(out var currentRound) &&
                !currentRound.RealTimeMinersInformation.ContainsKey(publicKey))
            {
                Context.LogDebug(() => "Sender is not a miner.");
                return(new ValidationResult {
                    Success = false, Message = $"Sender {publicKey} is not a miner."
                });
            }

            // Validate the time slots of round information.
            var timeSlotsCheckResult = updatedRound.CheckRoundTimeSlots();

            if (!timeSlotsCheckResult.Success)
            {
                Context.LogDebug(() => $"Round time slots incorrect: {timeSlotsCheckResult.Message}");
                return(timeSlotsCheckResult);
            }

            // Validate whether this miner abide by his time slot.
            // Maybe failing due to using too much time producing previous tiny blocks.
            if (updatedRound.RoundId == currentRound.RoundId && !CheckMinerTimeSlot(updatedRound, publicKey))
            {
                Context.LogDebug(() => "Time slot already passed before execution.");
                return(new ValidationResult {
                    Message = "Time slot already passed before execution."
                });
            }

            if (updatedRound.RealTimeMinersInformation.Values.Where(m => m.FinalOrderOfNextRound > 0).Distinct()
                .Count() != updatedRound.RealTimeMinersInformation.Values.Count(m => m.OutValue != null))
            {
                return(new ValidationResult {
                    Message = "Invalid FinalOrderOfNextRound."
                });
            }

            switch (extraData.Behaviour)
            {
            case AElfConsensusBehaviour.UpdateValueWithoutPreviousInValue:
            case AElfConsensusBehaviour.UpdateValue:
                return(ValidationForUpdateValue(extraData));

            case AElfConsensusBehaviour.NextRound:
                return(ValidationForNextRound(extraData));
            }

            return(new ValidationResult {
                Success = true
            });
        }
コード例 #7
0
        private BytesValue GetConsensusBlockExtraData(BytesValue input, bool isGeneratingTransactions = false)
        {
            var triggerInformation = new AElfConsensusTriggerInformation();

            triggerInformation.MergeFrom(input.Value);

            Assert(triggerInformation.Pubkey.Any(), "Invalid pubkey.");

            if (!TryToGetCurrentRoundInformation(out var currentRound))
            {
                Assert(false, "Failed to get current round information.");
            }

            var publicKeyBytes = triggerInformation.Pubkey;
            var pubkey         = publicKeyBytes.ToHex();

            LogIfPreviousMinerHasNotProduceEnoughTinyBlocks(currentRound, pubkey);

            var information = new AElfConsensusHeaderInformation();

            switch (triggerInformation.Behaviour)
            {
            case AElfConsensusBehaviour.UpdateValue:
                information = GetConsensusExtraDataToPublishOutValue(currentRound, pubkey,
                                                                     triggerInformation);
                if (!isGeneratingTransactions)
                {
                    information.Round = information.Round.GetUpdateValueRound(pubkey);
                }

                break;

            case AElfConsensusBehaviour.TinyBlock:
                information = GetConsensusExtraDataForTinyBlock(currentRound, pubkey,
                                                                triggerInformation);
                break;

            case AElfConsensusBehaviour.NextRound:
                information = GetConsensusExtraDataForNextRound(currentRound, pubkey,
                                                                triggerInformation);
                break;

            case AElfConsensusBehaviour.NextTerm:
                information = GetConsensusExtraDataForNextTerm(pubkey, triggerInformation);
                break;
            }

            if (!isGeneratingTransactions)
            {
                information.Round.DeleteSecretSharingInformation();
            }

            return(information.ToBytesValue());
        }
コード例 #8
0
        private ValidationResult ValidationForNextRound(AElfConsensusHeaderInformation extraData)
        {
            // None of in values should be filled.
            if (extraData.Round.RealTimeMinersInformation.Values.Any(m => m.InValue != null))
            {
                return(new ValidationResult {
                    Message = "Incorrect in values."
                });
            }

            return(new ValidationResult {
                Success = true
            });
        }
コード例 #9
0
        public override TransactionList GenerateConsensusTransactions(BytesValue input)
        {
            var triggerInformation = new AElfConsensusTriggerInformation();

            triggerInformation.MergeFrom(input.Value);
            // Some basic checks.
            Assert(triggerInformation.Pubkey.Any(),
                   "Data to request consensus information should contain pubkey.");

            var pubkey = triggerInformation.Pubkey;
            var consensusInformation = new AElfConsensusHeaderInformation();

            consensusInformation.MergeFrom(GetConsensusBlockExtraData(input, true).Value);
            var transactionList = GenerateTransactionListByExtraData(consensusInformation, pubkey);

            return(transactionList);
        }
コード例 #10
0
        public async Task DecryptMessage_Test()
        {
            var previousTriggers = await GenerateEncryptedMessagesAsync();

            await BootMinerChangeRoundAsync();

            var currentRound = await AEDPoSContractStub.GetCurrentRoundInformation.CallAsync(new Empty());

            var randomHashes = Enumerable.Range(0, EconomicContractsTestConstants.InitialCoreDataCenterCount)
                               .Select(_ => Hash.FromString("randomHashes")).ToList();
            var triggers = Enumerable.Range(0, EconomicContractsTestConstants.InitialCoreDataCenterCount).Select(i =>
                                                                                                                 new AElfConsensusTriggerInformation
            {
                Pubkey             = ByteString.CopyFrom(InitialCoreDataCenterKeyPairs[i].PublicKey),
                RandomHash         = randomHashes[i],
                PreviousRandomHash = previousTriggers[InitialCoreDataCenterKeyPairs[i].PublicKey.ToHex()].RandomHash
            }).ToDictionary(t => t.Pubkey.ToHex(), t => t);

            // Just `MinimumCount + 1` miners produce blocks.
            foreach (var minerInRound in currentRound.RealTimeMinersInformation.Values.OrderBy(m => m.Order)
                     .Take(MinimumCount + 1))
            {
                var currentKeyPair = InitialCoreDataCenterKeyPairs.First(p => p.PublicKey.ToHex() == minerInRound.Pubkey);

                KeyPairProvider.SetKeyPair(currentKeyPair);

                BlockTimeProvider.SetBlockTime(minerInRound.ExpectedMiningTime);

                var tester            = GetAEDPoSContractStub(currentKeyPair);
                var headerInformation = new AElfConsensusHeaderInformation();
                headerInformation.MergeFrom(
                    (await AEDPoSContractStub.GetInformationToUpdateConsensus.CallAsync(triggers[minerInRound.Pubkey]
                                                                                        .ToBytesValue())).Value);
                // Update consensus information.
                var toUpdate = headerInformation.Round.ExtractInformationToUpdateConsensus(minerInRound.Pubkey);
                await tester.UpdateValue.SendAsync(toUpdate);
            }

            // Won't pass because currently we postpone the revealing of in values to extra block time slot.
            // But in values all filled.
//            var secondRound = await BootMiner.GetCurrentRoundInformation.CallAsync(new Empty());
//            secondRound.RealTimeMinersInformation.Values.Count(v => v.PreviousInValue != null)
//                .ShouldBe(AEDPoSContractTestConstants.InitialMinersCount);
        }
コード例 #11
0
        private ValidationResult ValidationForNextTerm(AElfConsensusHeaderInformation extraData)
        {
            // Is next round information correct?
            var validationResult = ValidationForNextRound(extraData);

            if (!validationResult.Success)
            {
                return(validationResult);
            }
            if (TryToGetCurrentRoundInformation(out var currentRound, true) &&
                currentRound.TermNumber.Add(1) != extraData.Round.TermNumber)
            {
                return(new ValidationResult {
                    Message = "Incorrect term number for next round."
                });
            }

            return(new ValidationResult {
                Success = true
            });
        }
コード例 #12
0
        private ValidationResult ValidationForNextRound(AElfConsensusHeaderInformation extraData)
        {
            // Is next round information correct?
            if (TryToGetCurrentRoundInformation(out var currentRound, true) &&
                currentRound.RoundNumber.Add(1) != extraData.Round.RoundNumber)
            {
                return(new ValidationResult {
                    Message = "Incorrect round number for next round."
                });
            }
            if (extraData.Round.RealTimeMinersInformation.Values.Any(m => m.InValue != null))
            {
                return(new ValidationResult {
                    Message = "Incorrect next round information."
                });
            }


            return(new ValidationResult {
                Success = true
            });
        }
コード例 #13
0
        public override ValidationResult ValidateConsensusAfterExecution(BytesValue input)
        {
            var headerInformation = new AElfConsensusHeaderInformation();

            headerInformation.MergeFrom(input.Value);
            if (TryToGetCurrentRoundInformation(out var currentRound))
            {
                if (headerInformation.Behaviour == AElfConsensusBehaviour.UpdateValue)
                {
                    headerInformation.Round =
                        currentRound.RecoverFromUpdateValue(headerInformation.Round,
                                                            headerInformation.SenderPubkey.ToHex());
                }

                if (headerInformation.Behaviour == AElfConsensusBehaviour.TinyBlock)
                {
                    headerInformation.Round =
                        currentRound.RecoverFromTinyBlock(headerInformation.Round,
                                                          headerInformation.SenderPubkey.ToHex());
                }

                var isContainPreviousInValue = !currentRound.IsMinerListJustChanged;
                if (headerInformation.Round.GetHash(isContainPreviousInValue) !=
                    currentRound.GetHash(isContainPreviousInValue))
                {
                    Context.LogDebug(() => $"Round information of block header:\n{headerInformation.Round}");
                    Context.LogDebug(() => $"Round information of executing result:\n{currentRound}");
                    return(new ValidationResult
                    {
                        Success = false, Message = "Current round information is different with consensus extra data."
                    });
                }
            }

            return(new ValidationResult {
                Success = true
            });
        }
コード例 #14
0
        private async Task <AElfConsensusTriggerInformation> GetConsensusTriggerInfoAsync(
            AEDPoSContractImplContainer.AEDPoSContractImplStub contractStub, BytesValue pubkey)
        {
            var command = await contractStub.GetConsensusCommand.CallAsync(pubkey);

            var hint = AElfConsensusHint.Parser.ParseFrom(command.Hint);
            var triggerInformation = new AElfConsensusTriggerInformation
            {
                Behaviour = hint.Behaviour,
                // It doesn't matter for testing.
                InValue         = Hash.FromString($"InValueOf{pubkey}"),
                PreviousInValue = Hash.FromString($"InValueOf{pubkey}"),
                Pubkey          = pubkey.Value
            };

            var consensusExtraData = await contractStub.GetConsensusExtraData.CallAsync(new BytesValue
            {
                Value = triggerInformation.ToByteString()
            });

            var consensusHeaderInformation = new AElfConsensusHeaderInformation();

            consensusHeaderInformation.MergeFrom(consensusExtraData.Value);
            Debug.WriteLine($"Current header information: {consensusHeaderInformation}");

            // Validate consensus extra data.
            {
                var validationResult =
                    await _contractStubs.First().ValidateConsensusBeforeExecution.CallAsync(consensusExtraData);

                if (!validationResult.Success)
                {
                    throw new Exception($"Consensus extra data validation failed: {validationResult.Message}");
                }
            }

            return(triggerInformation);
        }
        public override ValidationResult ValidateConsensusAfterExecution(BytesValue input1)
        {
            var input = new AElfConsensusHeaderInformation();

            input.MergeFrom(input1.Value);
            if (TryToGetCurrentRoundInformation(out var currentRound))
            {
                var isContainPreviousInValue =
                    input.Behaviour != AElfConsensusBehaviour.UpdateValueWithoutPreviousInValue;
                if (input.Round.GetHash(isContainPreviousInValue) != currentRound.GetHash(isContainPreviousInValue))
                {
                    Context.LogDebug(() => $"Round information of block header:\n{input.Round}");
                    Context.LogDebug(() => $"Round information of executing result:\n{currentRound}");
                    return(new ValidationResult
                    {
                        Success = false, Message = "Current round information is different with consensus extra data."
                    });
                }
            }

            return(new ValidationResult {
                Success = true
            });
        }
コード例 #16
0
        /// <summary>
        /// This method will be executed before executing a block.
        /// </summary>
        /// <param name="extraData"></param>
        /// <returns></returns>
        private ValidationResult ValidateBeforeExecution(AElfConsensusHeaderInformation extraData)
        {
            // According to current round information:
            if (!TryToGetCurrentRoundInformation(out var baseRound))
            {
                return(new ValidationResult {
                    Success = false, Message = "Failed to get current round information."
                });
            }

            // Skip the certain initial miner during first several rounds. (When other nodes haven't produce blocks yet.)
            if (baseRound.RealTimeMinersInformation.Count != 1 &&
                Context.CurrentHeight < AEDPoSContractConstants.MaximumTinyBlocksCount.Mul(3))
            {
                string producedMiner = null;
                var    result        = true;
                for (var i = baseRound.RoundNumber; i > 0; i--)
                {
                    var producedMiners = State.Rounds[i].RealTimeMinersInformation.Values
                                         .Where(m => m.ActualMiningTimes.Any()).ToList();
                    if (producedMiners.Count != 1)
                    {
                        result = false;
                        break;
                    }
                    if (producedMiner == null)
                    {
                        producedMiner = producedMiners.Single().Pubkey;
                    }
                    else if (producedMiner != producedMiners.Single().Pubkey)
                    {
                        result = false;
                    }
                }

                if (result)
                {
                    return(new ValidationResult {
                        Success = true
                    });
                }
            }

            if (extraData.Behaviour == AElfConsensusBehaviour.UpdateValue)
            {
                baseRound.RecoverFromUpdateValue(extraData.Round, extraData.SenderPubkey.ToHex());
            }

            if (extraData.Behaviour == AElfConsensusBehaviour.TinyBlock)
            {
                baseRound.RecoverFromTinyBlock(extraData.Round, extraData.SenderPubkey.ToHex());
            }

            var validationContext = new ConsensusValidationContext
            {
                BaseRound                     = baseRound,
                CurrentTermNumber             = State.CurrentTermNumber.Value,
                CurrentRoundNumber            = State.CurrentRoundNumber.Value,
                PreviousRound                 = TryToGetPreviousRoundInformation(out var previousRound) ? previousRound : new Round(),
                LatestPubkeyToTinyBlocksCount = State.LatestPubkeyToTinyBlocksCount.Value,
                ExtraData                     = extraData
            };

            /* Ask several questions: */

            // Add basic providers at first.
            var validationProviders = new List <IHeaderInformationValidationProvider>
            {
                // Is sender in miner list (of base round)?
                new MiningPermissionValidationProvider(),

                // Is this block produced in proper time?
                new TimeSlotValidationProvider(),

                // Is sender produced too many blocks at one time?
                new ContinuousBlocksValidationProvider()
            };

            switch (extraData.Behaviour)
            {
            case AElfConsensusBehaviour.UpdateValue:
                validationProviders.Add(new UpdateValueValidationProvider());
                // Is confirmed lib height and lib round number went down? (Which should not happens.)
                validationProviders.Add(new LibInformationValidationProvider());
                break;

            case AElfConsensusBehaviour.NextRound:
                // Is sender's order of next round correct?
                validationProviders.Add(new NextRoundMiningOrderValidationProvider());
                validationProviders.Add(new RoundTerminateValidationProvider());
                break;

            case AElfConsensusBehaviour.NextTerm:
                validationProviders.Add(new RoundTerminateValidationProvider());
                break;
            }

            var service = new HeaderInformationValidationService(validationProviders);

            Context.LogDebug(() => $"Validating behaviour: {extraData.Behaviour.ToString()}");

            var validationResult = service.ValidateInformation(validationContext);

            if (validationResult.Success == false)
            {
                Context.LogDebug(() => $"Consensus Validation before execution failed : {validationResult.Message}");
            }

            return(validationResult);
        }
    }
コード例 #17
0
        /// <summary>
        /// This method will be executed before executing a block.
        /// </summary>
        /// <param name="extraData"></param>
        /// <returns></returns>
        private ValidationResult ValidateBeforeExecution(AElfConsensusHeaderInformation extraData)
        {
            // According to current round information:
            if (!TryToGetCurrentRoundInformation(out var baseRound))
            {
                return(new ValidationResult {
                    Success = false, Message = "Failed to get current round information."
                });
            }

            if (extraData.Behaviour == AElfConsensusBehaviour.UpdateValue)
            {
                baseRound.RecoverFromUpdateValue(extraData.Round, extraData.SenderPubkey.ToHex());
            }

            if (extraData.Behaviour == AElfConsensusBehaviour.TinyBlock)
            {
                baseRound.RecoverFromTinyBlock(extraData.Round, extraData.SenderPubkey.ToHex());
            }

            var validationContext = new ConsensusValidationContext
            {
                BaseRound                     = baseRound,
                CurrentTermNumber             = State.CurrentTermNumber.Value,
                CurrentRoundNumber            = State.CurrentRoundNumber.Value,
                PreviousRound                 = TryToGetPreviousRoundInformation(out var previousRound) ? previousRound : new Round(),
                LatestPubkeyToTinyBlocksCount = State.LatestPubkeyToTinyBlocksCount.Value,
                ExtraData                     = extraData
            };

            /* Ask several questions: */

            // Add basic providers at first.
            var validationProviders = new List <IHeaderInformationValidationProvider>
            {
                // Is sender in miner list (of base round)?
                new MiningPermissionValidationProvider(),

                // Is this block produced in proper time?
                new TimeSlotValidationProvider(),

                // Is sender produced too many blocks at one time?
                new ContinuousBlocksValidationProvider()
            };

            switch (extraData.Behaviour)
            {
            case AElfConsensusBehaviour.UpdateValue:
                validationProviders.Add(new UpdateValueValidationProvider());
                // Is confirmed lib height and lib round number went down? (Which should not happens.)
                validationProviders.Add(new LibInformationValidationProvider());
                break;

            case AElfConsensusBehaviour.NextRound:
                // Is sender's order of next round correct?
                validationProviders.Add(new NextRoundMiningOrderValidationProvider());
                validationProviders.Add(new RoundTerminateValidationProvider());
                break;

            case AElfConsensusBehaviour.NextTerm:
                validationProviders.Add(new RoundTerminateValidationProvider());
                break;
            }

            var service = new HeaderInformationValidationService(validationProviders);

            Context.LogDebug(() => $"Validating behaviour: {extraData.Behaviour.ToString()}");

            var validationResult = service.ValidateInformation(validationContext);

            if (validationResult.Success == false)
            {
                Context.LogDebug(() => $"Consensus Validation before execution failed : {validationResult.Message}");
            }

            return(validationResult);
        }
    }
コード例 #18
0
        /// <summary>
        /// This method will be executed before executing a block.
        /// </summary>
        /// <param name="extraData"></param>
        /// <returns></returns>
        private ValidationResult ValidateBeforeExecution(AElfConsensusHeaderInformation extraData)
        {
            // We can trust this because we already validated the pubkey
            // during `AEDPoSExtraDataExtractor.ExtractConsensusExtraData`
            var pubkey = extraData.SenderPubkey.ToHex();

            // This validation focuses on the new round information.
            var providedRound = extraData.Round;

            // According to current round information:
            if (!TryToGetCurrentRoundInformation(out var baseRound))
            {
                return(new ValidationResult {
                    Success = false, Message = "Failed to get current round information."
                });
            }

            /* Ask several questions: */

            // Is sender in miner list?
            if (!baseRound.IsInMinerList(pubkey))
            {
                Context.LogDebug(() => "Sender is not a miner.");
                return(new ValidationResult {
                    Success = false, Message = $"Sender {pubkey} is not a miner."
                });
            }

            // If provided round is a new round
            if (providedRound.RoundId != baseRound.RoundId)
            {
                // Is round information fits time slot rule?
                var timeSlotsCheckResult = providedRound.CheckRoundTimeSlots();
                if (!timeSlotsCheckResult.Success)
                {
                    Context.LogDebug(() => $"Round time slots incorrect: {timeSlotsCheckResult.Message}");
                    return(timeSlotsCheckResult);
                }
            }
            else
            {
                // Is sender respect his time slot?
                // It is maybe failing due to using too much time producing previous tiny blocks.
                if (!CheckMinerTimeSlot(providedRound, pubkey))
                {
                    Context.LogDebug(() => "Time slot already passed before execution.");
                    return(new ValidationResult {
                        Message = "Time slot already passed before execution."
                    });
                }
            }

            // Is sender produce too many continuous blocks?
            // Skip first two rounds.
            if (providedRound.RoundNumber > 2 && baseRound.RealTimeMinersInformation.Count != 1)
            {
                var latestProviderToTinyBlocksCount = State.LatestProviderToTinyBlocksCount.Value;
                if (latestProviderToTinyBlocksCount != null && latestProviderToTinyBlocksCount.Pubkey == pubkey &&
                    latestProviderToTinyBlocksCount.BlocksCount < 0)
                {
                    Context.LogDebug(() => $"Sender {pubkey} produced too many continuous blocks.");
                    return(new ValidationResult {
                        Message = "Sender produced too many continuous blocks."
                    });
                }
            }

            // Is sender's order of next round correct?
            // Miners that have determined the order of the next round should be equal to
            // miners that mined blocks during current round.
            if (providedRound.RealTimeMinersInformation.Values.Where(m => m.FinalOrderOfNextRound > 0).Distinct()
                .Count() != providedRound.RealTimeMinersInformation.Values.Count(m => m.OutValue != null))
            {
                return(new ValidationResult {
                    Message = "Invalid FinalOrderOfNextRound."
                });
            }

            // Is confirmed lib height and lib round number went down?
            if (baseRound.ConfirmedIrreversibleBlockHeight > providedRound.ConfirmedIrreversibleBlockHeight ||
                baseRound.ConfirmedIrreversibleBlockRoundNumber > providedRound.ConfirmedIrreversibleBlockRoundNumber)
            {
                return(new ValidationResult {
                    Message = "Incorrect confirmed lib information."
                });
            }

            switch (extraData.Behaviour)
            {
            case AElfConsensusBehaviour.UpdateValueWithoutPreviousInValue:
            case AElfConsensusBehaviour.UpdateValue:
                return(ValidationForUpdateValue(extraData));

            case AElfConsensusBehaviour.NextRound:
                return(ValidationForNextRound(extraData));

            case AElfConsensusBehaviour.NextTerm:
                return(ValidationForNextTerm(extraData));
            }

            return(new ValidationResult {
                Success = true
            });
        }
        public override TransactionList GenerateConsensusTransactions(BytesValue input)
        {
            var triggerInformation = new AElfConsensusTriggerInformation();

            triggerInformation.MergeFrom(input.Value);
            // Some basic checks.
            Assert(triggerInformation.Pubkey.Any(),
                   "Data to request consensus information should contain public key.");

            var publicKey            = triggerInformation.Pubkey;
            var consensusInformation = new AElfConsensusHeaderInformation();

            consensusInformation.MergeFrom(GetConsensusBlockExtraData(input, true).Value);
            var round     = consensusInformation.Round;
            var behaviour = consensusInformation.Behaviour;

            switch (behaviour)
            {
            case AElfConsensusBehaviour.UpdateValueWithoutPreviousInValue:
            case AElfConsensusBehaviour.UpdateValue:
                return(new TransactionList
                {
                    Transactions =
                    {
                        GenerateTransaction(nameof(UpdateValue),
                                            round.ExtractInformationToUpdateConsensus(publicKey.ToHex()))
                    }
                });

            case AElfConsensusBehaviour.TinyBlock:
                var minerInRound = round.RealTimeMinersInformation[publicKey.ToHex()];
                return(new TransactionList
                {
                    Transactions =
                    {
                        GenerateTransaction(nameof(UpdateTinyBlockInformation),
                                            new TinyBlockInput
                        {
                            ActualMiningTime = minerInRound.ActualMiningTimes.Last(),
                            ProducedBlocks = minerInRound.ProducedBlocks,
                            RoundId = round.RoundId
                        })
                    }
                });

            case AElfConsensusBehaviour.NextRound:
                return(new TransactionList
                {
                    Transactions =
                    {
                        GenerateTransaction(nameof(NextRound), round)
                    }
                });

            case AElfConsensusBehaviour.NextTerm:
                return(new TransactionList
                {
                    Transactions =
                    {
                        GenerateTransaction(nameof(NextTerm), round)
                    }
                });

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
コード例 #20
0
        public async Task MineBlockAsync(List <Transaction> transactions = null, bool withException = false)
        {
            if (!_isSystemContractsDeployed)
            {
                return;
            }

            if (transactions != null)
            {
                await _testDataProvider.AddTransactionListAsync(transactions);
            }

            var currentBlockTime = _testDataProvider.GetBlockTime();

            {
                {
                    var currentRound = await _contractStubs.First().GetCurrentRoundInformation.CallAsync(new Empty());

                    if (currentRound.RoundNumber == 0)
                    {
                        throw new InitializationFailedException("Can't find current round information.");
                    }
                }
            }

            var maximumBlocksCount = (await _contractStubs.First().GetMaximumBlocksCount.CallAsync(new Empty())).Value;

            var(contractStub, pubkey) = GetProperContractStub(currentBlockTime, maximumBlocksCount);
            currentBlockTime          = _testDataProvider.GetBlockTime();

            {
                var currentRound = await _contractStubs.First().GetCurrentRoundInformation.CallAsync(new Empty());

                if (currentRound.RoundNumber == 0)
                {
                    throw new InitializationFailedException("Can't find current round information.");
                }
            }

            var command = await contractStub.GetConsensusCommand.CallAsync(pubkey);

            var hint = AElfConsensusHint.Parser.ParseFrom(command.Hint);
            var triggerInformation = new AElfConsensusTriggerInformation
            {
                Behaviour = hint.Behaviour,
                // It doesn't matter for testing.
                InValue         = Hash.FromString($"InValueOf{pubkey}"),
                PreviousInValue = Hash.FromString($"InValueOf{pubkey}"),
                Pubkey          = pubkey.Value
            };

            var consensusExtraData = await contractStub.GetConsensusExtraData.CallAsync(new BytesValue
            {
                Value = triggerInformation.ToByteString()
            });

            var consensusHeaderInformation = new AElfConsensusHeaderInformation();

            consensusHeaderInformation.MergeFrom(consensusExtraData.Value);
            Debug.WriteLine($"Current header information: {consensusHeaderInformation}");

            // Validate consensus extra data.
            {
                var validationResult =
                    await _contractStubs.First().ValidateConsensusBeforeExecution.CallAsync(consensusExtraData);

                if (!validationResult.Success)
                {
                    throw new Exception($"Consensus extra data validation failed: {validationResult.Message}");
                }
            }

            var consensusTransaction = await contractStub.GenerateConsensusTransactions.CallAsync(new BytesValue
            {
                Value = triggerInformation.ToByteString()
            });

            await MineAsync(contractStub, consensusTransaction.Transactions.First(), withException);

            _currentRound = await _contractStubs.First().GetCurrentRoundInformation.CallAsync(new Empty());

            Debug.WriteLine($"Update current round information.{_currentRound}");
            if (!_isSkipped)
            {
                if (_currentRound.RealTimeMinersInformation.Any(i => i.Value.MissedTimeSlots != 0))
                {
                    var previousRound = await _contractStubs.First().GetPreviousRoundInformation.CallAsync(new Empty());

                    throw new BlockMiningException(
                              $"Someone missed time slot.\n{_currentRound}\n{previousRound}\nCurrent block time: {currentBlockTime}");
                }
            }

            _testDataProvider.SetBlockTime(
                consensusTransaction.Transactions.First().MethodName ==
                nameof(AEDPoSContractImplContainer.AEDPoSContractImplStub.NextTerm)
                    ? currentBlockTime.AddMilliseconds(AEDPoSExtensionConstants.MiningInterval)
                    : currentBlockTime.AddMilliseconds(AEDPoSExtensionConstants.ActualMiningInterval));

            await _testDataProvider.ResetAsync();

            _isSkipped = false;
        }