Esempio n. 1
0
        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);
        }
Esempio n. 2
0
 // A possible attack can happen if the last node to commit is malicious and either sends change view after his
 // commit to stall nodes in a higher view, or if he refuses to send recovery messages. In addition, if a node
 // asking change views loses network or crashes and comes back when nodes are committed in more than one higher
 // numbered view, it is possible for the node accepting recovery and commit in any of the higher views, thus
 // potentially splitting nodes among views and stalling the network.
 public static bool MoreThanFNodesCommitted(this IConsensusContext context) => context.CommitPayloads.Count(p => p != null) > context.F();
Esempio n. 3
0
 public static int M(this IConsensusContext context) => context.Validators.Length - context.F();
Esempio n. 4
0
 // A possible attack can happen if the last node to commit is malicious and either sends change view after his
 // commit to stall nodes in a higher view, or if he refuses to send recovery messages. In addition, if a node
 // asking change views loses network or crashes and comes back when nodes are committed in more than one higher
 // numbered view, it is possible for the node accepting recovery to commit in any of the higher views, thus
 // potentially splitting nodes among views and stalling the network.
 public static bool MoreThanFNodesCommittedOrLost(this IConsensusContext context) => (context.CountCommitted() + context.CountFailed()) > context.F();