private void OnRecoveryMessageReceived(ConsensusPayload payload, RecoveryMessage message) { if (message.ViewNumber < context.ViewNumber) { return; } Log($"{nameof(OnRecoveryMessageReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); isRecovering = true; try { if (message.ViewNumber > context.ViewNumber) { if (context.CommitSent()) { return; } ConsensusPayload[] changeViewPayloads = message.GetChangeViewPayloads(context, payload); foreach (ConsensusPayload changeViewPayload in changeViewPayloads) { ReverifyAndProcessPayload(changeViewPayload); } } if (message.ViewNumber != context.ViewNumber) { return; } if (!context.CommitSent()) { if (!context.RequestSentOrReceived()) { ConsensusPayload prepareRequestPayload = message.GetPrepareRequestPayload(context, payload); if (prepareRequestPayload != null) { ReverifyAndProcessPayload(prepareRequestPayload); } else if (context.IsPrimary()) { SendPrepareRequest(); } } ConsensusPayload[] prepareResponsePayloads = message.GetPrepareResponsePayloads(context, payload); foreach (ConsensusPayload prepareResponsePayload in prepareResponsePayloads) { ReverifyAndProcessPayload(prepareResponsePayload); } } ConsensusPayload[] commitPayloads = message.GetCommitPayloadsFromRecoveryMessage(context, payload); foreach (ConsensusPayload commitPayload in commitPayloads) { ReverifyAndProcessPayload(commitPayload); } } finally { isRecovering = false; } }
private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) { // We keep track of the payload hashes received in this block, and don't respond with recovery // in response to the same payload that we already responded to previously. // ChangeView messages include a Timestamp when the change view is sent, thus if a node restarts // and issues a change view for the same view, it will have a different hash and will correctly respond // again; however replay attacks of the ChangeView message from arbitrary nodes will not trigger an // additonal recovery message response. if (!knownHashes.Add(payload.Hash)) { return; } if (message.NewViewNumber <= context.ViewNumber) { bool shouldSendRecovery = false; // Limit recovery to sending from `f` nodes when the request is from a lower view number. int allowedRecoveryNodeCount = context.F(); for (int i = 0; i < allowedRecoveryNodeCount; i++) { var eligibleResponders = context.Validators.Length - 1; var chosenIndex = (payload.ValidatorIndex + i + message.NewViewNumber) % eligibleResponders; if (chosenIndex >= payload.ValidatorIndex) { chosenIndex++; } if (chosenIndex != context.MyIndex) { continue; } shouldSendRecovery = true; break; } if (!shouldSendRecovery) { return; } Log($"send recovery from view: {message.ViewNumber} to view: {context.ViewNumber}"); localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryMessage() }); } if (context.CommitSent()) { return; } var expectedView = GetLastExpectedView(payload.ValidatorIndex); if (message.NewViewNumber <= expectedView) { return; } Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber}"); context.ChangeViewPayloads[payload.ValidatorIndex] = payload; CheckExpectedView(message.NewViewNumber); }
private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) { if (message.NewViewNumber <= context.ViewNumber) { OnRecoveryRequestReceived(payload); } if (context.CommitSent()) { return; } var expectedView = context.ChangeViewPayloads[payload.ValidatorIndex]?.GetDeserializedMessage <ChangeView>().NewViewNumber ?? (byte)0; if (message.NewViewNumber <= expectedView) { return; } Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber}"); context.ChangeViewPayloads[payload.ValidatorIndex] = payload; CheckExpectedView(message.NewViewNumber); }