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()}"); } }
/// <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)); }
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); }
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)); }
public static Timestamp ArrangeMiningTimeBasedOnOffset(Timestamp currentBlockTime, int offset) { return(currentBlockTime.AddMilliseconds(offset)); }