private async Task CheckCommitAsync(ViewChangeCommitMessage vcm)
        {
            if (!commitMsgs.ContainsKey(vcm.From))
            {
                var cmt = new VCCommitWithTime(vcm);
                commitMsgs.AddOrUpdate(vcm.From, cmt, (key, oldValue) => cmt);

                await CheckAllStatsAsync();
            }
        }
 public VCCommitWithTime(ViewChangeCommitMessage Message)
 {
     msg = Message;
 }
        private async Task CheckAllStatsAsync()
        {
            if (!IsViewChanging)
            {
                RemoveOutDatedMsgs();

                if (reqMsgs.Count > _context.Board.AllVoters.Count - LyraGlobal.GetMajority(_context.Board.AllVoters.Count))
                {
                    var sb = new StringBuilder();
                    foreach (var msg in reqMsgs)
                    {
                        sb.Append($"{msg.Key.Shorten()}, ");
                    }

                    _log.LogInformation($"too many view change request, {sb.ToString()}. force into view change mode");

                    // too many view change request. force into view change mode
                    await _context.GotViewChangeRequestAsync(ViewId, reqMsgs.Count);
                }

                return;
            }

            // request
            if (!replySent && reqMsgs.Count >= LyraGlobal.GetMajority(_context.Board.AllVoters.Count))
            {
                _log.LogInformation($"CheckAllStats VID: {ViewId} Time: {TimeStarted} Req: {reqMsgs.Count} Reply: {replyMsgs.Count} Commit: {commitMsgs.Count} Votes {commitMsgs.Count}/{LyraGlobal.GetMajority(_context.Board.AllVoters.Count)}/{_context.Board.AllVoters.Count} Replyed: {replySent} Commited: {commitSent}");

                if (string.IsNullOrEmpty(nextLeader))
                {
                    CalculateLeaderCandidate();
                }

                var reply = new ViewChangeReplyMessage
                {
                    From      = _sys.PosWallet.AccountId,
                    ViewID    = ViewId,
                    Result    = Blocks.APIResultCodes.Success,
                    Candidate = nextLeader
                };

                _context.Send2P2pNetwork(reply);

                replySent = true;
                await CheckReplyAsync(reply);
            }

            if (!commitSent)
            {
                if (replyMsgs.Count >= LyraGlobal.GetMajority(_context.Board.AllVoters.Count))
                {
                    _log.LogInformation($"CheckAllStats VID: {ViewId} Time: {TimeStarted} Req: {reqMsgs.Count} Reply: {replyMsgs.Count} Commit: {commitMsgs.Count} Votes {commitMsgs.Count}/{LyraGlobal.GetMajority(_context.Board.AllVoters.Count)}/{_context.Board.AllVoters.Count} Replyed: {replySent} Commited: {commitSent}");

                    // reply
                    // only if we have enough reply
                    var decQr = from rep in replyMsgs.Values
                                where rep.msg.Result == Blocks.APIResultCodes.Success
                                group rep by rep.msg.Candidate into g
                                select new { Candidate = g.Key, Count = g.Count() };

                    var decisions = decQr.OrderByDescending(x => x.Count).ToList();

                    var candidateQR = decisions.FirstOrDefault();

                    var sb = new StringBuilder();
                    sb.AppendLine($"Decisions for View ID: {ViewId}");
                    foreach (var x in decisions)
                    {
                        sb.AppendLine($"\t{x.Candidate.Shorten()}: {x.Count}");
                    }
                    _log.LogInformation(sb.ToString());

                    if (candidateQR?.Count >= LyraGlobal.GetMajority(_context.Board.AllVoters.Count))
                    {
                        var commit = new ViewChangeCommitMessage
                        {
                            From      = _sys.PosWallet.AccountId,
                            ViewID    = ViewId,
                            Candidate = candidateQR.Candidate,
                            Consensus = ConsensusResult.Yea
                        };

                        _context.Send2P2pNetwork(commit);
                        commitSent = true;
                        await CheckCommitAsync(commit);
                    }
                    else
                    {
                        //_log.LogInformation($"CheckAllStats, By ReplyMsgs, not commit, top candidate {candidateQR?.Candidate.Shorten()} has {candidateQR?.Count} votes");
                    }
                }
            }

            if (!selectedSuccess)
            {
                // commit
                var q = from rep in commitMsgs.Values
                        group rep by rep.msg.Candidate into g
                        select new { Candidate = g.Key, Count = g.Count() };

                var candidate = q.OrderByDescending(x => x.Count).FirstOrDefault();
                if (candidate != null)
                {
                    if (nextLeader != candidate.Candidate)
                    {
                        _log.LogWarning($"Next Leader {nextLeader} not {candidate.Candidate}");
                    }
                    nextLeader = candidate.Candidate;
                    if (candidate?.Count >= LyraGlobal.GetMajority(_context.Board.AllVoters.Count))
                    {
                        _log.LogInformation($"CheckAllStats, By CommitMsgs, leader selected {candidate.Candidate} with {candidate.Count} votes.");

                        selectedSuccess = true;
                        _leaderSelected(this, ViewId, candidate.Candidate, candidate.Count, _context.Board.AllVoters);
                    }
                }
            }
        }