IBFT ensure non-validator does not partake in consensus (#627)

Nodes which are not validators in a network should not inject
IBFT messages to the consensus round, and should not gossip
received messages.

I.e. all events should ensure that they are only handled if
the node is a validator at the current height.
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
tmohay 6 years ago committed by GitHub
parent a1ef038865
commit cde9425cfb
  1. 46
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/BlockHeightManager.java
  2. 24
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManager.java
  3. 14
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManagerFactory.java
  4. 4
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftController.java
  5. 4
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java
  6. 66
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/NoOpBlockHeightManager.java
  7. 4
      consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftControllerTest.java

@ -0,0 +1,46 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.statemachine;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.ibftevent.RoundExpiry;
import tech.pegasys.pantheon.consensus.ibft.payload.CommitPayload;
import tech.pegasys.pantheon.consensus.ibft.payload.NewRoundPayload;
import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload;
import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload;
import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload;
import tech.pegasys.pantheon.consensus.ibft.payload.SignedData;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
public interface BlockHeightManager {
void start();
void handleBlockTimerExpiry(ConsensusRoundIdentifier roundIdentifier);
void roundExpired(RoundExpiry expire);
void handleProposalPayload(SignedData<ProposalPayload> signedPayload);
void handlePreparePayload(SignedData<PreparePayload> signedPayload);
void handleCommitPayload(SignedData<CommitPayload> payload);
void handleRoundChangePayload(SignedData<RoundChangePayload> signedPayload);
void handleNewRoundPayload(SignedData<NewRoundPayload> signedPayload);
long getChainHeight();
BlockHeader getParentBlockHeader();
}

@ -53,13 +53,7 @@ import org.apache.logging.log4j.Logger;
* and sends a Proposal message. If the round times out prior to importing a block, this class is * and sends a Proposal message. If the round times out prior to importing a block, this class is
* responsible for creating a RoundChange message and transmitting it. * responsible for creating a RoundChange message and transmitting it.
*/ */
public class IbftBlockHeightManager { public class IbftBlockHeightManager implements BlockHeightManager {
protected enum MessageAge {
PRIOR_ROUND,
CURRENT_ROUND,
FUTURE_ROUND
}
private static final Logger LOG = LogManager.getLogger(); private static final Logger LOG = LogManager.getLogger();
@ -107,6 +101,7 @@ public class IbftBlockHeightManager {
messageValidatorFactory.createMessageValidator(roundIdentifier, parentHeader)); messageValidatorFactory.createMessageValidator(roundIdentifier, parentHeader));
} }
@Override
public void start() { public void start() {
startNewRound(0); startNewRound(0);
if (finalState.isLocalNodeProposerForRound(currentRound.getRoundIdentifier())) { if (finalState.isLocalNodeProposerForRound(currentRound.getRoundIdentifier())) {
@ -114,6 +109,7 @@ public class IbftBlockHeightManager {
} }
} }
@Override
public void handleBlockTimerExpiry(final ConsensusRoundIdentifier roundIdentifier) { public void handleBlockTimerExpiry(final ConsensusRoundIdentifier roundIdentifier) {
if (roundIdentifier.equals(currentRound.getRoundIdentifier())) { if (roundIdentifier.equals(currentRound.getRoundIdentifier())) {
currentRound.createAndSendProposalMessage(clock.millis() / 1000); currentRound.createAndSendProposalMessage(clock.millis() / 1000);
@ -125,6 +121,7 @@ public class IbftBlockHeightManager {
} }
} }
@Override
public void roundExpired(final RoundExpiry expire) { public void roundExpired(final RoundExpiry expire) {
if (!expire.getView().equals(currentRound.getRoundIdentifier())) { if (!expire.getView().equals(currentRound.getRoundIdentifier())) {
LOG.info("Ignoring Round timer expired which does not match current round."); LOG.info("Ignoring Round timer expired which does not match current round.");
@ -151,18 +148,21 @@ public class IbftBlockHeightManager {
handleRoundChangePayload(localRoundChange); handleRoundChangePayload(localRoundChange);
} }
@Override
public void handleProposalPayload(final SignedData<ProposalPayload> signedPayload) { public void handleProposalPayload(final SignedData<ProposalPayload> signedPayload) {
LOG.info("Received a Proposal Payload."); LOG.info("Received a Proposal Payload.");
actionOrBufferMessage( actionOrBufferMessage(
signedPayload, currentRound::handleProposalMessage, RoundState::setProposedBlock); signedPayload, currentRound::handleProposalMessage, RoundState::setProposedBlock);
} }
@Override
public void handlePreparePayload(final SignedData<PreparePayload> signedPayload) { public void handlePreparePayload(final SignedData<PreparePayload> signedPayload) {
LOG.info("Received a prepare Payload."); LOG.info("Received a prepare Payload.");
actionOrBufferMessage( actionOrBufferMessage(
signedPayload, currentRound::handlePrepareMessage, RoundState::addPrepareMessage); signedPayload, currentRound::handlePrepareMessage, RoundState::addPrepareMessage);
} }
@Override
public void handleCommitPayload(final SignedData<CommitPayload> payload) { public void handleCommitPayload(final SignedData<CommitPayload> payload) {
LOG.info("Received a commit Payload."); LOG.info("Received a commit Payload.");
actionOrBufferMessage(payload, currentRound::handleCommitMessage, RoundState::addCommitMessage); actionOrBufferMessage(payload, currentRound::handleCommitMessage, RoundState::addCommitMessage);
@ -185,6 +185,7 @@ public class IbftBlockHeightManager {
} }
} }
@Override
public void handleRoundChangePayload(final SignedData<RoundChangePayload> signedPayload) { public void handleRoundChangePayload(final SignedData<RoundChangePayload> signedPayload) {
final ConsensusRoundIdentifier targetRound = signedPayload.getPayload().getRoundIdentifier(); final ConsensusRoundIdentifier targetRound = signedPayload.getPayload().getRoundIdentifier();
LOG.info("Received a RoundChange Payload for {}", targetRound.toString()); LOG.info("Received a RoundChange Payload for {}", targetRound.toString());
@ -223,6 +224,7 @@ public class IbftBlockHeightManager {
roundTimer.startTimer(currentRound.getRoundIdentifier()); roundTimer.startTimer(currentRound.getRoundIdentifier());
} }
@Override
public void handleNewRoundPayload(final SignedData<NewRoundPayload> signedPayload) { public void handleNewRoundPayload(final SignedData<NewRoundPayload> signedPayload) {
final NewRoundPayload payload = signedPayload.getPayload(); final NewRoundPayload payload = signedPayload.getPayload();
final MessageAge messageAge = determineAgeOfPayload(payload); final MessageAge messageAge = determineAgeOfPayload(payload);
@ -241,10 +243,12 @@ public class IbftBlockHeightManager {
} }
} }
@Override
public long getChainHeight() { public long getChainHeight() {
return currentRound.getRoundIdentifier().getSequenceNumber(); return currentRound.getRoundIdentifier().getSequenceNumber();
} }
@Override
public BlockHeader getParentBlockHeader() { public BlockHeader getParentBlockHeader() {
return parentHeader; return parentHeader;
} }
@ -259,4 +263,10 @@ public class IbftBlockHeightManager {
} }
return PRIOR_ROUND; return PRIOR_ROUND;
} }
public enum MessageAge {
PRIOR_ROUND,
CURRENT_ROUND,
FUTURE_ROUND
}
} }

@ -31,7 +31,19 @@ public class IbftBlockHeightManagerFactory {
this.messageValidatorFactory = messageValidatorFactory; this.messageValidatorFactory = messageValidatorFactory;
} }
public IbftBlockHeightManager create(final BlockHeader parentHeader) { public BlockHeightManager create(final BlockHeader parentHeader) {
if (finalState.isLocalNodeValidator()) {
return createFullBlockHeightManager(parentHeader);
} else {
return createNoOpBlockHeightManager(parentHeader);
}
}
private BlockHeightManager createNoOpBlockHeightManager(final BlockHeader parentHeader) {
return new NoOpBlockHeightManager(parentHeader);
}
private BlockHeightManager createFullBlockHeightManager(final BlockHeader parentHeader) {
return new IbftBlockHeightManager( return new IbftBlockHeightManager(
parentHeader, parentHeader,
finalState, finalState,

@ -50,7 +50,7 @@ public class IbftController {
private final IbftFinalState ibftFinalState; private final IbftFinalState ibftFinalState;
private final IbftBlockHeightManagerFactory ibftBlockHeightManagerFactory; private final IbftBlockHeightManagerFactory ibftBlockHeightManagerFactory;
private final Map<Long, List<Message>> futureMessages; private final Map<Long, List<Message>> futureMessages;
private IbftBlockHeightManager currentHeightManager; private BlockHeightManager currentHeightManager;
private final IbftGossip gossiper; private final IbftGossip gossiper;
public IbftController( public IbftController(
@ -188,7 +188,7 @@ public class IbftController {
private boolean processMessage(final SignedData<? extends Payload> msg, final Message rawMsg) { private boolean processMessage(final SignedData<? extends Payload> msg, final Message rawMsg) {
final ConsensusRoundIdentifier msgRoundIdentifier = msg.getPayload().getRoundIdentifier(); final ConsensusRoundIdentifier msgRoundIdentifier = msg.getPayload().getRoundIdentifier();
if (isMsgForCurrentHeight(msgRoundIdentifier)) { if (isMsgForCurrentHeight(msgRoundIdentifier)) {
return isMsgFromKnownValidator(msg); return isMsgFromKnownValidator(msg) && ibftFinalState.isLocalNodeValidator();
} else if (isMsgForFutureChainHeight(msgRoundIdentifier)) { } else if (isMsgForFutureChainHeight(msgRoundIdentifier)) {
addMessageToFutureMessageBuffer(msgRoundIdentifier.getSequenceNumber(), rawMsg); addMessageToFutureMessageBuffer(msgRoundIdentifier.getSequenceNumber(), rawMsg);
} else { } else {

@ -87,6 +87,10 @@ public class IbftFinalState {
return getProposerForRound(roundIdentifier).equals(localAddress); return getProposerForRound(roundIdentifier).equals(localAddress);
} }
public boolean isLocalNodeValidator() {
return getValidators().contains(localAddress);
}
public ValidatorMulticaster getValidatorMulticaster() { public ValidatorMulticaster getValidatorMulticaster() {
return validatorMulticaster; return validatorMulticaster;
} }

@ -0,0 +1,66 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.consensus.ibft.statemachine;
import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier;
import tech.pegasys.pantheon.consensus.ibft.ibftevent.RoundExpiry;
import tech.pegasys.pantheon.consensus.ibft.payload.CommitPayload;
import tech.pegasys.pantheon.consensus.ibft.payload.NewRoundPayload;
import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload;
import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload;
import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload;
import tech.pegasys.pantheon.consensus.ibft.payload.SignedData;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
public class NoOpBlockHeightManager implements BlockHeightManager {
private final BlockHeader parentHeader;
public NoOpBlockHeightManager(final BlockHeader parentHeader) {
this.parentHeader = parentHeader;
}
@Override
public void start() {}
@Override
public void handleBlockTimerExpiry(final ConsensusRoundIdentifier roundIdentifier) {}
@Override
public void roundExpired(final RoundExpiry expire) {}
@Override
public void handleProposalPayload(final SignedData<ProposalPayload> signedPayload) {}
@Override
public void handlePreparePayload(final SignedData<PreparePayload> signedPayload) {}
@Override
public void handleCommitPayload(final SignedData<CommitPayload> payload) {}
@Override
public void handleRoundChangePayload(final SignedData<RoundChangePayload> signedPayload) {}
@Override
public void handleNewRoundPayload(final SignedData<NewRoundPayload> signedPayload) {}
@Override
public long getChainHeight() {
return parentHeader.getNumber() + 1;
}
@Override
public BlockHeader getParentBlockHeader() {
return parentHeader;
}
}

@ -66,7 +66,7 @@ public class IbftControllerTest {
@Mock private IbftBlockHeightManagerFactory blockHeightManagerFactory; @Mock private IbftBlockHeightManagerFactory blockHeightManagerFactory;
@Mock private BlockHeader chainHeadBlockHeader; @Mock private BlockHeader chainHeadBlockHeader;
@Mock private BlockHeader nextBlock; @Mock private BlockHeader nextBlock;
@Mock private IbftBlockHeightManager blockHeightManager; @Mock private BlockHeightManager blockHeightManager;
@Mock private SignedData<ProposalPayload> signedProposal; @Mock private SignedData<ProposalPayload> signedProposal;
private Message proposalMessage; private Message proposalMessage;
@ -116,6 +116,8 @@ public class IbftControllerTest {
when(blockHeightManager.getParentBlockHeader()).thenReturn(chainHeadBlockHeader); when(blockHeightManager.getParentBlockHeader()).thenReturn(chainHeadBlockHeader);
when(nextBlock.getNumber()).thenReturn(2L); when(nextBlock.getNumber()).thenReturn(2L);
when(ibftFinalState.isLocalNodeValidator()).thenReturn(true);
} }
@Test @Test

Loading…
Cancel
Save