/// <summary> /// two ways to begin view changing: either one third of all voters requested, or local requested. /// /// </summary> /// <returns></returns> internal async Task BeginChangeViewAsync(ViewChangeReason reason) { _log.LogInformation($"BeginChangeViewAsync: VID: {ViewId} 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}"); _reason = reason; var lastSb = await _sys.Storage.GetLastServiceBlockAsync(); if (lastSb == null) { // genesis? _log.LogCritical($"BeginChangeViewAsync has null service block. should not happend. error."); return; } _isViewChanging = true; ShiftView(lastSb.Height + 1); selectedSuccess = false; _log.LogInformation($"View change for ViewId {ViewId} begin at {TimeStarted}"); CalculateLeaderCandidate(); var lastCons = await _sys.Storage.GetLastConsolidationBlockAsync(); var req = new ViewChangeRequestMessage { From = _sys.PosWallet.AccountId, ViewID = ViewId, prevViewID = lastSb.Height, requestSignature = Signatures.GetSignature(_sys.PosWallet.PrivateKey, $"{lastSb.Hash}|{lastCons.Hash}", _sys.PosWallet.AccountId), }; _context.Send2P2pNetwork(req); await CheckRequestAsync(req); }
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); } } } }