From 22498da50c8498a5721dbd9b4a571268ad661ebf Mon Sep 17 00:00:00 2001 From: tmohay <37158202+rain-on@users.noreply.github.com> Date: Fri, 30 Nov 2018 09:54:24 +1100 Subject: [PATCH] Ibft Preprepare to validate round matches block (#329) --- .../ibft/validation/MessageValidator.java | 17 +++++++ .../ibft/validation/MessageValidatorTest.java | 46 +++++++++++++------ 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidator.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidator.java index 864574bf68..5aa66f52f3 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidator.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidator.java @@ -16,6 +16,7 @@ import static tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode.FULL; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.IbftContext; +import tech.pegasys.pantheon.consensus.ibft.IbftExtraData; import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.AbstractIbftUnsignedInRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.IbftSignedMessageData; import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.IbftUnsignedCommitMessageData; @@ -75,6 +76,10 @@ public class MessageValidator { return false; } + if (!validateBlocKMatchesPrepareMessageRound(msg.getUnsignedMessageData())) { + return false; + } + preprepareMessage = Optional.of(msg); return true; } @@ -201,4 +206,16 @@ public class MessageValidator { return right.getBlock().getHash().equals(left.getBlock().getHash()) && right.getRoundIdentifier().equals(left.getRoundIdentifier()); } + + private boolean validateBlocKMatchesPrepareMessageRound( + final IbftUnsignedPrePrepareMessageData msgData) { + final ConsensusRoundIdentifier msgRound = msgData.getRoundIdentifier(); + final IbftExtraData extraData = + IbftExtraData.decode(msgData.getBlock().getHeader().getExtraData()); + if (extraData.getRound() != msgRound.getRoundNumber()) { + LOG.info("Invalid Preprepare message, round number in block does not match that in message."); + return false; + } + return true; + } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorTest.java index c90e8232f7..79bbbf1113 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorTest.java @@ -19,6 +19,7 @@ import static org.mockito.Mockito.when; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.IbftContext; +import tech.pegasys.pantheon.consensus.ibft.IbftExtraData; import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.IbftMessageFactory; import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.IbftSignedMessageData; import tech.pegasys.pantheon.consensus.ibft.ibftmessagedata.IbftUnsignedCommitMessageData; @@ -35,8 +36,11 @@ import tech.pegasys.pantheon.ethereum.core.Hash; import tech.pegasys.pantheon.ethereum.core.Util; import tech.pegasys.pantheon.ethereum.db.WorldStateArchive; import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator; +import tech.pegasys.pantheon.util.bytes.BytesValue; +import java.util.Collections; import java.util.List; +import java.util.Optional; import com.google.common.collect.Lists; import org.junit.Before; @@ -84,6 +88,21 @@ public class MessageValidatorTest { parentHeader); when(block.getHash()).thenReturn(Hash.fromHexStringLenient("1")); + when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true); + insertRoundToBlockHeader(0); + } + + private void insertRoundToBlockHeader(final int round) { + final IbftExtraData extraData = + new IbftExtraData( + BytesValue.wrap(new byte[32]), + Collections.emptyList(), + Optional.empty(), + round, + validators); + final BlockHeader header = mock(BlockHeader.class); + when(header.getExtraData()).thenReturn(extraData.encode()); + when(block.getHeader()).thenReturn(header); } @Test @@ -139,17 +158,12 @@ public class MessageValidatorTest { @Test public void receivingPrepareFromNonValidatorFails() { when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true); - final Block block = mock(Block.class); - final BlockHeader header = mock(BlockHeader.class); - when(header.getHash()).thenReturn(Hash.fromHexStringLenient("1")); // arbitrary hash value. - when(block.getHeader()).thenReturn(header); - final IbftSignedMessageData preprepareMsg = proposerMessageFactory.createIbftSignedPrePrepareMessageData(roundIdentifier, block); final IbftSignedMessageData prepareMsg = nonValidatorMessageFactory.createIbftSignedPrepareMessageData( - roundIdentifier, header.getHash()); + roundIdentifier, block.getHash()); assertThat(validator.addPreprepareMessage(preprepareMsg)).isTrue(); assertThat(validator.validatePrepareMessage(prepareMsg)).isFalse(); @@ -180,18 +194,12 @@ public class MessageValidatorTest { @Test public void receivingPrepareNonProposerValidatorWithCorrectRoundIsSuccessful() { when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true); - final Block block = mock(Block.class); - final BlockHeader header = mock(BlockHeader.class); - final Hash blockHash = Hash.fromHexStringLenient("1"); - when(header.getHash()).thenReturn(blockHash); // arbitrary hash value. - when(block.getHeader()).thenReturn(header); - when(block.getHash()).thenReturn(blockHash); final IbftSignedMessageData preprepareMsg = proposerMessageFactory.createIbftSignedPrePrepareMessageData(roundIdentifier, block); final IbftSignedMessageData prepareMsg = validatorMessageFactory.createIbftSignedPrepareMessageData( - roundIdentifier, header.getHash()); + roundIdentifier, block.getHash()); assertThat(validator.addPreprepareMessage(preprepareMsg)).isTrue(); assertThat(validator.validatePrepareMessage(prepareMsg)).isTrue(); @@ -247,7 +255,6 @@ public class MessageValidatorTest { @Test public void subsequentPreprepareHasDifferentContentFails() { - when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true); final IbftSignedMessageData preprepareMsg = proposerMessageFactory.createIbftSignedPrePrepareMessageData(roundIdentifier, block); @@ -271,4 +278,15 @@ public class MessageValidatorTest { proposerMessageFactory.createIbftSignedPrePrepareMessageData(roundIdentifier, block); assertThat(validator.addPreprepareMessage(secondPreprepareMsg)).isTrue(); } + + @Test + public void blockRoundMisMatchWithMessageRoundFails() { + insertRoundToBlockHeader(roundIdentifier.getRoundNumber() + 1); + + when(headerValidator.validateHeader(any(), any(), any(), any())).thenReturn(true); + final IbftSignedMessageData preprepareMsg = + proposerMessageFactory.createIbftSignedPrePrepareMessageData(roundIdentifier, block); + + assertThat(validator.addPreprepareMessage(preprepareMsg)).isFalse(); + } }