Esempio n. 1
0
        public Round GenerateFirstRoundOfNewTerm(int miningInterval,
                                                 Timestamp currentBlockTime, long currentRoundNumber = 0, long currentTermNumber = 0)
        {
            var sortedMiners =
                (from obj in Pubkeys
                 .ToDictionary <ByteString, string, int>(miner => miner.ToHex(), miner => miner[0])
                 orderby obj.Value descending
                 select obj.Key).ToList();

            var round = new Consensus.AEDPoS.Round();

            for (var i = 0; i < sortedMiners.Count; i++)
            {
                var minerInRound = new MinerInRound();

                // The first miner will be the extra block producer of first round of each term.
                if (i == 0)
                {
                    minerInRound.IsExtraBlockProducer = true;
                }

                minerInRound.Pubkey             = sortedMiners[i];
                minerInRound.Order              = i + 1;
                minerInRound.ExpectedMiningTime = currentBlockTime.AddMilliseconds((i * miningInterval) + miningInterval);
                // Should be careful during validation.
                minerInRound.PreviousInValue = Hash.Empty;

                round.RealTimeMinersInformation.Add(sortedMiners[i], minerInRound);
            }

            round.RoundNumber = currentRoundNumber + 1;
            round.TermNumber  = currentTermNumber + 1;

            return(round);
        }
        private Round GenerateFirstRoundOfNewTerm(MinerList minerList, int miningInterval,
                                                  Timestamp currentBlockTime, long currentRoundNumber = 0, long currentTermNumber = 0)
        {
            var sortedMiners = minerList.Pubkeys.Select(x => x.ToHex()).ToList();
            var round        = new Round();

            for (var i = 0; i < sortedMiners.Count; i++)
            {
                var minerInRound = new MinerInRound();

                // The third miner will be the extra block producer of first round of each term.
                if (i == 0)
                {
                    minerInRound.IsExtraBlockProducer = true;
                }

                minerInRound.Pubkey             = sortedMiners[i];
                minerInRound.Order              = i + 1;
                minerInRound.ExpectedMiningTime = currentBlockTime.AddMilliseconds(i * miningInterval + miningInterval);
                // Should be careful during validation.
                minerInRound.PreviousInValue = Hash.Empty;
                round.RealTimeMinersInformation.Add(sortedMiners[i], minerInRound);
            }

            round.RoundNumber                       = currentRoundNumber + 1;
            round.TermNumber                        = currentTermNumber + 1;
            round.IsMinerListJustChanged            = true;
            round.ExtraBlockProducerOfPreviousRound = sortedMiners[0];

            return(round);
        }
        public bool IsInCorrectFutureMiningSlot(string pubkey, Timestamp currentBlockTime)
        {
            var miningInterval = GetMiningInterval();

            var arrangedMiningTime =
                ArrangeAbnormalMiningTime(pubkey, currentBlockTime.AddMilliseconds(-miningInterval));

            return(arrangedMiningTime <= currentBlockTime &&
                   currentBlockTime <= arrangedMiningTime.AddMilliseconds(miningInterval));
        }
        /// <summary>
        /// Finally make current block time in the range of (expected_mining_time, expected_mining_time + time_for_each_block)
        /// </summary>
        /// <param name="miningInterval"></param>
        /// <param name="originExpectedMiningTime"></param>
        /// <param name="expectedMiningTime"></param>
        private void TuneExpectedMiningTimeForTinyBlock(int miningInterval, Timestamp originExpectedMiningTime,
                                                        ref Timestamp expectedMiningTime)
        {
            var timeForEachBlock = miningInterval.Div(AEDPoSContractConstants.TotalTinySlots);
            var currentBlockTime = Context.CurrentBlockTime;

            while (expectedMiningTime < currentBlockTime &&
                   expectedMiningTime < originExpectedMiningTime.AddMilliseconds(miningInterval))
            {
                expectedMiningTime = expectedMiningTime.AddMilliseconds(timeForEachBlock);
                var toPrint = expectedMiningTime.Clone();
                Context.LogDebug(() => $"Moving to next tiny block time slot. {toPrint}");
            }
        }
        /// <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()}");
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Returns the data dictionary from cache or disk (updates the data type version as well).
        /// </summary>
        protected virtual byte[] ReadDictionary(double maxAge)
        {
            byte[] dictionary = Value;

            // return cached value.
            if (dictionary != null)
            {
                if (Timestamp.AddMilliseconds(MinimumSamplingInterval) > DateTime.UtcNow)
                {
                    return(dictionary);
                }

                if (Timestamp.AddMilliseconds(maxAge) > DateTime.UtcNow)
                {
                    return(dictionary);
                }

                if (m_assembly != null)
                {
                    return(dictionary);
                }
            }

            // read from source.
            try
            {
                if (m_assembly != null)
                {
                    dictionary = ReadDictionaryFromResource(m_filePath);
                }
                else if (m_filePath.StartsWith(Uri.UriSchemeHttp))
                {
                    dictionary = ReadDictionaryFromHttpFile(m_filePath);
                }
                else
                {
                    dictionary = ReadDictionaryFromDiskFile(m_filePath);
                }

                return(dictionary);
            }
            catch (Exception e)
            {
                ServiceResult error = ServiceResult.Create(e, StatusCodes.BadOutOfService, "Could not access data dictionary file: {0}.", m_filePath);
                UpdateStatus(error);
                return(null);
            }
        }
 public void SetBlockTime(int offsetMilliseconds)
 {
     SetBlockTime(_blockTime.AddMilliseconds(offsetMilliseconds));
 }
Esempio n. 8
0
        public bool GenerateNextRoundInformation(Timestamp currentBlockTimestamp, Timestamp blockchainStartTimestamp,
                                                 out Round nextRound, bool isMinerListChanged = false)
        {
            nextRound = new Round {
                IsMinerListJustChanged = isMinerListChanged
            };

            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,
                    // Update missed time slots count of one miner.
                    MissedTimeSlots = minerInRound.MissedTimeSlots.Add(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);

            nextRound.ConfirmedIrreversibleBlockHeight      = ConfirmedIrreversibleBlockHeight;
            nextRound.ConfirmedIrreversibleBlockRoundNumber = ConfirmedIrreversibleBlockRoundNumber;

            return(true);
        }
Esempio n. 9
0
        private (AEDPoSContractImplContainer.AEDPoSContractImplStub, BytesValue) GetProperContractStub(
            Timestamp currentBlockTime, int maximumBlocksCount)
        {
            try
            {
                if (_currentRound.RoundNumber == 0)
                {
                    throw new BlockMiningException("Invalid round information.");
                }

                var roundStartTime = _currentRound.RealTimeMinersInformation.Single(i => i.Value.Order == 1).Value
                                     .ExpectedMiningTime;

                var roundEndTime = _currentRound.RealTimeMinersInformation
                                   .Single(i => i.Value.Order == _currentRound.RealTimeMinersInformation.Count).Value
                                   .ExpectedMiningTime.AddMilliseconds(AEDPoSExtensionConstants.MiningInterval);
                if (currentBlockTime > roundEndTime)
                {
                    throw new BlockMiningException("Failed to find proper contract stub.");
                }

                var ebp = _currentRound.RealTimeMinersInformation.Values.FirstOrDefault(i =>
                                                                                        i.Pubkey == _currentRound.ExtraBlockProducerOfPreviousRound);
                if (ebp != null && _currentRound.RealTimeMinersInformation.Values.All(i => i.OutValue == null) &&
                    currentBlockTime < roundStartTime && ebp.ActualMiningTimes.Count + 1 <= maximumBlocksCount)
                {
                    Debug.WriteLine("Tiny block before new round.");
                    return(ProperContractStub(ebp));
                }

                foreach (var minerInRound in _currentRound.RealTimeMinersInformation.Values.OrderBy(m => m.Order)
                         .ToList())
                {
                    if (minerInRound.ExpectedMiningTime <= currentBlockTime && currentBlockTime <
                        minerInRound.ExpectedMiningTime.AddMilliseconds(AEDPoSExtensionConstants.MiningInterval) &&
                        (minerInRound.ActualMiningTimes.Count + 1 <= maximumBlocksCount ||
                         minerInRound.Pubkey == _currentRound.ExtraBlockProducerOfPreviousRound &&
                         minerInRound.ActualMiningTimes.Count + 2 <= maximumBlocksCount * 2))
                    {
                        Debug.WriteLine("Normal block or tiny block.");
                        return(ProperContractStub(minerInRound));
                    }

                    var minersCount = _currentRound.RealTimeMinersInformation.Count;
                    if (minerInRound.IsExtraBlockProducer &&
                        _currentRound.RealTimeMinersInformation.Values.Count(m => m.OutValue != null) == minersCount)
                    {
                        Debug.WriteLine("End of current round.");
                        return(ProperContractStub(minerInRound));
                    }
                }
            }
            catch (Exception e)
            {
                throw new BlockMiningException("Failed to find proper contract stub.", e);
            }

            _testDataProvider.SetBlockTime(AEDPoSExtensionConstants.ActualMiningInterval);
            Debug.WriteLine("Move forward time.");
            return(GetProperContractStub(
                       currentBlockTime.AddMilliseconds(AEDPoSExtensionConstants.ActualMiningInterval), maximumBlocksCount));
        }
Esempio n. 10
0
 public static Timestamp ArrangeMiningTimeBasedOnOffset(Timestamp currentBlockTime, int offset)
 {
     return(currentBlockTime.AddMilliseconds(offset));
 }