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);
        }
Example #2
0
        /// <summary>
        /// Only able to generate information of first round.
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="minersCount"></param>
        /// <param name="miningInterval"></param>
        /// <returns></returns>
        private Round GenerateFirstRound(DateTime startTime, int minersCount, int miningInterval)
        {
            var round = new Round {
                RoundNumber = 1
            };
            var extraBlockProducerOrder = new Random().Next(1, minersCount);

            for (var i = 0; i < minersCount; i++)
            {
                var keyPair      = CryptoHelpers.GenerateKeyPair();
                var minerInRound = new MinerInRound
                {
                    PublicKey          = keyPair.PublicKey.ToHex(),
                    Signature          = Hash.Generate(),
                    Order              = i + 1,
                    ExpectedMiningTime = startTime.AddMilliseconds(miningInterval * i).ToTimestamp()
                };

                if (extraBlockProducerOrder == minerInRound.Order)
                {
                    minerInRound.IsExtraBlockProducer = true;
                }

                round.RealTimeMinersInformation.Add(minerInRound.PublicKey, minerInRound);
            }

            return(round);
        }
Example #3
0
        private (AEDPoSContractImplContainer.AEDPoSContractImplStub, BytesValue) ProperContractStub(
            MinerInRound minerInRound)
        {
            var pubkey  = ByteArrayHelper.HexStringToByteArray(minerInRound.Pubkey);
            var keyPair = SampleECKeyPairs.KeyPairs.First(p => p.PublicKey.BytesEqual(pubkey));

            _testDataProvider.SetKeyPair(keyPair);
            Debug.WriteLine($"Chosen miner: {keyPair.PublicKey.ToHex()}");
            return(_contractTesterFactory.Create <AEDPoSContractImplContainer.AEDPoSContractImplStub>(
                       _consensusContractAddress, keyPair), new BytesValue {
                Value = ByteString.CopyFrom(pubkey)
            });
        }
        /// <summary>
        /// For now, if current time is behind the half of expected mining time slot,
        /// we can say this node missed his time slot.
        /// </summary>
        /// <param name="round"></param>
        /// <param name="publicKey"></param>
        /// <param name="dateTime"></param>
        /// <param name="minerInRound"></param>
        /// <returns></returns>
        public static bool IsTimeSlotPassed(this Round round, string publicKey, DateTime dateTime,
                                            out MinerInRound minerInRound)
        {
            minerInRound = null;
            var miningInterval = round.GetMiningInterval();

            if (round.RealTimeMinersInformation.ContainsKey(publicKey))
            {
                minerInRound = round.RealTimeMinersInformation[publicKey];
                return(minerInRound.ExpectedMiningTime.ToDateTime().AddMilliseconds((double)miningInterval / 2) <
                       dateTime);
            }

            return(false);
        }
Example #5
0
//        public static bool IsEmpty(this Round round)
//        {
//            return round.RoundId == 0;
//        }
//
//        public static string GetLogs(this Round round, string publicKey, DPoSBehaviour behaviour)
//        {
//            var logs = new StringBuilder($"\n[Round {round.RoundNumber}](Round Id: {round.RoundId})[Term {round.TermNumber}]");
//            foreach (var minerInRound in round.RealTimeMinersInformation.Values.OrderBy(m => m.Order))
//            {
//                var minerInformation = new StringBuilder("\n");
//                minerInformation.Append($"[{minerInRound.PublicKey.Substring(0, 10)}]");
//                minerInformation.Append(minerInRound.IsExtraBlockProducer ? "(Current EBP)" : "");
//                minerInformation.AppendLine(minerInRound.PublicKey == publicKey
//                    ? "(This Node)"
//                    : "");
//                minerInformation.AppendLine($"Order:\t {minerInRound.Order}");
//                minerInformation.AppendLine(
//                    $"Expect:\t {minerInRound.ExpectedMiningTime?.ToDateTime().ToUniversalTime():yyyy-MM-dd HH.mm.ss,ffffff}");
//                minerInformation.AppendLine(
//                    $"Actual:\t {minerInRound.ActualMiningTime?.ToDateTime().ToUniversalTime():yyyy-MM-dd HH.mm.ss,ffffff}");
//                minerInformation.AppendLine($"Out:\t {minerInRound.OutValue?.ToHex()}");
//                if (round.RoundNumber != 1)
//                {
//                    minerInformation.AppendLine($"PreIn:\t {minerInRound.PreviousInValue?.ToHex()}");
//                }
//
//                minerInformation.AppendLine($"Sig:\t {minerInRound.Signature?.ToHex()}");
//                minerInformation.AppendLine($"Mine:\t {minerInRound.ProducedBlocks}");
//                minerInformation.AppendLine($"Miss:\t {minerInRound.MissedTimeSlots}");
//                minerInformation.AppendLine($"Proms:\t {minerInRound.PromisedTinyBlocks}");
//                minerInformation.AppendLine($"NOrder:\t {minerInRound.FinalOrderOfNextRound}");
//
//                logs.Append(minerInformation);
//            }
//
//            logs.AppendLine($"Recent behaviour: {behaviour.ToString()}");
//
//            return logs.ToString();
//        }
//
//        public static Hash CalculateInValue(this Round round, Hash randomHash)
//        {
//            return Hash.FromTwoHashes(Hash.FromMessage(new Int64Value {Value = round.RoundId}), randomHash);
//        }
//
//        public static MinerInRound GetExtraBlockProducerInformation(this Round round)
//        {
//            return round.RealTimeMinersInformation.First(bp => bp.Value.IsExtraBlockProducer).Value;
//        }
//
//        /// <summary>
//        /// Maybe tune other miners' supposed order of next round,
//        /// will record this purpose to their FinalOrderOfNextRound field.
//        /// </summary>
//        /// <param name="round"></param>
//        /// <param name="publicKey"></param>
//        /// <returns></returns>
//        public static ToUpdate ExtractInformationToUpdateConsensus(this Round round, string publicKey)
//        {
//            if (!round.RealTimeMinersInformation.ContainsKey(publicKey))
//            {
//                return null;
//            }
//
//            var tuneOrderInformation = round.RealTimeMinersInformation.Values
//                .Where(m => m.FinalOrderOfNextRound != m.SupposedOrderOfNextRound)
//                .ToDictionary(m => m.PublicKey, m => m.FinalOrderOfNextRound);
//
//            var decryptedPreviousInValues = round.RealTimeMinersInformation.Values.Where(v =>
//                    v.PublicKey != publicKey && v.DecryptedPreviousInValues.ContainsKey(publicKey))
//                .ToDictionary(info => info.PublicKey, info => info.DecryptedPreviousInValues[publicKey]);
//
//            var minersPreviousInValues =
//                round.RealTimeMinersInformation.Values.Where(info => info.PreviousInValue != null).ToDictionary(info => info.PublicKey,
//                    info => info.PreviousInValue);
//
//            var minerInRound = round.RealTimeMinersInformation[publicKey];
//            return new ToUpdate
//            {
//                OutValue = minerInRound.OutValue,
//                Signature = minerInRound.Signature,
//                PreviousInValue = minerInRound.PreviousInValue ?? Hash.Empty,
//                RoundId = round.RoundId,
//                PromiseTinyBlocks = minerInRound.PromisedTinyBlocks,
//                ProducedBlocks = minerInRound.ProducedBlocks,
//                ActualMiningTime = minerInRound.ActualMiningTime,
//                SupposedOrderOfNextRound = minerInRound.SupposedOrderOfNextRound,
//                TuneOrderInformation = {tuneOrderInformation},
//                EncryptedInValues = {minerInRound.EncryptedInValues},
//                DecryptedPreviousInValues = {decryptedPreviousInValues},
//                MinersPreviousInValues = {minersPreviousInValues}
//            };
//        }
//
//        public static Round ApplyNormalConsensusData(this Round round, string publicKey, Hash previousInValue,
//            Hash outValue, Hash signature, DateTime dateTime)
//        {
//            if (!round.RealTimeMinersInformation.ContainsKey(publicKey))
//            {
//                return round;
//            }
//
//            round.RealTimeMinersInformation[publicKey].ActualMiningTime = dateTime.ToTimestamp();
//            round.RealTimeMinersInformation[publicKey].OutValue = outValue;
//            round.RealTimeMinersInformation[publicKey].Signature = signature;
//            round.RealTimeMinersInformation[publicKey].ProducedBlocks += 1;
//            if (previousInValue != Hash.Empty)
//            {
//                round.RealTimeMinersInformation[publicKey].PreviousInValue = previousInValue;
//            }
//
//            var minersCount = round.RealTimeMinersInformation.Count;
//            var sigNum =
//                BitConverter.ToInt64(
//                    BitConverter.IsLittleEndian ? signature.Value.Reverse().ToArray() : signature.Value.ToArray(),
//                    0);
//            var supposedOrderOfNextRound = GetAbsModulus(sigNum, minersCount) + 1;
//
//            // Check the existence of conflicts about OrderOfNextRound.
//            // If so, modify others'.
//            var conflicts = round.RealTimeMinersInformation.Values
//                .Where(i => i.FinalOrderOfNextRound == supposedOrderOfNextRound).ToList();
//
//            foreach (var orderConflictedMiner in conflicts)
//            {
//                // Though multiple conflicts should be wrong, we can still arrange their orders of next round.
//
//                for (var i = supposedOrderOfNextRound + 1; i < minersCount * 2; i++)
//                {
//                    var maybeNewOrder = i > minersCount ? i % minersCount : i;
//                    if (round.RealTimeMinersInformation.Values.All(m => m.FinalOrderOfNextRound != maybeNewOrder))
//                    {
//                        round.RealTimeMinersInformation[orderConflictedMiner.PublicKey].FinalOrderOfNextRound =
//                            maybeNewOrder;
//                        break;
//                    }
//                }
//            }
//
//            round.RealTimeMinersInformation[publicKey].SupposedOrderOfNextRound = supposedOrderOfNextRound;
//            // Initialize FinalOrderOfNextRound as the value of SupposedOrderOfNextRound
//            round.RealTimeMinersInformation[publicKey].FinalOrderOfNextRound = supposedOrderOfNextRound;
//
//            return round;
//        }
//
//        public static List<MinerInRound> GetMinedMiners(this Round round)
//        {
//            // For now only this implementation can support test cases.
//            return round.RealTimeMinersInformation.Values.Where(m => m.SupposedOrderOfNextRound != 0).ToList();
//        }
//
//        public static List<MinerInRound> GetNotMinedMiners(this Round round)
//        {
//            // For now only this implementation can support test cases.
//            return round.RealTimeMinersInformation.Values.Where(m => m.SupposedOrderOfNextRound == 0).ToList();
//        }
//
//        public static bool GenerateNextRoundInformation(this Round currentRound, DateTime dateTime,
//            Timestamp blockchainStartTimestamp, out Round nextRound)
//        {
//            nextRound = new Round();
//
//            var minersMinedCurrentRound = currentRound.GetMinedMiners();
//            var minersNotMinedCurrentRound = currentRound.GetNotMinedMiners();
//            var minersCount = currentRound.RealTimeMinersInformation.Count;
//
//            var miningInterval = currentRound.GetMiningInterval();
//            nextRound.RoundNumber = currentRound.RoundNumber + 1;
//            nextRound.TermNumber = currentRound.TermNumber;
//            if (currentRound.RoundNumber == 1)
//            {
//                nextRound.BlockchainAge = 1;
//            }
//            else
//            {
//                nextRound.BlockchainAge =
//                    (long) (dateTime - blockchainStartTimestamp.ToDateTime())
//                    .TotalMinutes; // TODO: Change to TotalDays after testing.
//            }
//
//            // 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.PublicKey] = new MinerInRound
//                {
//                    PublicKey = minerInRound.PublicKey,
//                    Order = order,
//                    ExpectedMiningTime = dateTime.ToTimestamp().GetArrangedTimestamp(order, miningInterval),
//                    PromisedTinyBlocks = minerInRound.PromisedTinyBlocks,
//                    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.PublicKey] = new MinerInRound
//                {
//                    PublicKey = minersNotMinedCurrentRound[i].PublicKey,
//                    Order = order,
//                    ExpectedMiningTime = dateTime.ToTimestamp().GetArrangedTimestamp(order, miningInterval),
//                    PromisedTinyBlocks = minerInRound.PromisedTinyBlocks,
//                    ProducedBlocks = minerInRound.ProducedBlocks,
//                    MissedTimeSlots = minerInRound.MissedTimeSlots + 1
//                };
//            }
//
//            // Calculate extra block producer order and set the producer.
//            var extraBlockProducerOrder = currentRound.CalculateNextExtraBlockProducerOrder();
//            var expectedExtraBlockProducer =
//                nextRound.RealTimeMinersInformation.Values.FirstOrDefault(m => m.Order == extraBlockProducerOrder);
//            if (expectedExtraBlockProducer == null)
//            {
//                nextRound.RealTimeMinersInformation.Values.First().IsExtraBlockProducer = true;
//            }
//            else
//            {
//                expectedExtraBlockProducer.IsExtraBlockProducer = true;
//            }
//
//            return true;
//        }
//
//        private static int CalculateNextExtraBlockProducerOrder(this Round round)
//        {
//            var firstPlaceInfo = round.GetFirstPlaceMinerInformation();
//            if (firstPlaceInfo == null)
//            {
//                // If no miner produce block during this round, just appoint the first miner to be the extra block producer of next round.
//                return 1;
//            }
//
//            var signature = firstPlaceInfo.Signature;
//            var sigNum = BitConverter.ToInt64(
//                BitConverter.IsLittleEndian ? signature.Value.Reverse().ToArray() : signature.Value.ToArray(), 0);
//            var blockProducerCount = round.RealTimeMinersInformation.Count;
//            var order = GetAbsModulus(sigNum, blockProducerCount) + 1;
//            return order;
//        }
//
//        /// <summary>
//        /// Get the first valid (mined) miner's information, which means this miner's signature shouldn't be empty.
//        /// </summary>
//        /// <param name="round"></param>
//        /// <returns></returns>
//        public static MinerInRound GetFirstPlaceMinerInformation(this Round round)
//        {
//            return round.RealTimeMinersInformation.Values.OrderBy(m => m.Order)
//                .FirstOrDefault(m => m.Signature != null);
//        }
//
//        public static Hash CalculateSignature(this Round previousRound, Hash inValue)
//        {
//            // Check the signatures
//            foreach (var minerInRound in previousRound.RealTimeMinersInformation)
//            {
//                if (minerInRound.Value.Signature == null)
//                {
//                    minerInRound.Value.Signature = Hash.FromString(minerInRound.Key);
//                }
//            }
//
//            return Hash.FromTwoHashes(inValue,
//                previousRound.RealTimeMinersInformation.Values.Aggregate(Hash.Empty,
//                    (current, minerInRound) => Hash.FromTwoHashes(current, minerInRound.Signature)));
//        }
//
//        public static Int64Value ToInt64Value(this long value)
//        {
//            return new Int64Value {Value = value};
//        }
//
//        public static StringValue ToStringValue(this string value)
//        {
//            return new StringValue {Value = value};
//        }
//
//        /// <summary>
//        /// Include both min and max value.
//        /// </summary>
//        /// <param name="value"></param>
//        /// <param name="min"></param>
//        /// <param name="max"></param>
//        /// <returns></returns>
//        public static bool InRange(this int value, int min, int max)
//        {
//            return value >= min && value <= max;
//        }
//
        public static Round GenerateFirstRoundOfNewTerm(this Miners miners, int miningInterval,
                                                        DateTime currentBlockTime, long currentRoundNumber = 0, long currentTermNumber = 0)
        {
            var dict = new Dictionary <string, int>();

            foreach (var miner in miners.PublicKeys)
            {
                dict.Add(miner, miner[0]);
            }

            var sortedMiners =
                (from obj in dict
                 orderby obj.Value descending
                 select obj.Key).ToList();

            var round = new 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.PublicKey          = sortedMiners[i];
                minerInRound.Order              = i + 1;
                minerInRound.ExpectedMiningTime =
                    currentBlockTime.AddMilliseconds((i * miningInterval) + miningInterval).ToTimestamp();
                minerInRound.PromisedTinyBlocks = 1;
                // 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);
        }