From e279f1f726e8b04d81971f3b9c04f283e064e9e7 Mon Sep 17 00:00:00 2001 From: Trent Mohay <37158202+rain-on@users.noreply.github.com> Date: Wed, 20 Feb 2019 15:58:00 +1100 Subject: [PATCH] Ibft2: Replace NewRound with extended Proposal (#872) Signed-off-by: Adrian Sutton --- .../ibft/support/IntegrationTestHelpers.java | 6 +- .../ibft/support/RoundSpecificPeers.java | 4 - .../consensus/ibft/support/ValidatorPeer.java | 27 +-- .../consensus/ibft/tests/FutureRoundTest.java | 13 +- .../consensus/ibft/tests/GossipTest.java | 15 +- .../ibft/tests/LocalNodeIsProposerTest.java | 4 +- ...t.java => ReceivedFutureProposalTest.java} | 63 +++--- .../consensus/ibft/tests/RoundChangeTest.java | 90 ++++----- .../ibft/tests/SpuriousBehaviourTest.java | 2 +- .../pantheon/consensus/ibft/IbftGossip.java | 4 - .../consensus/ibft/messagedata/IbftV2.java | 3 +- .../ibft/messagedata/NewRoundMessageData.java | 44 ----- .../ibft/messagewrappers/NewRound.java | 66 ------- .../ibft/messagewrappers/Proposal.java | 37 +++- .../ibft/network/IbftMessageTransmitter.java | 27 +-- .../ibft/payload/MessageFactory.java | 19 +- .../ibft/payload/NewRoundPayload.java | 145 -------------- .../consensus/ibft/payload/SignedData.java | 11 +- .../ibft/protocol/IbftSubProtocol.java | 3 - .../ibft/statemachine/BlockHeightManager.java | 3 - .../statemachine/IbftBlockHeightManager.java | 47 ++--- .../ibft/statemachine/IbftController.java | 8 - .../ibft/statemachine/IbftRound.java | 70 +++---- .../statemachine/NoOpBlockHeightManager.java | 4 - .../ibft/statemachine/RoundChangeManager.java | 2 +- .../FutureRoundProposalMessageValidator.java | 53 +++++ .../ibft/validation/MessageValidator.java | 62 +++++- .../validation/MessageValidatorFactory.java | 30 +-- .../validation/NewRoundMessageValidator.java | 84 -------- .../validation/NewRoundPayloadValidator.java | 84 -------- .../RoundChangeCertificateValidator.java | 4 +- .../consensus/ibft/IbftGossipTest.java | 7 - .../consensus/ibft/IbftHelpersTest.java | 5 +- .../pantheon/consensus/ibft/TestHelpers.java | 17 +- .../ibft/messagedata/NewRoundMessageTest.java | 69 ------- .../ibft/payload/NewRoundPayloadTest.java | 98 ---------- .../ibft/protocol/IbftSubProtocolTest.java | 5 +- .../IbftBlockHeightManagerTest.java | 60 ++++-- .../ibft/statemachine/IbftControllerTest.java | 62 +----- .../ibft/statemachine/IbftRoundTest.java | 41 ++-- .../RoundChangeArtifactsTest.java | 2 +- .../statemachine/RoundChangeManagerTest.java | 2 +- .../ibft/statemachine/RoundStateTest.java | 16 +- ...tureRoundProposalMessageValidatorTest.java | 89 +++++++++ .../ibft/validation/MessageValidatorTest.java | 79 +++++++- .../NewRoundMessageValidatorTest.java | 182 ----------------- .../NewRoundSignedDataValidatorTest.java | 185 ------------------ ...ProposalBlockConsistencyValidatorTest.java | 10 +- .../RoundChangeCertificateValidatorTest.java | 12 +- .../RoundChangeSignedDataValidatorTest.java | 12 +- .../validation/SignedDataValidatorTest.java | 37 ++-- 51 files changed, 612 insertions(+), 1412 deletions(-) rename consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/{ReceivedNewRoundTest.java => ReceivedFutureProposalTest.java} (76%) delete mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagedata/NewRoundMessageData.java delete mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagewrappers/NewRound.java delete mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/NewRoundPayload.java create mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/FutureRoundProposalMessageValidator.java delete mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundMessageValidator.java delete mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundPayloadValidator.java delete mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/messagedata/NewRoundMessageTest.java delete mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/payload/NewRoundPayloadTest.java create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/FutureRoundProposalMessageValidatorTest.java delete mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundMessageValidatorTest.java delete mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundSignedDataValidatorTest.java diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/IntegrationTestHelpers.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/IntegrationTestHelpers.java index 336df86b08..63c25af914 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/IntegrationTestHelpers.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/IntegrationTestHelpers.java @@ -25,6 +25,7 @@ import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.crypto.SECP256K1.Signature; import tech.pegasys.pantheon.ethereum.core.Block; +import java.util.Optional; import java.util.stream.Collectors; public class IntegrationTestHelpers { @@ -49,7 +50,10 @@ public class IntegrationTestHelpers { final RoundSpecificPeers peers = context.roundSpecificPeers(preparedRound); return new PreparedRoundArtifacts( - peers.getProposer().getMessageFactory().createProposal(preparedRound, block), + peers + .getProposer() + .getMessageFactory() + .createProposal(preparedRound, block, Optional.empty()), peers.createSignedPreparePayloadOfNonProposing(preparedRound, block.getHash()).stream() .map(Prepare::new) .collect(Collectors.toList())); diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java index f904e72956..671160d98d 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java @@ -19,7 +19,6 @@ import static org.assertj.core.api.Fail.fail; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.IbftV2; -import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; @@ -197,9 +196,6 @@ public class RoundSpecificPeers { case IbftV2.COMMIT: actualSignedPayload = CommitMessageData.fromMessageData(actual).decode(); break; - case IbftV2.NEW_ROUND: - actualSignedPayload = NewRoundMessageData.fromMessageData(actual).decode(); - break; case IbftV2.ROUND_CHANGE: actualSignedPayload = RoundChangeMessageData.fromMessageData(actual).decode(); break; diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/ValidatorPeer.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/ValidatorPeer.java index 343edd95b8..03d1e29c8b 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/ValidatorPeer.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/ValidatorPeer.java @@ -16,19 +16,15 @@ import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.EventMultiplexer; import tech.pegasys.pantheon.consensus.ibft.ibftevent.IbftEvents; import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; -import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; -import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload; -import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; import tech.pegasys.pantheon.consensus.ibft.statemachine.PreparedRoundArtifacts; import tech.pegasys.pantheon.crypto.SECP256K1; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; @@ -79,7 +75,7 @@ public class ValidatorPeer { } public Proposal injectProposal(final ConsensusRoundIdentifier rId, final Block block) { - final Proposal payload = messageFactory.createProposal(rId, block); + final Proposal payload = messageFactory.createProposal(rId, block, Optional.empty()); injectMessage(ProposalMessageData.create(payload)); return payload; @@ -108,26 +104,15 @@ public class ValidatorPeer { return payload; } - public NewRound injectNewRound( + public Proposal injectProposalForFutureRound( final ConsensusRoundIdentifier rId, final RoundChangeCertificate roundChangeCertificate, - final Proposal proposal) { - - final NewRound payload = - messageFactory.createNewRound( - rId, roundChangeCertificate, proposal.getSignedPayload(), proposal.getBlock()); - injectMessage(NewRoundMessageData.create(payload)); - return payload; - } - - public NewRound injectEmptyNewRound( - final ConsensusRoundIdentifier targetRoundId, - final List> roundChangePayloads, final Block blockToPropose) { - final Proposal proposal = messageFactory.createProposal(targetRoundId, blockToPropose); - - return injectNewRound(targetRoundId, new RoundChangeCertificate(roundChangePayloads), proposal); + final Proposal payload = + messageFactory.createProposal(rId, blockToPropose, Optional.of(roundChangeCertificate)); + injectMessage(ProposalMessageData.create(payload)); + return payload; } public RoundChange injectRoundChange( diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureRoundTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureRoundTest.java index f1ab08d5c3..bab80ec658 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureRoundTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureRoundTest.java @@ -19,6 +19,7 @@ import tech.pegasys.pantheon.consensus.ibft.IbftHelpers; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; +import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; @@ -93,8 +94,10 @@ public class FutureRoundTest { // and updates blockchain futurePeers .getProposer() - .injectEmptyNewRound( - futureRoundId, futurePeers.createSignedRoundChangePayload(futureRoundId), futureBlock); + .injectProposalForFutureRound( + futureRoundId, + new RoundChangeCertificate(futurePeers.createSignedRoundChangePayload(futureRoundId)), + futureBlock); final Prepare expectedPrepare = localNodeMessageFactory.createPrepare(futureRoundId, futureBlock.getHash()); @@ -136,8 +139,10 @@ public class FutureRoundTest { futurePeers .getProposer() - .injectEmptyNewRound( - futureRoundId, futurePeers.createSignedRoundChangePayload(futureRoundId), futureBlock); + .injectProposalForFutureRound( + futureRoundId, + new RoundChangeCertificate(futurePeers.createSignedRoundChangePayload(futureRoundId)), + futureBlock); final Prepare expectedFuturePrepare = localNodeMessageFactory.createPrepare(futureRoundId, futureBlock.getHash()); diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/GossipTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/GossipTest.java index 9753aba637..fd3dff3149 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/GossipTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/GossipTest.java @@ -20,7 +20,6 @@ import tech.pegasys.pantheon.consensus.ibft.IbftHelpers; import tech.pegasys.pantheon.consensus.ibft.ibftevent.NewChainHead; import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; @@ -93,8 +92,10 @@ public class GossipTest { final RoundChange roundChange = msgFactory.createRoundChange(roundId, Optional.empty()); final RoundChangeCertificate roundChangeCert = new RoundChangeCertificate(singleton(roundChange.getSignedPayload())); - final NewRound newRound = sender.injectNewRound(roundId, roundChangeCert, proposal); - peers.verifyMessagesReceivedNonPropsing(newRound); + + final Proposal nextRoundProposal = + sender.injectProposalForFutureRound(roundId, roundChangeCert, proposal.getBlock()); + peers.verifyMessagesReceivedNonPropsing(nextRoundProposal); peers.verifyNoMessagesReceivedProposer(); sender.injectRoundChange(roundId, Optional.empty()); @@ -118,7 +119,8 @@ public class GossipTest { public void messageWithUnknownValidatorIsNotGossiped() { final KeyPair unknownKeyPair = KeyPair.generate(); final MessageFactory unknownMsgFactory = new MessageFactory(unknownKeyPair); - final Proposal unknownProposal = unknownMsgFactory.createProposal(roundId, block); + final Proposal unknownProposal = + unknownMsgFactory.createProposal(roundId, block, Optional.empty()); sender.injectMessage(ProposalMessageData.create(unknownProposal)); peers.verifyNoMessagesReceived(); @@ -128,7 +130,8 @@ public class GossipTest { public void messageIsNotGossipedToSenderOrCreator() { final ValidatorPeer msgCreator = peers.getFirstNonProposer(); final MessageFactory peerMsgFactory = msgCreator.getMessageFactory(); - final Proposal proposalFromPeer = peerMsgFactory.createProposal(roundId, block); + final Proposal proposalFromPeer = + peerMsgFactory.createProposal(roundId, block, Optional.empty()); sender.injectMessage(ProposalMessageData.create(proposalFromPeer)); @@ -139,7 +142,7 @@ public class GossipTest { @Test public void futureMessageIsNotGossipedImmediately() { ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(2, 0); - msgFactory.createProposal(futureRoundId, block); + msgFactory.createProposal(futureRoundId, block, Optional.empty()); sender.injectProposal(futureRoundId, block); peers.verifyNoMessagesReceived(); diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeIsProposerTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeIsProposerTest.java index 51573d81ac..24bb0d9014 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeIsProposerTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeIsProposerTest.java @@ -28,6 +28,7 @@ import tech.pegasys.pantheon.ethereum.core.Block; import java.time.Clock; import java.time.Instant; import java.time.ZoneId; +import java.util.Optional; import org.junit.Before; import org.junit.Test; @@ -63,7 +64,8 @@ public class LocalNodeIsProposerTest { @Before public void setup() { expectedProposedBlock = context.createBlockForProposalFromChainHead(0, blockTimeStamp); - expectedTxProposal = localNodeMessageFactory.createProposal(roundId, expectedProposedBlock); + expectedTxProposal = + localNodeMessageFactory.createProposal(roundId, expectedProposedBlock, Optional.empty()); expectedTxCommit = new Commit( diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedNewRoundTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedFutureProposalTest.java similarity index 76% rename from consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedNewRoundTest.java rename to consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedFutureProposalTest.java index 0301fe9969..2b43599e1e 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedNewRoundTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedFutureProposalTest.java @@ -17,7 +17,6 @@ import static tech.pegasys.pantheon.consensus.ibft.support.IntegrationTestHelper import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload; @@ -35,8 +34,11 @@ import java.util.List; import org.junit.Before; import org.junit.Test; -/** Ensure the Ibft component responds appropriately when a NewRound message is received. */ -public class ReceivedNewRoundTest { +/** + * Ensure the Ibft component responds appropriately when a future round Proposal message is + * received. + */ +public class ReceivedFutureProposalTest { private final int NETWORK_SIZE = 5; @@ -57,7 +59,7 @@ public class ReceivedNewRoundTest { } @Test - public void newRoundMessageWithEmptyPrepareCertificatesOfferNewBlock() { + public void proposalWithEmptyPrepareCertificatesOfferNewBlock() { final ConsensusRoundIdentifier nextRoundId = new ConsensusRoundIdentifier(1, 1); final Block blockToPropose = context.createBlockForProposalFromChainHead(nextRoundId.getRoundNumber(), 15); @@ -68,10 +70,8 @@ public class ReceivedNewRoundTest { final ValidatorPeer nextProposer = context.roundSpecificPeers(nextRoundId).getProposer(); - nextProposer.injectNewRound( - targetRound, - new RoundChangeCertificate(roundChanges), - nextProposer.getMessageFactory().createProposal(targetRound, blockToPropose)); + nextProposer.injectProposalForFutureRound( + targetRound, new RoundChangeCertificate(roundChanges), blockToPropose); final Prepare expectedPrepare = localNodeMessageFactory.createPrepare(targetRound, blockToPropose.getHash()); @@ -80,7 +80,7 @@ public class ReceivedNewRoundTest { } @Test - public void newRoundMessageFromIllegalSenderIsDiscardedAndNoPrepareForNewRoundIsSent() { + public void proposalFromIllegalSenderIsDiscardedAndNoPrepareForNewRoundIsSent() { final ConsensusRoundIdentifier nextRoundId = new ConsensusRoundIdentifier(1, 1); final Block blockToPropose = context.createBlockForProposalFromChainHead(nextRoundId.getRoundNumber(), 15); @@ -91,16 +91,14 @@ public class ReceivedNewRoundTest { final ValidatorPeer illegalProposer = context.roundSpecificPeers(nextRoundId).getNonProposing(0); - illegalProposer.injectNewRound( - nextRoundId, - new RoundChangeCertificate(roundChanges), - illegalProposer.getMessageFactory().createProposal(nextRoundId, blockToPropose)); + illegalProposer.injectProposalForFutureRound( + nextRoundId, new RoundChangeCertificate(roundChanges), blockToPropose); peers.verifyNoMessagesReceived(); } @Test - public void newRoundWithPrepareCertificateResultsInNewRoundStartingWithExpectedBlock() { + public void proposalWithPrepareCertificateResultsInNewRoundStartingWithExpectedBlock() { final Block initialBlock = context.createBlockForProposalFromChainHead(0, 15); final Block reproposedBlock = context.createBlockForProposalFromChainHead(1, 15); final ConsensusRoundIdentifier nextRoundId = new ConsensusRoundIdentifier(1, 1); @@ -113,18 +111,16 @@ public class ReceivedNewRoundTest { final ValidatorPeer nextProposer = context.roundSpecificPeers(nextRoundId).getProposer(); - nextProposer.injectNewRound( - nextRoundId, - new RoundChangeCertificate(roundChanges), - peers.getNonProposing(0).getMessageFactory().createProposal(nextRoundId, reproposedBlock)); + nextProposer.injectProposalForFutureRound( + nextRoundId, new RoundChangeCertificate(roundChanges), reproposedBlock); peers.verifyMessagesReceived( localNodeMessageFactory.createPrepare(nextRoundId, reproposedBlock.getHash())); } @Test - public void newRoundMessageForPriorRoundIsNotActioned() { - // first move to a future round, then inject a newRound for a prior round, local node + public void proposalMessageForPriorRoundIsNotActioned() { + // first move to a future round, then inject a proposal for a prior round, local node // should send no messages. final ConsensusRoundIdentifier futureRound = new ConsensusRoundIdentifier(1, 2); peers.roundChange(futureRound); @@ -136,19 +132,16 @@ public class ReceivedNewRoundTest { final ValidatorPeer interimRoundProposer = context.roundSpecificPeers(interimRound).getProposer(); - final Proposal proposal = - interimRoundProposer - .getMessageFactory() - .createProposal(interimRound, context.createBlockForProposalFromChainHead(1, 30)); - - interimRoundProposer.injectNewRound( - interimRound, new RoundChangeCertificate(roundChangePayloads), proposal); + interimRoundProposer.injectProposalForFutureRound( + interimRound, + new RoundChangeCertificate(roundChangePayloads), + context.createBlockForProposalFromChainHead(1, 30)); peers.verifyNoMessagesReceived(); } @Test - public void receiveRoundStateIsNotLostIfASecondNewRoundMessageIsReceivedForCurrentRound() { + public void receiveRoundStateIsNotLostIfASecondProposalMessageIsReceivedForCurrentRound() { final Block initialBlock = context.createBlockForProposalFromChainHead(0, 15); final Block reproposedBlock = context.createBlockForProposalFromChainHead(1, 15); final ConsensusRoundIdentifier nextRoundId = new ConsensusRoundIdentifier(1, 1); @@ -162,22 +155,18 @@ public class ReceivedNewRoundTest { final RoundSpecificPeers nextRoles = context.roundSpecificPeers(nextRoundId); final ValidatorPeer nextProposer = nextRoles.getProposer(); - nextProposer.injectNewRound( - nextRoundId, - new RoundChangeCertificate(roundChanges), - peers.getNonProposing(0).getMessageFactory().createProposal(nextRoundId, reproposedBlock)); + nextProposer.injectProposalForFutureRound( + nextRoundId, new RoundChangeCertificate(roundChanges), reproposedBlock); peers.verifyMessagesReceived( localNodeMessageFactory.createPrepare(nextRoundId, reproposedBlock.getHash())); - // Inject a prepare, then re-inject the newRound - then ensure only a single prepare is enough + // Inject a prepare, then re-inject the proposal - then ensure only a single prepare is enough // to trigger a Commit transmission from the local node nextRoles.getNonProposing(0).injectPrepare(nextRoundId, reproposedBlock.getHash()); - nextProposer.injectNewRound( - nextRoundId, - new RoundChangeCertificate(roundChanges), - peers.getNonProposing(0).getMessageFactory().createProposal(nextRoundId, reproposedBlock)); + nextProposer.injectProposalForFutureRound( + nextRoundId, new RoundChangeCertificate(roundChanges), reproposedBlock); peers.verifyNoMessagesReceived(); diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/RoundChangeTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/RoundChangeTest.java index 80c24bf060..65558ba82c 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/RoundChangeTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/RoundChangeTest.java @@ -19,7 +19,6 @@ import static tech.pegasys.pantheon.consensus.ibft.support.IntegrationTestHelper import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.IbftHelpers; import tech.pegasys.pantheon.consensus.ibft.ibftevent.RoundExpiry; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; @@ -138,7 +137,7 @@ public class RoundChangeTest { } @Test - public void whenSufficientRoundChangeMessagesAreReceivedForNewRoundLocalNodeCreatesNewRoundMsg() { + public void whenSufficientRoundChangeMessagesAreReceivedForNewRoundLocalNodeCreatesProposalMsg() { // Note: Round-4 is the next round for which the local node is Proposer final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); final Block locallyProposedBlock = @@ -149,25 +148,23 @@ public class RoundChangeTest { final RoundChange rc3 = peers.getNonProposing(2).injectRoundChange(targetRound, empty()); final RoundChange rc4 = peers.getProposer().injectRoundChange(targetRound, empty()); - final NewRound expectedNewRound = - localNodeMessageFactory.createNewRound( + final Proposal expectedProposal = + localNodeMessageFactory.createProposal( targetRound, - new RoundChangeCertificate( - Lists.newArrayList( - rc1.getSignedPayload(), - rc2.getSignedPayload(), - rc3.getSignedPayload(), - rc4.getSignedPayload())), - localNodeMessageFactory - .createProposal(targetRound, locallyProposedBlock) - .getSignedPayload(), - locallyProposedBlock); - - peers.verifyMessagesReceived(expectedNewRound); + locallyProposedBlock, + Optional.of( + new RoundChangeCertificate( + Lists.newArrayList( + rc1.getSignedPayload(), + rc2.getSignedPayload(), + rc3.getSignedPayload(), + rc4.getSignedPayload())))); + + peers.verifyMessagesReceived(expectedProposal); } @Test - public void newRoundMessageContainsBlockOnWhichPeerPrepared() { + public void proposalMessageContainsBlockOnWhichPeerPrepared() { final long ARBITRARY_BLOCKTIME = 1500; final PreparedRoundArtifacts earlierPrepCert = @@ -204,21 +201,19 @@ public class RoundChangeTest { context.createBlockForProposalFromChainHead( targetRound.getRoundNumber(), ARBITRARY_BLOCKTIME); - final NewRound expectedNewRound = - localNodeMessageFactory.createNewRound( + final Proposal expectedProposal = + localNodeMessageFactory.createProposal( targetRound, - new RoundChangeCertificate( - Lists.newArrayList( - rc1.getSignedPayload(), - rc2.getSignedPayload(), - rc3.getSignedPayload(), - rc4.getSignedPayload())), - localNodeMessageFactory - .createProposal(targetRound, expectedBlockToPropose) - .getSignedPayload(), - expectedBlockToPropose); - - peers.verifyMessagesReceived(expectedNewRound); + expectedBlockToPropose, + Optional.of( + new RoundChangeCertificate( + Lists.newArrayList( + rc1.getSignedPayload(), + rc2.getSignedPayload(), + rc3.getSignedPayload(), + rc4.getSignedPayload())))); + + peers.verifyMessagesReceived(expectedProposal); } @Test @@ -234,16 +229,13 @@ public class RoundChangeTest { final Block locallyProposedBlock = context.createBlockForProposalFromChainHead(futureRound.getRoundNumber(), blockTimeStamp); - final NewRound expectedNewRound = - localNodeMessageFactory.createNewRound( + final Proposal expectedProposal = + localNodeMessageFactory.createProposal( futureRound, - new RoundChangeCertificate(roundChangeMessages), - localNodeMessageFactory - .createProposal(futureRound, locallyProposedBlock) - .getSignedPayload(), - locallyProposedBlock); + locallyProposedBlock, + Optional.of(new RoundChangeCertificate(roundChangeMessages))); - peers.verifyMessagesReceived(expectedNewRound); + peers.verifyMessagesReceived(expectedProposal); } @Test @@ -289,16 +281,13 @@ public class RoundChangeTest { context.createBlockForProposalFromChainHead( targetRound.getRoundNumber(), ARBITRARY_BLOCKTIME); - final NewRound expectedNewRound = - localNodeMessageFactory.createNewRound( + final Proposal expectedProposal = + localNodeMessageFactory.createProposal( targetRound, - new RoundChangeCertificate(Lists.newArrayList(roundChangeMessages)), - localNodeMessageFactory - .createProposal(targetRound, expectedBlockToPropose) - .getSignedPayload(), - expectedBlockToPropose); + expectedBlockToPropose, + Optional.of(new RoundChangeCertificate(Lists.newArrayList(roundChangeMessages)))); - peers.verifyMessagesReceived(expectedNewRound); + peers.verifyMessagesReceived(expectedProposal); } @Test @@ -335,14 +324,17 @@ public class RoundChangeTest { // create illegal RoundChangeMessage final PreparedRoundArtifacts illegalPreparedRoundArtifacts = new PreparedRoundArtifacts( - peers.getNonProposing(0).getMessageFactory().createProposal(roundId, blockToPropose), + peers + .getNonProposing(0) + .getMessageFactory() + .createProposal(roundId, blockToPropose, Optional.empty()), emptyList()); peers .getNonProposing(2) .injectRoundChange(targetRound, Optional.of(illegalPreparedRoundArtifacts)); - // Ensure no NewRound message is sent. + // Ensure no Proposal message is sent. peers.verifyNoMessagesReceived(); } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/SpuriousBehaviourTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/SpuriousBehaviourTest.java index 03b10c2ff5..d70743d8a3 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/SpuriousBehaviourTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/SpuriousBehaviourTest.java @@ -96,7 +96,7 @@ public class SpuriousBehaviourTest { } @Test - public void nonValidatorsCannotTriggerReponses() { + public void nonValidatorsCannotTriggerResponses() { final KeyPair nonValidatorKeys = KeyPair.generate(); final NodeParams nonValidatorParams = new NodeParams(Util.publicKeyToAddress(nonValidatorKeys.getPublicKey()), nonValidatorKeys); diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java index be11883764..8ce4de51e0 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java @@ -14,7 +14,6 @@ package tech.pegasys.pantheon.consensus.ibft; import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.IbftV2; -import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; @@ -64,9 +63,6 @@ public class IbftGossip implements Gossiper { case IbftV2.ROUND_CHANGE: decodedMessage = RoundChangeMessageData.fromMessageData(messageData).decode(); break; - case IbftV2.NEW_ROUND: - decodedMessage = NewRoundMessageData.fromMessageData(messageData).decode(); - break; default: throw new IllegalArgumentException( "Received message does not conform to any recognised IBFT message structure."); diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagedata/IbftV2.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagedata/IbftV2.java index 4fb75ca798..d3ab022daa 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagedata/IbftV2.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagedata/IbftV2.java @@ -18,7 +18,6 @@ public class IbftV2 { public static final int PREPARE = 1; public static final int COMMIT = 2; public static final int ROUND_CHANGE = 3; - public static final int NEW_ROUND = 4; - public static final int MESSAGE_SPACE = 5; + public static final int MESSAGE_SPACE = 4; } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagedata/NewRoundMessageData.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagedata/NewRoundMessageData.java deleted file mode 100644 index fc7cd12bb4..0000000000 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagedata/NewRoundMessageData.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2018 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.messagedata; - -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; -import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; -import tech.pegasys.pantheon.util.bytes.BytesValue; - -public class NewRoundMessageData extends AbstractIbftMessageData { - - private static final int MESSAGE_CODE = IbftV2.NEW_ROUND; - - private NewRoundMessageData(final BytesValue data) { - super(data); - } - - public static NewRoundMessageData fromMessageData(final MessageData messageData) { - return fromMessageData( - messageData, MESSAGE_CODE, NewRoundMessageData.class, NewRoundMessageData::new); - } - - public NewRound decode() { - return NewRound.decode(data); - } - - public static NewRoundMessageData create(final NewRound newRound) { - return new NewRoundMessageData(newRound.encode()); - } - - @Override - public int getCode() { - return MESSAGE_CODE; - } -} diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagewrappers/NewRound.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagewrappers/NewRound.java deleted file mode 100644 index b93dc7173e..0000000000 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagewrappers/NewRound.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.messagewrappers; - -import tech.pegasys.pantheon.consensus.ibft.IbftBlockHashing; -import tech.pegasys.pantheon.consensus.ibft.payload.NewRoundPayload; -import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; -import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; -import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.ethereum.core.Block; -import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput; -import tech.pegasys.pantheon.ethereum.rlp.RLP; -import tech.pegasys.pantheon.ethereum.rlp.RLPInput; -import tech.pegasys.pantheon.util.bytes.BytesValue; - -public class NewRound extends IbftMessage { - - private final Block proposedBlock; - - public NewRound(final SignedData payload, final Block proposedBlock) { - super(payload); - this.proposedBlock = proposedBlock; - } - - public RoundChangeCertificate getRoundChangeCertificate() { - return getPayload().getRoundChangeCertificate(); - } - - public SignedData getProposalPayload() { - return getPayload().getProposalPayload(); - } - - public Block getBlock() { - return proposedBlock; - } - - @Override - public BytesValue encode() { - final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput(); - rlpOut.startList(); - getSignedPayload().writeTo(rlpOut); - proposedBlock.writeTo(rlpOut); - rlpOut.endList(); - return rlpOut.encoded(); - } - - public static NewRound decode(final BytesValue data) { - RLPInput rlpIn = RLP.input(data); - rlpIn.enterList(); - final SignedData payload = SignedData.readSignedNewRoundPayloadFrom(rlpIn); - final Block proposedBlock = - Block.readFrom(rlpIn, IbftBlockHashing::calculateDataHashForCommittedSeal); - rlpIn.leaveList(); - return new NewRound(payload, proposedBlock); - } -} diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagewrappers/Proposal.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagewrappers/Proposal.java index a3466c8827..a7765d2ed9 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagewrappers/Proposal.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/messagewrappers/Proposal.java @@ -14,6 +14,7 @@ package tech.pegasys.pantheon.consensus.ibft.messagewrappers; import tech.pegasys.pantheon.consensus.ibft.IbftBlockHashing; import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; +import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; import tech.pegasys.pantheon.ethereum.core.Block; import tech.pegasys.pantheon.ethereum.core.Hash; @@ -22,13 +23,21 @@ import tech.pegasys.pantheon.ethereum.rlp.RLP; import tech.pegasys.pantheon.ethereum.rlp.RLPInput; import tech.pegasys.pantheon.util.bytes.BytesValue; +import java.util.Optional; + public class Proposal extends IbftMessage { private final Block proposedBlock; - public Proposal(final SignedData payload, final Block proposedBlock) { + private final Optional roundChangeCertificate; + + public Proposal( + final SignedData payload, + final Block proposedBlock, + final Optional certificate) { super(payload); this.proposedBlock = proposedBlock; + this.roundChangeCertificate = certificate; } public Block getBlock() { @@ -39,12 +48,21 @@ public class Proposal extends IbftMessage { return getPayload().getDigest(); } + public Optional getRoundChangeCertificate() { + return roundChangeCertificate; + } + @Override public BytesValue encode() { final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput(); rlpOut.startList(); getSignedPayload().writeTo(rlpOut); proposedBlock.writeTo(rlpOut); + if (roundChangeCertificate.isPresent()) { + roundChangeCertificate.get().writeTo(rlpOut); + } else { + rlpOut.writeNull(); + } rlpOut.endList(); return rlpOut.encoded(); } @@ -55,7 +73,22 @@ public class Proposal extends IbftMessage { final SignedData payload = SignedData.readSignedProposalPayloadFrom(rlpIn); final Block proposedBlock = Block.readFrom(rlpIn, IbftBlockHashing::calculateDataHashForCommittedSeal); + + final Optional roundChangeCertificate = + readRoundChangeCertificate(rlpIn); + rlpIn.leaveList(); - return new Proposal(payload, proposedBlock); + return new Proposal(payload, proposedBlock, roundChangeCertificate); + } + + private static Optional readRoundChangeCertificate(final RLPInput rlpIn) { + RoundChangeCertificate roundChangeCertificate = null; + if (!rlpIn.nextIsNull()) { + roundChangeCertificate = RoundChangeCertificate.readFrom(rlpIn); + } else { + rlpIn.skipNext(); + } + + return Optional.ofNullable(roundChangeCertificate); } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java index d219b3d4a0..61871b4042 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java @@ -14,19 +14,15 @@ package tech.pegasys.pantheon.consensus.ibft.network; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; -import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; -import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; -import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; import tech.pegasys.pantheon.consensus.ibft.statemachine.PreparedRoundArtifacts; import tech.pegasys.pantheon.crypto.SECP256K1.Signature; import tech.pegasys.pantheon.ethereum.core.Block; @@ -45,8 +41,12 @@ public class IbftMessageTransmitter { this.multicaster = multicaster; } - public void multicastProposal(final ConsensusRoundIdentifier roundIdentifier, final Block block) { - final Proposal data = messageFactory.createProposal(roundIdentifier, block); + public void multicastProposal( + final ConsensusRoundIdentifier roundIdentifier, + final Block block, + final Optional roundChangeCertificate) { + final Proposal data = + messageFactory.createProposal(roundIdentifier, block, roundChangeCertificate); final ProposalMessageData message = ProposalMessageData.create(data); @@ -83,19 +83,4 @@ public class IbftMessageTransmitter { multicaster.send(message); } - - public void multicastNewRound( - final ConsensusRoundIdentifier roundIdentifier, - final RoundChangeCertificate roundChangeCertificate, - final SignedData proposalPayload, - final Block block) { - - final NewRound signedPayload = - messageFactory.createNewRound( - roundIdentifier, roundChangeCertificate, proposalPayload, block); - - final NewRoundMessageData message = NewRoundMessageData.create(signedPayload); - - multicaster.send(message); - } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/MessageFactory.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/MessageFactory.java index a6ce818b9a..3204b744f0 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/MessageFactory.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/MessageFactory.java @@ -14,7 +14,6 @@ package tech.pegasys.pantheon.consensus.ibft.payload; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; @@ -38,11 +37,13 @@ public class MessageFactory { } public Proposal createProposal( - final ConsensusRoundIdentifier roundIdentifier, final Block block) { + final ConsensusRoundIdentifier roundIdentifier, + final Block block, + final Optional roundChangeCertificate) { final ProposalPayload payload = new ProposalPayload(roundIdentifier, block.getHash()); - return new Proposal(createSignedMessage(payload), block); + return new Proposal(createSignedMessage(payload), block, roundChangeCertificate); } public Prepare createPrepare(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { @@ -74,18 +75,6 @@ public class MessageFactory { createSignedMessage(payload), preparedRoundArtifacts.map(PreparedRoundArtifacts::getBlock)); } - public NewRound createNewRound( - final ConsensusRoundIdentifier roundIdentifier, - final RoundChangeCertificate roundChangeCertificate, - final SignedData proposalPayload, - final Block block) { - - final NewRoundPayload payload = - new NewRoundPayload(roundIdentifier, roundChangeCertificate, proposalPayload); - - return new NewRound(createSignedMessage(payload), block); - } - private SignedData createSignedMessage(final M payload) { final Signature signature = sign(payload, validatorKeyPair); diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/NewRoundPayload.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/NewRoundPayload.java deleted file mode 100644 index 8c46bb15ec..0000000000 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/NewRoundPayload.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2018 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.payload; - -import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; -import tech.pegasys.pantheon.consensus.ibft.messagedata.IbftV2; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; -import tech.pegasys.pantheon.ethereum.rlp.RLPInput; -import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; - -import java.util.Collections; -import java.util.Objects; -import java.util.StringJoiner; - -public class NewRoundPayload implements Payload { - private static final int TYPE = IbftV2.NEW_ROUND; - private final ConsensusRoundIdentifier roundChangeIdentifier; - private final RoundChangeCertificate roundChangeCertificate; - private final SignedData proposalPayload; - - public NewRoundPayload( - final ConsensusRoundIdentifier roundIdentifier, - final RoundChangeCertificate roundChangeCertificate, - final SignedData proposalPayload) { - this.roundChangeIdentifier = roundIdentifier; - this.roundChangeCertificate = roundChangeCertificate; - this.proposalPayload = proposalPayload; - } - - @Override - public ConsensusRoundIdentifier getRoundIdentifier() { - return roundChangeIdentifier; - } - - public RoundChangeCertificate getRoundChangeCertificate() { - return roundChangeCertificate; - } - - public SignedData getProposalPayload() { - return proposalPayload; - } - - @Override - public void writeTo(final RLPOutput rlpOutput) { - // RLP encode of the message data content (round identifier and prepared certificate) - rlpOutput.startList(); - roundChangeIdentifier.writeTo(rlpOutput); - roundChangeCertificate.writeTo(rlpOutput); - proposalPayload.writeTo(rlpOutput); - rlpOutput.endList(); - } - - public static NewRoundPayload readFrom(final RLPInput rlpInput) { - - rlpInput.enterList(); - final ConsensusRoundIdentifier roundIdentifier = ConsensusRoundIdentifier.readFrom(rlpInput); - final RoundChangeCertificate roundChangeCertificate = RoundChangeCertificate.readFrom(rlpInput); - final SignedData proposalPayload = - SignedData.readSignedProposalPayloadFrom(rlpInput); - rlpInput.leaveList(); - - return new NewRoundPayload(roundIdentifier, roundChangeCertificate, proposalPayload); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final NewRoundPayload that = (NewRoundPayload) o; - return Objects.equals(roundChangeIdentifier, that.roundChangeIdentifier) - && Objects.equals(roundChangeCertificate, that.roundChangeCertificate) - && Objects.equals(proposalPayload, that.proposalPayload); - } - - @Override - public int hashCode() { - return Objects.hash(roundChangeIdentifier, roundChangeCertificate, proposalPayload); - } - - @Override - public String toString() { - return new StringJoiner(", ", NewRoundPayload.class.getSimpleName() + "[", "]") - .add("roundChangeIdentifier=" + roundChangeIdentifier) - .add("roundChangeCertificate=" + roundChangeCertificate) - .add("proposalPayload=" + proposalPayload) - .toString(); - } - - @Override - public int getMessageType() { - return TYPE; - } - - public static class Builder { - - private ConsensusRoundIdentifier roundChangeIdentifier = new ConsensusRoundIdentifier(1, 0); - - private RoundChangeCertificate roundChangeCertificate = - new RoundChangeCertificate(Collections.emptyList()); - - private SignedData proposalPayload = null; - - public Builder( - final ConsensusRoundIdentifier roundChangeIdentifier, - final RoundChangeCertificate roundChangeCertificate, - final SignedData proposalPayload) { - this.roundChangeIdentifier = roundChangeIdentifier; - this.roundChangeCertificate = roundChangeCertificate; - this.proposalPayload = proposalPayload; - } - - public static Builder fromExisting(final NewRound payload) { - return new Builder( - payload.getRoundIdentifier(), - payload.getRoundChangeCertificate(), - payload.getProposalPayload()); - } - - public void setRoundChangeIdentifier(final ConsensusRoundIdentifier roundChangeIdentifier) { - this.roundChangeIdentifier = roundChangeIdentifier; - } - - public void setRoundChangeCertificate(final RoundChangeCertificate roundChangeCertificate) { - this.roundChangeCertificate = roundChangeCertificate; - } - - public NewRoundPayload build() { - return new NewRoundPayload(roundChangeIdentifier, roundChangeCertificate, proposalPayload); - } - } -} diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/SignedData.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/SignedData.java index fd89f8c1bf..ec68e399d5 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/SignedData.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/payload/SignedData.java @@ -24,6 +24,7 @@ import java.util.Objects; import java.util.StringJoiner; public class SignedData implements Authored { + private final Address sender; private final Signature signature; private final M unsignedPayload; @@ -98,16 +99,6 @@ public class SignedData implements Authored { return from(unsignedMessageData, signature); } - public static SignedData readSignedNewRoundPayloadFrom(final RLPInput rlpInput) { - - rlpInput.enterList(); - final NewRoundPayload unsignedMessageData = NewRoundPayload.readFrom(rlpInput); - final Signature signature = readSignature(rlpInput); - rlpInput.leaveList(); - - return from(unsignedMessageData, signature); - } - protected static SignedData from( final M unsignedMessageData, final Signature signature) { diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/protocol/IbftSubProtocol.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/protocol/IbftSubProtocol.java index c6b546ce4f..3bb74e72a9 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/protocol/IbftSubProtocol.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/protocol/IbftSubProtocol.java @@ -44,7 +44,6 @@ public class IbftSubProtocol implements SubProtocol { case IbftV2.PREPARE: case IbftV2.COMMIT: case IbftV2.ROUND_CHANGE: - case IbftV2.NEW_ROUND: return true; default: @@ -63,8 +62,6 @@ public class IbftSubProtocol implements SubProtocol { return "Commit"; case IbftV2.ROUND_CHANGE: return "RoundChange"; - case IbftV2.NEW_ROUND: - return "NewRound"; default: return INVALID_MESSAGE_NAME; } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/BlockHeightManager.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/BlockHeightManager.java index 018b0d5979..ff0d78343e 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/BlockHeightManager.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/BlockHeightManager.java @@ -15,7 +15,6 @@ 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.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; @@ -37,8 +36,6 @@ public interface BlockHeightManager { void handleRoundChangePayload(RoundChange roundChange); - void handleNewRoundPayload(NewRound newRound); - long getChainHeight(); BlockHeader getParentBlockHeader(); diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManager.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManager.java index 061dfcb104..aec650b751 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManager.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManager.java @@ -22,15 +22,14 @@ import tech.pegasys.pantheon.consensus.ibft.RoundTimer; import tech.pegasys.pantheon.consensus.ibft.ibftevent.RoundExpiry; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.IbftMessage; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; import tech.pegasys.pantheon.consensus.ibft.network.IbftMessageTransmitter; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.Payload; +import tech.pegasys.pantheon.consensus.ibft.validation.FutureRoundProposalMessageValidator; import tech.pegasys.pantheon.consensus.ibft.validation.MessageValidatorFactory; -import tech.pegasys.pantheon.consensus.ibft.validation.NewRoundMessageValidator; import tech.pegasys.pantheon.ethereum.core.BlockHeader; import java.time.Clock; @@ -64,7 +63,7 @@ public class IbftBlockHeightManager implements BlockHeightManager { private final IbftMessageTransmitter transmitter; private final MessageFactory messageFactory; private final Map futureRoundStateBuffer = Maps.newHashMap(); - private final NewRoundMessageValidator newRoundMessageValidator; + private final FutureRoundProposalMessageValidator futureRoundProposalMessageValidator; private final Clock clock; private final Function roundStateCreator; private final IbftFinalState finalState; @@ -90,8 +89,9 @@ public class IbftBlockHeightManager implements BlockHeightManager { this.roundChangeManager = roundChangeManager; this.finalState = finalState; - newRoundMessageValidator = - messageValidatorFactory.createNewRoundValidator(getChainHeight(), parentHeader); + futureRoundProposalMessageValidator = + messageValidatorFactory.createFutureRoundProposalMessageValidator( + getChainHeight(), parentHeader); roundStateCreator = (roundIdentifier) -> @@ -157,8 +157,21 @@ public class IbftBlockHeightManager implements BlockHeightManager { @Override public void handleProposalPayload(final Proposal proposal) { LOG.trace("Received a Proposal Payload."); - actionOrBufferMessage( - proposal, currentRound::handleProposalMessage, RoundState::setProposedBlock); + final MessageAge messageAge = + determineAgeOfPayload(proposal.getRoundIdentifier().getRoundNumber()); + + if (messageAge == PRIOR_ROUND) { + LOG.trace("Received Proposal Payload for a prior round={}", proposal.getRoundIdentifier()); + } else { + if (messageAge == FUTURE_ROUND) { + if (!futureRoundProposalMessageValidator.validateProposalMessage(proposal)) { + LOG.debug("Received future Proposal which is illegal, no round change triggered."); + return; + } + startNewRound(proposal.getRoundIdentifier().getRoundNumber()); + } + currentRound.handleProposalMessage(proposal); + } } @Override @@ -237,26 +250,6 @@ public class IbftBlockHeightManager implements BlockHeightManager { roundTimer.startTimer(currentRound.getRoundIdentifier()); } - @Override - public void handleNewRoundPayload(final NewRound newRound) { - // final NewRoundPayload payload = newRound.getSignedPayload().getPayload(); - final MessageAge messageAge = - determineAgeOfPayload(newRound.getRoundIdentifier().getRoundNumber()); - - if (messageAge == PRIOR_ROUND) { - LOG.trace("Received NewRound Payload for a prior round={}", newRound.getRoundIdentifier()); - return; - } - LOG.trace("Received NewRound Payload for {}", newRound.getRoundIdentifier()); - - if (newRoundMessageValidator.validateNewRoundMessage(newRound)) { - if (messageAge == FUTURE_ROUND) { - startNewRound(newRound.getRoundIdentifier().getRoundNumber()); - } - currentRound.handleProposalFromNewRound(newRound); - } - } - @Override public long getChainHeight() { return parentHeader.getNumber() + 1; diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftController.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftController.java index c8cb5d6223..6433ce38fd 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftController.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftController.java @@ -23,7 +23,6 @@ import tech.pegasys.pantheon.consensus.ibft.ibftevent.NewChainHead; import tech.pegasys.pantheon.consensus.ibft.ibftevent.RoundExpiry; import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.IbftV2; -import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; @@ -128,13 +127,6 @@ public class IbftController { currentHeightManager::handleRoundChangePayload); break; - case IbftV2.NEW_ROUND: - consumeMessage( - message, - NewRoundMessageData.fromMessageData(messageData).decode(), - currentHeightManager::handleNewRoundPayload); - break; - default: throw new IllegalArgumentException( String.format( diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRound.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRound.java index e7a3efb817..7fbc158959 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRound.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRound.java @@ -20,11 +20,11 @@ import tech.pegasys.pantheon.consensus.ibft.IbftExtraData; import tech.pegasys.pantheon.consensus.ibft.IbftHelpers; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreator; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.network.IbftMessageTransmitter; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; +import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.crypto.SECP256K1; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.crypto.SECP256K1.Signature; @@ -86,72 +86,54 @@ public class IbftRound { roundState.getRoundIdentifier(), extraData, block.getHeader()); - transmitter.multicastProposal(roundState.getRoundIdentifier(), block); - updateStateWithProposedBlock( - messageFactory.createProposal(roundState.getRoundIdentifier(), block)); + updateStateWithProposalAndTransmit(block, Optional.empty()); } public void startRoundWith( final RoundChangeArtifacts roundChangeArtifacts, final long headerTimestamp) { final Optional bestBlockFromRoundChange = roundChangeArtifacts.getBlock(); - Proposal proposal; + final RoundChangeCertificate roundChangeCertificate = + roundChangeArtifacts.getRoundChangeCertificate(); + Block blockToPublish; if (!bestBlockFromRoundChange.isPresent()) { - LOG.debug("Multicasting NewRound with new block. round={}", roundState.getRoundIdentifier()); - final Block block = blockCreator.createBlock(headerTimestamp); - proposal = messageFactory.createProposal(getRoundIdentifier(), block); + LOG.debug("Multicasting Proposal with new block. round={}", roundState.getRoundIdentifier()); + blockToPublish = blockCreator.createBlock(headerTimestamp); } else { LOG.debug( - "Multicasting NewRound from PreparedCertificate. round={}", + "Multicasting Proposal from PreparedCertificate. round={}", roundState.getRoundIdentifier()); - proposal = createProposalAroundBlock(bestBlockFromRoundChange.get()); + blockToPublish = + IbftBlockInterface.replaceRoundInBlock( + bestBlockFromRoundChange.get(), + getRoundIdentifier().getRoundNumber(), + IbftBlockHashing::calculateDataHashForCommittedSeal); } - transmitter.multicastNewRound( - getRoundIdentifier(), - roundChangeArtifacts.getRoundChangeCertificate(), - proposal.getSignedPayload(), - proposal.getBlock()); - updateStateWithProposedBlock(proposal); - } - private Proposal createProposalAroundBlock(final Block block) { - final Block blockToPublish = - IbftBlockInterface.replaceRoundInBlock( - block, - getRoundIdentifier().getRoundNumber(), - IbftBlockHashing::calculateDataHashForCommittedSeal); - return messageFactory.createProposal(getRoundIdentifier(), blockToPublish); + updateStateWithProposalAndTransmit(blockToPublish, Optional.of(roundChangeCertificate)); } - public void handleProposalMessage(final Proposal msg) { - LOG.debug("Handling a Proposal message."); + private void updateStateWithProposalAndTransmit( + final Block block, final Optional roundChangeCertificate) { + final Proposal proposal = + messageFactory.createProposal(getRoundIdentifier(), block, roundChangeCertificate); - if (getRoundIdentifier().getRoundNumber() != 0) { - LOG.error("Illegally received a Proposal message when not in Round 0."); - return; - } - actionReceivedProposal(msg); - } - - public void handleProposalFromNewRound(final NewRound msg) { - LOG.debug("Handling a New Round Proposal."); - - if (getRoundIdentifier().getRoundNumber() == 0) { - LOG.error("Illegally received a NewRound message when in Round 0."); - return; - } - actionReceivedProposal(new Proposal(msg.getProposalPayload(), msg.getBlock())); + transmitter.multicastProposal( + proposal.getRoundIdentifier(), proposal.getBlock(), proposal.getRoundChangeCertificate()); + updateStateWithProposedBlock(proposal); } - private void actionReceivedProposal(final Proposal msg) { + public void handleProposalMessage(final Proposal msg) { + LOG.debug("Handling a Proposal message."); final Block block = msg.getBlock(); if (updateStateWithProposedBlock(msg)) { LOG.debug("Sending prepare message."); - transmitter.multicastPrepare(getRoundIdentifier(), block.getHash()); final Prepare localPrepareMessage = - messageFactory.createPrepare(roundState.getRoundIdentifier(), block.getHash()); + messageFactory.createPrepare(getRoundIdentifier(), block.getHash()); + transmitter.multicastPrepare( + localPrepareMessage.getRoundIdentifier(), localPrepareMessage.getDigest()); peerIsPrepared(localPrepareMessage); } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/NoOpBlockHeightManager.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/NoOpBlockHeightManager.java index e49f0e84f2..5714079d47 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/NoOpBlockHeightManager.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/NoOpBlockHeightManager.java @@ -15,7 +15,6 @@ 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.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; @@ -50,9 +49,6 @@ public class NoOpBlockHeightManager implements BlockHeightManager { @Override public void handleRoundChangePayload(final RoundChange roundChange) {} - @Override - public void handleNewRoundPayload(final NewRound newRound) {} - @Override public long getChainHeight() { return parentHeader.getNumber() + 1; diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeManager.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeManager.java index 3f263604ee..edc9f8f38e 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeManager.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeManager.java @@ -32,7 +32,7 @@ import org.apache.logging.log4j.Logger; * and messages for a future round should have been buffered). * *

If enough RoundChange messages all targeting a given round are received (and this node is the - * proposer for said round) - a newRound message is sent, and a new round should be started by the + * proposer for said round) - a proposal message is sent, and a new round should be started by the * controlling class. */ public class RoundChangeManager { diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/FutureRoundProposalMessageValidator.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/FutureRoundProposalMessageValidator.java new file mode 100644 index 0000000000..508bad9155 --- /dev/null +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/FutureRoundProposalMessageValidator.java @@ -0,0 +1,53 @@ +/* + * 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.validation; + +import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; +import tech.pegasys.pantheon.ethereum.core.BlockHeader; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/* One of these will be created by the IbftBlockHeightManager and will exist for the life of the +chainheight, and used to ensure supplied Proposals are suitable for starting a new round. + */ +public class FutureRoundProposalMessageValidator { + + private static final Logger LOG = LogManager.getLogger(); + + private final MessageValidatorFactory messageValidatorFactory; + private final long chainHeight; + private final BlockHeader parentHeader; + + public FutureRoundProposalMessageValidator( + final MessageValidatorFactory messageValidatorFactory, + final long chainHeight, + final BlockHeader parentHeader) { + this.messageValidatorFactory = messageValidatorFactory; + this.chainHeight = chainHeight; + this.parentHeader = parentHeader; + } + + public boolean validateProposalMessage(final Proposal msg) { + + if (msg.getRoundIdentifier().getSequenceNumber() != chainHeight) { + LOG.debug("Illegal Proposal message, does not target the correct round height."); + return false; + } + + final MessageValidator messageValidator = + messageValidatorFactory.createMessageValidator(msg.getRoundIdentifier(), parentHeader); + + return messageValidator.validateProposal(msg); + } +} 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 529fe29b33..485ac1e7b1 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 @@ -12,10 +12,12 @@ */ package tech.pegasys.pantheon.consensus.ibft.validation; +import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.IbftContext; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; +import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.ethereum.BlockValidator; import tech.pegasys.pantheon.ethereum.BlockValidator.BlockProcessingOutputs; import tech.pegasys.pantheon.ethereum.ProtocolContext; @@ -35,19 +37,23 @@ public class MessageValidator { private final ProposalBlockConsistencyValidator proposalConsistencyValidator; private final BlockValidator blockValidator; private final ProtocolContext protocolContext; + private RoundChangeCertificateValidator roundChangeCertificateValidator; public MessageValidator( final SignedDataValidator signedDataValidator, final ProposalBlockConsistencyValidator proposalConsistencyValidator, final BlockValidator blockValidator, - final ProtocolContext protocolContext) { + final ProtocolContext protocolContext, + final RoundChangeCertificateValidator roundChangeCertificateValidator) { this.signedDataValidator = signedDataValidator; this.proposalConsistencyValidator = proposalConsistencyValidator; this.blockValidator = blockValidator; this.protocolContext = protocolContext; + this.roundChangeCertificateValidator = roundChangeCertificateValidator; } public boolean validateProposal(final Proposal msg) { + if (!signedDataValidator.validateProposal(msg.getSignedPayload())) { LOG.debug("Illegal Proposal message, embedded signed data failed validation"); return false; @@ -57,6 +63,11 @@ public class MessageValidator { return false; } + if (!validateProposalAndRoundChangeAreConsistent(msg)) { + LOG.debug("Illegal Proposal message, embedded roundChange does not match proposal."); + return false; + } + return proposalConsistencyValidator.validateProposalMatchesBlock( msg.getSignedPayload(), msg.getBlock()); } @@ -74,6 +85,55 @@ public class MessageValidator { return true; } + private boolean validateProposalAndRoundChangeAreConsistent(final Proposal proposal) { + final ConsensusRoundIdentifier proposalRoundIdentifier = proposal.getRoundIdentifier(); + + if (proposalRoundIdentifier.getRoundNumber() == 0) { + return validateRoundZeroProposalHasNoRoundChangeCertificate(proposal); + } else { + return validateRoundChangeCertificateIsValidAndMatchesProposedBlock(proposal); + } + } + + private boolean validateRoundZeroProposalHasNoRoundChangeCertificate(final Proposal proposal) { + if (proposal.getRoundChangeCertificate().isPresent()) { + LOG.debug( + "Illegal Proposal message, round-0 proposal must not contain a round change certificate."); + return false; + } + return true; + } + + private boolean validateRoundChangeCertificateIsValidAndMatchesProposedBlock( + final Proposal proposal) { + + final Optional roundChangeCertificate = + proposal.getRoundChangeCertificate(); + + if (!roundChangeCertificate.isPresent()) { + LOG.debug( + "Illegal Proposal message, rounds other than 0 must contain a round change certificate."); + return false; + } + + final RoundChangeCertificate roundChangeCert = roundChangeCertificate.get(); + + if (!roundChangeCertificateValidator.validateRoundChangeMessagesAndEnsureTargetRoundMatchesRoot( + proposal.getRoundIdentifier(), roundChangeCert)) { + LOG.debug("Illegal Proposal message, embedded RoundChangeCertificate is not self-consistent"); + return false; + } + + if (!roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate( + roundChangeCert, proposal.getBlock())) { + LOG.debug( + "Illegal Proposal message, piggybacked block does not match latest PrepareCertificate"); + return false; + } + + return true; + } + public boolean validatePrepare(final Prepare msg) { return signedDataValidator.validatePrepare(msg.getSignedPayload()); } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorFactory.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorFactory.java index 469e714e98..83fae657e4 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorFactory.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/MessageValidatorFactory.java @@ -62,12 +62,17 @@ public class MessageValidatorFactory { final ConsensusRoundIdentifier roundIdentifier, final BlockHeader parentHeader) { final BlockValidator blockValidator = protocolSchedule.getByBlockNumber(roundIdentifier.getSequenceNumber()).getBlockValidator(); + final Collection

validators = getValidatorsAfterBlock(parentHeader); return new MessageValidator( createSignedDataValidator(roundIdentifier, parentHeader), new ProposalBlockConsistencyValidator(), blockValidator, - protocolContext); + protocolContext, + new RoundChangeCertificateValidator( + validators, + (ri) -> createSignedDataValidator(ri, parentHeader), + roundIdentifier.getSequenceNumber())); } public RoundChangeMessageValidator createRoundChangeMessageValidator( @@ -84,28 +89,9 @@ public class MessageValidatorFactory { new ProposalBlockConsistencyValidator()); } - public NewRoundMessageValidator createNewRoundValidator( + public FutureRoundProposalMessageValidator createFutureRoundProposalMessageValidator( final long chainHeight, final BlockHeader parentHeader) { - final Collection
validators = getValidatorsAfterBlock(parentHeader); - - final BlockValidator blockValidator = - protocolSchedule.getByBlockNumber(chainHeight).getBlockValidator(); - final RoundChangeCertificateValidator roundChangeCertificateValidator = - new RoundChangeCertificateValidator( - validators, - (roundIdentifier) -> createSignedDataValidator(roundIdentifier, parentHeader), - chainHeight); - - return new NewRoundMessageValidator( - new NewRoundPayloadValidator( - proposerSelector, - (roundIdentifier) -> createSignedDataValidator(roundIdentifier, parentHeader), - chainHeight, - roundChangeCertificateValidator), - new ProposalBlockConsistencyValidator(), - blockValidator, - protocolContext, - roundChangeCertificateValidator); + return new FutureRoundProposalMessageValidator(this, chainHeight, parentHeader); } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundMessageValidator.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundMessageValidator.java deleted file mode 100644 index dbdedb6aa1..0000000000 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundMessageValidator.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.validation; - -import tech.pegasys.pantheon.consensus.ibft.IbftContext; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; -import tech.pegasys.pantheon.ethereum.BlockValidator; -import tech.pegasys.pantheon.ethereum.BlockValidator.BlockProcessingOutputs; -import tech.pegasys.pantheon.ethereum.ProtocolContext; -import tech.pegasys.pantheon.ethereum.core.Block; -import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode; - -import java.util.Optional; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class NewRoundMessageValidator { - - private static final Logger LOG = LogManager.getLogger(); - - private final NewRoundPayloadValidator payloadValidator; - private final ProposalBlockConsistencyValidator proposalConsistencyValidator; - private final BlockValidator blockValidator; - private final ProtocolContext protocolContext; - private final RoundChangeCertificateValidator roundChangeCertificateValidator; - - public NewRoundMessageValidator( - final NewRoundPayloadValidator payloadValidator, - final ProposalBlockConsistencyValidator proposalConsistencyValidator, - final BlockValidator blockValidator, - final ProtocolContext protocolContext, - final RoundChangeCertificateValidator roundChangeCertificateValidator) { - this.payloadValidator = payloadValidator; - this.proposalConsistencyValidator = proposalConsistencyValidator; - this.blockValidator = blockValidator; - this.protocolContext = protocolContext; - this.roundChangeCertificateValidator = roundChangeCertificateValidator; - } - - public boolean validateNewRoundMessage(final NewRound msg) { - if (!payloadValidator.validateNewRoundMessage(msg.getSignedPayload())) { - LOG.debug("Illegal NewRound message, embedded signed data failed validation."); - return false; - } - - if (!roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate( - msg.getRoundChangeCertificate(), msg.getBlock())) { - LOG.debug( - "Illegal NewRound message, piggybacked block does not match latest PrepareCertificate"); - return false; - } - - if (!validateBlock(msg.getBlock())) { - return false; - } - - return proposalConsistencyValidator.validateProposalMatchesBlock( - msg.getProposalPayload(), msg.getBlock()); - } - - private boolean validateBlock(final Block block) { - final Optional validationResult = - blockValidator.validateAndProcessBlock( - protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL); - - if (!validationResult.isPresent()) { - LOG.info("Invalid Proposal message, block did not pass validation."); - return false; - } - - return true; - } -} diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundPayloadValidator.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundPayloadValidator.java deleted file mode 100644 index 60993a6414..0000000000 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundPayloadValidator.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2018 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.validation; - -import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; -import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector; -import tech.pegasys.pantheon.consensus.ibft.payload.NewRoundPayload; -import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; -import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; -import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.validation.RoundChangePayloadValidator.MessageValidatorForHeightFactory; -import tech.pegasys.pantheon.ethereum.core.Address; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class NewRoundPayloadValidator { - - private static final Logger LOG = LogManager.getLogger(); - - private final ProposerSelector proposerSelector; - private final MessageValidatorForHeightFactory messageValidatorFactory; - private final long chainHeight; - private final RoundChangeCertificateValidator roundChangeCertificateValidator; - - public NewRoundPayloadValidator( - final ProposerSelector proposerSelector, - final MessageValidatorForHeightFactory messageValidatorFactory, - final long chainHeight, - final RoundChangeCertificateValidator roundChangeCertificateValidator) { - this.proposerSelector = proposerSelector; - this.messageValidatorFactory = messageValidatorFactory; - this.chainHeight = chainHeight; - this.roundChangeCertificateValidator = roundChangeCertificateValidator; - } - - public boolean validateNewRoundMessage(final SignedData msg) { - - final NewRoundPayload payload = msg.getPayload(); - final ConsensusRoundIdentifier rootRoundIdentifier = payload.getRoundIdentifier(); - final Address expectedProposer = proposerSelector.selectProposerForRound(rootRoundIdentifier); - final RoundChangeCertificate roundChangeCert = payload.getRoundChangeCertificate(); - - if (!expectedProposer.equals(msg.getAuthor())) { - LOG.info("Invalid NewRound message, did not originate from expected proposer."); - return false; - } - - if (msg.getPayload().getRoundIdentifier().getSequenceNumber() != chainHeight) { - LOG.info("Invalid NewRound message, not valid for local chain height."); - return false; - } - - if (msg.getPayload().getRoundIdentifier().getRoundNumber() == 0) { - LOG.info("Invalid NewRound message, illegally targets a new round of 0."); - return false; - } - - final SignedData proposalPayload = payload.getProposalPayload(); - final SignedDataValidator proposalValidator = - messageValidatorFactory.createAt(rootRoundIdentifier); - if (!proposalValidator.validateProposal(proposalPayload)) { - LOG.info("Invalid NewRound message, embedded proposal failed validation"); - return false; - } - - if (!roundChangeCertificateValidator.validateRoundChangeMessagesAndEnsureTargetRoundMatchesRoot( - rootRoundIdentifier, roundChangeCert)) { - return false; - } - - return true; - } -} diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeCertificateValidator.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeCertificateValidator.java index e7ea6badff..5e5833bccd 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeCertificateValidator.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeCertificateValidator.java @@ -77,7 +77,7 @@ public class RoundChangeCertificateValidator { if (!roundChangeCert.getRoundChangePayloads().stream() .allMatch(roundChangeValidator::validateRoundChange)) { - LOG.info("Invalid NewRound message, embedded RoundChange message failed validation."); + LOG.info("Invalid RoundChangeCertificate, embedded RoundChange message failed validation."); return false; } @@ -116,7 +116,7 @@ public class RoundChangeCertificateValidator { .getHash() .equals(latestPreparedCertificate.get().getProposalPayload().getPayload().getDigest())) { LOG.info( - "Invalid NewRound message, block in latest RoundChange does not match proposed block."); + "Invalid RoundChangeCertificate, block in latest RoundChange does not match proposed block."); return false; } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java index 425488c0b7..a96ea6a837 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java @@ -15,7 +15,6 @@ package tech.pegasys.pantheon.consensus.ibft; import static com.google.common.collect.Lists.newArrayList; import static org.mockito.Mockito.verify; -import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.IbftMessage; @@ -73,10 +72,4 @@ public class IbftGossipTest { assertRebroadcastToAllExceptSignerAndSender( TestHelpers::createSignedRoundChangePayload, RoundChangeMessageData::create); } - - @Test - public void assertRebroadcastsNewRoundToAllExceptSignerAndSender() { - assertRebroadcastToAllExceptSignerAndSender( - TestHelpers::createSignedNewRoundPayload, NewRoundMessageData::create); - } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftHelpersTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftHelpersTest.java index 332c972526..9f43f38e85 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftHelpersTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftHelpersTest.java @@ -91,7 +91,7 @@ public class IbftHelpersTest { final ConsensusRoundIdentifier preparedRound = TestHelpers.createFrom(roundIdentifier, 0, -1); final Proposal differentProposal = - proposerMessageFactory.createProposal(preparedRound, proposedBlock); + proposerMessageFactory.createProposal(preparedRound, proposedBlock, Optional.empty()); final Optional latterPreparedRoundArtifacts = Optional.of( @@ -107,7 +107,8 @@ public class IbftHelpersTest { final ConsensusRoundIdentifier earlierPreparedRound = TestHelpers.createFrom(roundIdentifier, 0, -2); final Proposal earlierProposal = - proposerMessageFactory.createProposal(earlierPreparedRound, proposedBlock); + proposerMessageFactory.createProposal( + earlierPreparedRound, proposedBlock, Optional.empty()); final Optional earlierPreparedRoundArtifacts = Optional.of( new PreparedRoundArtifacts( diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TestHelpers.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TestHelpers.java index 53d884e8f8..812e9d27a3 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TestHelpers.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TestHelpers.java @@ -12,16 +12,13 @@ */ package tech.pegasys.pantheon.consensus.ibft; -import static com.google.common.collect.Lists.newArrayList; import static java.util.Collections.singletonList; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; -import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.crypto.SECP256K1.Signature; import tech.pegasys.pantheon.ethereum.core.Address; @@ -74,7 +71,7 @@ public class TestHelpers { new ConsensusRoundIdentifier(0x1234567890ABCDEFL, round); final Block block = TestHelpers.createProposalBlock(singletonList(AddressHelpers.ofValue(1)), roundIdentifier); - return messageFactory.createProposal(roundIdentifier, block); + return messageFactory.createProposal(roundIdentifier, block, Optional.empty()); } public static Prepare createSignedPreparePayload(final KeyPair signerKeys) { @@ -100,16 +97,4 @@ public class TestHelpers { new ConsensusRoundIdentifier(0x1234567890ABCDEFL, 0xFEDCBA98); return messageFactory.createRoundChange(roundIdentifier, Optional.empty()); } - - public static NewRound createSignedNewRoundPayload(final KeyPair signerKeys) { - final MessageFactory messageFactory = new MessageFactory(signerKeys); - final ConsensusRoundIdentifier roundIdentifier = - new ConsensusRoundIdentifier(0x1234567890ABCDEFL, 0xFEDCBA98); - final Proposal proposal = createSignedProposalPayload(signerKeys); - return messageFactory.createNewRound( - roundIdentifier, - new RoundChangeCertificate(newArrayList()), - proposal.getSignedPayload(), - proposal.getBlock()); - } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/messagedata/NewRoundMessageTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/messagedata/NewRoundMessageTest.java deleted file mode 100644 index 7d2d1c47d9..0000000000 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/messagedata/NewRoundMessageTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2018 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.messagedata; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; -import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; -import tech.pegasys.pantheon.util.bytes.BytesValue; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class NewRoundMessageTest { - @Mock private NewRound newRoundPayload; - @Mock private BytesValue messageBytes; - @Mock private MessageData messageData; - @Mock private NewRoundMessageData newRoundMessage; - - @Test - public void createMessageFromNewRoundChangeMessageData() { - when(newRoundPayload.encode()).thenReturn(messageBytes); - NewRoundMessageData prepareMessage = NewRoundMessageData.create(newRoundPayload); - - assertThat(prepareMessage.getData()).isEqualTo(messageBytes); - assertThat(prepareMessage.getCode()).isEqualTo(IbftV2.NEW_ROUND); - verify(newRoundPayload).encode(); - } - - @Test - public void createMessageFromNewRoundMessage() { - NewRoundMessageData message = NewRoundMessageData.fromMessageData(newRoundMessage); - assertThat(message).isSameAs(newRoundMessage); - } - - @Test - public void createMessageFromGenericMessageData() { - when(messageData.getData()).thenReturn(messageBytes); - when(messageData.getCode()).thenReturn(IbftV2.NEW_ROUND); - NewRoundMessageData newRoundMessage = NewRoundMessageData.fromMessageData(messageData); - - assertThat(newRoundMessage.getData()).isEqualTo(messageData.getData()); - assertThat(newRoundMessage.getCode()).isEqualTo(IbftV2.NEW_ROUND); - } - - @Test - public void createMessageFailsWhenIncorrectMessageCode() { - when(messageData.getCode()).thenReturn(42); - assertThatThrownBy(() -> NewRoundMessageData.fromMessageData(messageData)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("MessageData has code 42 and thus is not a NewRoundMessageData"); - } -} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/payload/NewRoundPayloadTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/payload/NewRoundPayloadTest.java deleted file mode 100644 index 0b8fc4ff5c..0000000000 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/payload/NewRoundPayloadTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2018 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.payload; - -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; - -import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; -import tech.pegasys.pantheon.consensus.ibft.TestHelpers; -import tech.pegasys.pantheon.consensus.ibft.messagedata.IbftV2; -import tech.pegasys.pantheon.crypto.SECP256K1.Signature; -import tech.pegasys.pantheon.ethereum.core.AddressHelpers; -import tech.pegasys.pantheon.ethereum.core.Block; -import tech.pegasys.pantheon.ethereum.core.Hash; -import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput; -import tech.pegasys.pantheon.ethereum.rlp.RLP; -import tech.pegasys.pantheon.ethereum.rlp.RLPInput; - -import java.math.BigInteger; -import java.util.Collections; -import java.util.Optional; - -import org.assertj.core.util.Lists; -import org.junit.Test; - -public class NewRoundPayloadTest { - - private static final ConsensusRoundIdentifier ROUND_IDENTIFIER = - new ConsensusRoundIdentifier(0x1234567890ABCDEFL, 0xFEDCBA98); - - @Test - public void roundTripRlpWithNoRoundChangePayloads() { - final Block block = - TestHelpers.createProposalBlock(singletonList(AddressHelpers.ofValue(1)), ROUND_IDENTIFIER); - final ProposalPayload proposalPayload = new ProposalPayload(ROUND_IDENTIFIER, block.getHash()); - final Signature signature = Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0); - final SignedData proposalPayloadSignedData = - SignedData.from(proposalPayload, signature); - - final RoundChangeCertificate roundChangeCertificate = - new RoundChangeCertificate(Collections.emptyList()); - final NewRoundPayload expectedNewRoundPayload = - new NewRoundPayload(ROUND_IDENTIFIER, roundChangeCertificate, proposalPayloadSignedData); - final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput(); - expectedNewRoundPayload.writeTo(rlpOut); - - final RLPInput rlpInput = RLP.input(rlpOut.encoded()); - final NewRoundPayload newRoundPayload = NewRoundPayload.readFrom(rlpInput); - assertThat(newRoundPayload.getProposalPayload()).isEqualTo(proposalPayloadSignedData); - assertThat(newRoundPayload.getRoundChangeCertificate()).isEqualTo(roundChangeCertificate); - assertThat(newRoundPayload.getRoundIdentifier()).isEqualTo(ROUND_IDENTIFIER); - assertThat(newRoundPayload.getMessageType()).isEqualTo(IbftV2.NEW_ROUND); - } - - @Test - public void roundTripRlpWithRoundChangePayloads() { - final Block block = - TestHelpers.createProposalBlock(singletonList(AddressHelpers.ofValue(1)), ROUND_IDENTIFIER); - final ProposalPayload proposalPayload = new ProposalPayload(ROUND_IDENTIFIER, block.getHash()); - final Signature signature = Signature.create(BigInteger.ONE, BigInteger.TEN, (byte) 0); - final SignedData signedProposal = SignedData.from(proposalPayload, signature); - - final PreparePayload preparePayload = - new PreparePayload(ROUND_IDENTIFIER, Hash.fromHexStringLenient("0x8523ba6e7c5f59ae87")); - final SignedData signedPrepare = SignedData.from(preparePayload, signature); - final PreparedCertificate preparedCert = - new PreparedCertificate(signedProposal, Lists.newArrayList(signedPrepare)); - - final RoundChangePayload roundChangePayload = - new RoundChangePayload(ROUND_IDENTIFIER, Optional.of(preparedCert)); - SignedData signedRoundChange = - SignedData.from(roundChangePayload, signature); - - final RoundChangeCertificate roundChangeCertificate = - new RoundChangeCertificate(Lists.list(signedRoundChange)); - final NewRoundPayload expectedNewRoundPayload = - new NewRoundPayload(ROUND_IDENTIFIER, roundChangeCertificate, signedProposal); - final BytesValueRLPOutput rlpOut = new BytesValueRLPOutput(); - expectedNewRoundPayload.writeTo(rlpOut); - - final RLPInput rlpInput = RLP.input(rlpOut.encoded()); - final NewRoundPayload newRoundPayload = NewRoundPayload.readFrom(rlpInput); - assertThat(newRoundPayload.getProposalPayload()).isEqualTo(signedProposal); - assertThat(newRoundPayload.getRoundChangeCertificate()).isEqualTo(roundChangeCertificate); - assertThat(newRoundPayload.getRoundIdentifier()).isEqualTo(ROUND_IDENTIFIER); - assertThat(newRoundPayload.getMessageType()).isEqualTo(IbftV2.NEW_ROUND); - } -} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/protocol/IbftSubProtocolTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/protocol/IbftSubProtocolTest.java index 3e1ee3baf8..b969560aef 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/protocol/IbftSubProtocolTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/protocol/IbftSubProtocolTest.java @@ -22,7 +22,7 @@ public class IbftSubProtocolTest { public void messageSpaceReportsCorrectly() { final IbftSubProtocol subProt = new IbftSubProtocol(); - assertThat(subProt.messageSpace(1)).isEqualTo(5); + assertThat(subProt.messageSpace(1)).isEqualTo(4); } @Test @@ -33,13 +33,12 @@ public class IbftSubProtocolTest { assertThat(subProt.isValidMessageCode(1, 1)).isTrue(); assertThat(subProt.isValidMessageCode(1, 2)).isTrue(); assertThat(subProt.isValidMessageCode(1, 3)).isTrue(); - assertThat(subProt.isValidMessageCode(1, 4)).isTrue(); } @Test public void invalidMessageTypesAreNotAcceptedByTheSubprotocol() { final IbftSubProtocol subProt = new IbftSubProtocol(); - assertThat(subProt.isValidMessageCode(1, 5)).isFalse(); + assertThat(subProt.isValidMessageCode(1, 4)).isFalse(); } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java index a2b58c805a..1c38622239 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,15 +36,15 @@ import tech.pegasys.pantheon.consensus.ibft.RoundTimer; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreator; import tech.pegasys.pantheon.consensus.ibft.ibftevent.RoundExpiry; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; +import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; import tech.pegasys.pantheon.consensus.ibft.network.IbftMessageTransmitter; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; +import tech.pegasys.pantheon.consensus.ibft.validation.FutureRoundProposalMessageValidator; import tech.pegasys.pantheon.consensus.ibft.validation.MessageValidator; import tech.pegasys.pantheon.consensus.ibft.validation.MessageValidatorFactory; -import tech.pegasys.pantheon.consensus.ibft.validation.NewRoundMessageValidator; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.crypto.SECP256K1.Signature; import tech.pegasys.pantheon.ethereum.ProtocolContext; @@ -90,7 +91,7 @@ public class IbftBlockHeightManagerTest { @Mock private BlockImporter blockImporter; @Mock private BlockTimer blockTimer; @Mock private RoundTimer roundTimer; - @Mock private NewRoundMessageValidator newRoundPayloadValidator; + @Mock private FutureRoundProposalMessageValidator futureRoundProposalMessageValidator; @Captor private ArgumentCaptor> preparedRoundArtifactsCaptor; @@ -134,9 +135,10 @@ public class IbftBlockHeightManagerTest { when(finalState.getQuorum()).thenReturn(3); when(finalState.getMessageFactory()).thenReturn(messageFactory); when(blockCreator.createBlock(anyLong())).thenReturn(createdBlock); - when(newRoundPayloadValidator.validateNewRoundMessage(any())).thenReturn(true); - when(messageValidatorFactory.createNewRoundValidator(anyLong(), any())) - .thenReturn(newRoundPayloadValidator); + + when(futureRoundProposalMessageValidator.validateProposalMessage(any())).thenReturn(true); + when(messageValidatorFactory.createFutureRoundProposalMessageValidator(anyLong(), any())) + .thenReturn(futureRoundProposalMessageValidator); when(messageValidatorFactory.createMessageValidator(any(), any())).thenReturn(messageValidator); protocolContext = new ProtocolContext<>(null, null, setupContextWithValidators(validators)); @@ -206,7 +208,7 @@ public class IbftBlockHeightManagerTest { manager.start(); manager.handleBlockTimerExpiry(roundIdentifier); - verify(messageTransmitter, times(1)).multicastProposal(eq(roundIdentifier), any()); + verify(messageTransmitter, times(1)).multicastProposal(eq(roundIdentifier), any(), any()); verify(messageTransmitter, never()).multicastPrepare(any(), any()); verify(messageTransmitter, never()).multicastPrepare(any(), any()); } @@ -256,7 +258,7 @@ public class IbftBlockHeightManagerTest { } @Test - public void whenSufficientRoundChangesAreReceivedANewRoundMessageIsTransmitted() { + public void whenSufficientRoundChangesAreReceivedAProposalMessageIsTransmitted() { final ConsensusRoundIdentifier futureRoundIdentifier = createFrom(roundIdentifier, 0, +2); final RoundChange roundChange = messageFactory.createRoundChange(futureRoundIdentifier, Optional.empty()); @@ -276,11 +278,12 @@ public class IbftBlockHeightManagerTest { clock, messageValidatorFactory); manager.start(); + reset(messageTransmitter); manager.handleRoundChangePayload(roundChange); verify(messageTransmitter, times(1)) - .multicastNewRound(eq(futureRoundIdentifier), eq(roundChangCert), any(), any()); + .multicastProposal(eq(futureRoundIdentifier), any(), eq(Optional.of(roundChangCert))); } @Test @@ -313,14 +316,13 @@ public class IbftBlockHeightManagerTest { manager.handleCommitPayload(commit); // Force a new round to be started at new round number. - final NewRound newRound = - messageFactory.createNewRound( + final Proposal futureRoundProposal = + messageFactory.createProposal( futureRoundIdentifier, - new RoundChangeCertificate(Collections.emptyList()), - messageFactory.createProposal(futureRoundIdentifier, createdBlock).getSignedPayload(), - createdBlock); + createdBlock, + Optional.of(new RoundChangeCertificate(Collections.emptyList()))); - manager.handleNewRoundPayload(newRound); + manager.handleProposalPayload(futureRoundProposal); // Final state sets the Quorum Size to 3, so should send a Prepare and also a commit verify(messageTransmitter, times(1)).multicastPrepare(eq(futureRoundIdentifier), any()); @@ -364,4 +366,32 @@ public class IbftBlockHeightManagerTest { assertThat(preparedCert.get().getPreparedCertificate().getPreparePayloads()) .containsOnly(firstPrepare.getSignedPayload(), secondPrepare.getSignedPayload()); } + + @Test + public void illegalFutureRoundProposalDoesNotTriggerNewRound() { + when(futureRoundProposalMessageValidator.validateProposalMessage(any())).thenReturn(false); + + final ConsensusRoundIdentifier futureRoundIdentifier = createFrom(roundIdentifier, 0, +2); + + final IbftBlockHeightManager manager = + new IbftBlockHeightManager( + headerTestFixture.buildHeader(), + finalState, + roundChangeManager, + roundFactory, + clock, + messageValidatorFactory); + + // Force a new round to be started at new round number. + final Proposal futureRoundProposal = + messageFactory.createProposal( + futureRoundIdentifier, + createdBlock, + Optional.of(new RoundChangeCertificate(Collections.emptyList()))); + manager.start(); + reset(roundFactory); // Discard the existing createNewRound invocation. + + manager.handleProposalPayload(futureRoundProposal); + verify(roundFactory, never()).createNewRound(any(), anyInt()); + } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftControllerTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftControllerTest.java index 2e6a5f1446..470d95da81 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftControllerTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftControllerTest.java @@ -31,12 +31,10 @@ import tech.pegasys.pantheon.consensus.ibft.ibftevent.NewChainHead; import tech.pegasys.pantheon.consensus.ibft.ibftevent.RoundExpiry; import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.IbftV2; -import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.RoundChange; @@ -80,10 +78,6 @@ public class IbftControllerTest { private Message commitMessage; @Mock private CommitMessageData commitMessageData; - @Mock private NewRound newRound; - private Message newRoundMessage; - @Mock private NewRoundMessageData newRoundMessageData; - @Mock private RoundChange roundChange; private Message roundChangeMessage; @Mock private RoundChangeMessageData roundChangeMessageData; @@ -136,11 +130,10 @@ public class IbftControllerTest { setupProposal(roundIdentifierHeight3, validator); setupCommit(futureRoundIdentifier, validator); setupRoundChange(futureRoundIdentifier, validator); - setupNewRound(roundIdentifierHeight3, validator); final List height2Msgs = newArrayList(prepareMessage, commitMessage, roundChangeMessage); - final List height3Msgs = newArrayList(proposalMessage, newRoundMessage); + final List height3Msgs = newArrayList(proposalMessage); futureMessages.put(2L, height2Msgs); futureMessages.put(3L, height3Msgs); when(blockHeightManager.getChainHeight()).thenReturn(2L); @@ -158,7 +151,6 @@ public class IbftControllerTest { verify(ibftGossip).send(commitMessage); verify(blockHeightManager).handleRoundChangePayload(roundChange); verify(ibftGossip).send(roundChangeMessage); - verify(blockHeightManager, never()).handleNewRoundPayload(newRound); } @Test @@ -167,12 +159,9 @@ public class IbftControllerTest { setupProposal(futureRoundIdentifier, validator); setupCommit(futureRoundIdentifier, validator); setupRoundChange(futureRoundIdentifier, validator); - setupNewRound(futureRoundIdentifier, validator); futureMessages.put( - 2L, - ImmutableList.of( - prepareMessage, proposalMessage, commitMessage, roundChangeMessage, newRoundMessage)); + 2L, ImmutableList.of(prepareMessage, proposalMessage, commitMessage, roundChangeMessage)); when(blockHeightManager.getChainHeight()).thenReturn(2L); ibftController.start(); @@ -190,8 +179,6 @@ public class IbftControllerTest { verify(ibftGossip).send(commitMessage); verify(blockHeightManager).handleRoundChangePayload(roundChange); verify(ibftGossip).send(roundChangeMessage); - verify(blockHeightManager).handleNewRoundPayload(newRound); - verify(ibftGossip).send(newRoundMessage); } @Test @@ -275,21 +262,6 @@ public class IbftControllerTest { verifyNoMoreInteractions(blockHeightManager); } - @Test - public void newRoundForCurrentHeightIsPassedToBlockHeightManager() { - roundIdentifier = new ConsensusRoundIdentifier(0, 1); - setupNewRound(roundIdentifier, validator); - ibftController.start(); - ibftController.handleMessageEvent(new IbftReceivedMessageEvent(newRoundMessage)); - - assertThat(futureMessages).isEmpty(); - verify(blockHeightManager).handleNewRoundPayload(newRound); - verify(ibftGossip).send(newRoundMessage); - verify(blockHeightManager, atLeastOnce()).getChainHeight(); - verify(blockHeightManager).start(); - verifyNoMoreInteractions(blockHeightManager); - } - @Test public void roundChangeForCurrentHeightIsPassedToBlockHeightManager() { roundIdentifier = new ConsensusRoundIdentifier(0, 1); @@ -326,13 +298,6 @@ public class IbftControllerTest { verifyNotHandledAndNoFutureMsgs(new IbftReceivedMessageEvent(commitMessage)); } - @Test - public void newRoundForPastHeightIsDiscarded() { - setupNewRound(roundIdentifier, validator); - when(blockHeightManager.getChainHeight()).thenReturn(1L); - verifyNotHandledAndNoFutureMsgs(new IbftReceivedMessageEvent(newRoundMessage)); - } - @Test public void roundChangeForPastHeightIsDiscarded() { setupRoundChange(roundIdentifier, validator); @@ -378,12 +343,6 @@ public class IbftControllerTest { verifyNotHandledAndNoFutureMsgs(new IbftReceivedMessageEvent(commitMessage)); } - @Test - public void newRoundForUnknownValidatorIsDiscarded() { - setupNewRound(roundIdentifier, unknownValidator); - verifyNotHandledAndNoFutureMsgs(new IbftReceivedMessageEvent(newRoundMessage)); - } - @Test public void roundChangeForUnknownValidatorIsDiscarded() { setupRoundChange(roundIdentifier, unknownValidator); @@ -414,14 +373,6 @@ public class IbftControllerTest { verifyHasFutureMessages(new IbftReceivedMessageEvent(commitMessage), expectedFutureMsgs); } - @Test - public void newRoundForFutureHeightIsBuffered() { - setupNewRound(futureRoundIdentifier, validator); - final Map> expectedFutureMsgs = - ImmutableMap.of(2L, ImmutableList.of(newRoundMessage)); - verifyHasFutureMessages(new IbftReceivedMessageEvent(newRoundMessage), expectedFutureMsgs); - } - @Test public void roundChangeForFutureHeightIsBuffered() { setupRoundChange(futureRoundIdentifier, validator); @@ -497,15 +448,6 @@ public class IbftControllerTest { commitMessage = new DefaultMessage(null, commitMessageData); } - private void setupNewRound( - final ConsensusRoundIdentifier roundIdentifier, final Address validator) { - when(newRound.getAuthor()).thenReturn(validator); - when(newRound.getRoundIdentifier()).thenReturn(roundIdentifier); - when(newRoundMessageData.getCode()).thenReturn(IbftV2.NEW_ROUND); - when(newRoundMessageData.decode()).thenReturn(newRound); - newRoundMessage = new DefaultMessage(null, newRoundMessageData); - } - private void setupRoundChange( final ConsensusRoundIdentifier roundIdentifier, final Address validator) { when(roundChange.getAuthor()).thenReturn(validator); diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRoundTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRoundTest.java index b2068a0c50..e2c097fab8 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRoundTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRoundTest.java @@ -129,7 +129,8 @@ public class IbftRoundTest { messageFactory, transmitter); - round.handleProposalMessage(messageFactory.createProposal(roundIdentifier, proposedBlock)); + round.handleProposalMessage( + messageFactory.createProposal(roundIdentifier, proposedBlock, Optional.empty())); verify(transmitter, times(1)).multicastPrepare(roundIdentifier, proposedBlock.getHash()); verify(transmitter, never()).multicastCommit(any(), any(), any()); } @@ -149,7 +150,8 @@ public class IbftRoundTest { transmitter); round.createAndSendProposalMessage(15); - verify(transmitter, times(1)).multicastProposal(roundIdentifier, proposedBlock); + verify(transmitter, times(1)) + .multicastProposal(roundIdentifier, proposedBlock, Optional.empty()); verify(transmitter, never()).multicastPrepare(any(), any()); verify(transmitter, never()).multicastCommit(any(), any(), any()); } @@ -168,7 +170,8 @@ public class IbftRoundTest { messageFactory, transmitter); round.createAndSendProposalMessage(15); - verify(transmitter, times(1)).multicastProposal(roundIdentifier, proposedBlock); + verify(transmitter, times(1)) + .multicastProposal(roundIdentifier, proposedBlock, Optional.empty()); verify(transmitter, never()).multicastPrepare(any(), any()); verify(transmitter, times(1)).multicastCommit(any(), any(), any()); verify(blockImporter, times(1)).importBlock(any(), any(), any()); @@ -194,7 +197,8 @@ public class IbftRoundTest { final Signature localCommitSeal = SECP256K1.sign(commitSealHash, localNodeKeys); // Receive Proposal Message - round.handleProposalMessage(messageFactory.createProposal(roundIdentifier, proposedBlock)); + round.handleProposalMessage( + messageFactory.createProposal(roundIdentifier, proposedBlock, Optional.empty())); verify(transmitter, times(1)).multicastPrepare(roundIdentifier, proposedBlock.getHash()); verify(transmitter, times(1)) .multicastCommit(roundIdentifier, proposedBlock.getHash(), localCommitSeal); @@ -251,7 +255,7 @@ public class IbftRoundTest { } @Test - public void aNewRoundMessageWithAnewBlockIsSentUponReceptionOfARoundChangeWithNoCertificate() { + public void aProposalWithAnewBlockIsSentUponReceptionOfARoundChangeWithNoCertificate() { final RoundState roundState = new RoundState(roundIdentifier, 2, messageValidator); final IbftRound round = new IbftRound( @@ -268,11 +272,11 @@ public class IbftRoundTest { round.startRoundWith(new RoundChangeArtifacts(empty(), emptyList()), 15); verify(transmitter, times(1)) - .multicastNewRound(eq(roundIdentifier), eq(roundChangeCertificate), any(), any()); + .multicastProposal(eq(roundIdentifier), any(), eq(Optional.of(roundChangeCertificate))); } @Test - public void aNewRoundMessageWithTheSameBlockIsSentUponReceptionOfARoundChangeWithCertificate() { + public void aProposalMessageWithTheSameBlockIsSentUponReceptionOfARoundChangeWithCertificate() { final ConsensusRoundIdentifier priorRoundChange = new ConsensusRoundIdentifier(1, 0); final RoundState roundState = new RoundState(roundIdentifier, 2, messageValidator); final IbftRound round = @@ -293,18 +297,18 @@ public class IbftRoundTest { roundIdentifier, Optional.of( new PreparedRoundArtifacts( - messageFactory.createProposal(priorRoundChange, proposedBlock), + messageFactory.createProposal( + priorRoundChange, proposedBlock, Optional.empty()), emptyList()))))); // NOTE: IbftRound assumes the prepare's are valid round.startRoundWith(roundChangeArtifacts, 15); verify(transmitter, times(1)) - .multicastNewRound( + .multicastProposal( eq(roundIdentifier), - eq(roundChangeArtifacts.getRoundChangeCertificate()), - any(), - blockCaptor.capture()); + blockCaptor.capture(), + eq(Optional.of(roundChangeArtifacts.getRoundChangeCertificate()))); final IbftExtraData proposedExtraData = IbftExtraData.decode(blockCaptor.getValue().getHeader().getExtraData()); @@ -337,11 +341,10 @@ public class IbftRoundTest { round.startRoundWith(roundChangeArtifacts, 15); verify(transmitter, times(1)) - .multicastNewRound( + .multicastProposal( eq(roundIdentifier), - eq(roundChangeArtifacts.getRoundChangeCertificate()), - payloadArgCaptor.capture(), - blockCaptor.capture()); + blockCaptor.capture(), + eq(Optional.of(roundChangeArtifacts.getRoundChangeCertificate()))); // Inject a single Prepare message, and confirm the roundState has gone to Prepared (which // indicates the block has entered the roundState (note: all msgs are deemed valid due to mocks) @@ -386,7 +389,8 @@ public class IbftRoundTest { round.handleCommitMessage( messageFactory.createCommit(roundIdentifier, proposedBlock.getHash(), remoteCommitSeal)); - round.handleProposalMessage(messageFactory.createProposal(roundIdentifier, proposedBlock)); + round.handleProposalMessage( + messageFactory.createProposal(roundIdentifier, proposedBlock, Optional.empty())); verify(blockImporter, times(1)).importBlock(any(), any(), any()); } @@ -409,7 +413,8 @@ public class IbftRoundTest { round.handleCommitMessage( messageFactory.createCommit(roundIdentifier, proposedBlock.getHash(), remoteCommitSeal)); - round.handleProposalMessage(messageFactory.createProposal(roundIdentifier, proposedBlock)); + round.handleProposalMessage( + messageFactory.createProposal(roundIdentifier, proposedBlock, Optional.empty())); verify(blockImporter, times(1)).importBlock(any(), any(), any()); } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeArtifactsTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeArtifactsTest.java index 7f853627d2..873e7c3999 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeArtifactsTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeArtifactsTest.java @@ -54,7 +54,7 @@ public class RoundChangeArtifactsTest { final Block block = TestHelpers.createProposalBlock(emptyList(), preparedRound); return new PreparedRoundArtifacts( - messageFactories.get(0).createProposal(preparedRound, block), + messageFactories.get(0).createProposal(preparedRound, block, Optional.empty()), messageFactories.stream() .map(factory -> factory.createPrepare(preparedRound, block.getHash())) .collect(Collectors.toList())); diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeManagerTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeManagerTest.java index 8d359c4633..f27874b266 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeManagerTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundChangeManagerTest.java @@ -134,7 +134,7 @@ public class RoundChangeManagerTest { final ConsensusRoundIdentifier proposalRound = TestHelpers.createFrom(round, 0, -1); final Block block = TestHelpers.createProposalBlock(validators, proposalRound); // Proposal must come from an earlier round. - final Proposal proposal = messageFactory.createProposal(proposalRound, block); + final Proposal proposal = messageFactory.createProposal(proposalRound, block, Optional.empty()); final List preparePayloads = prepareProviders.stream() diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundStateTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundStateTest.java index c0ccab9f13..0dcb580370 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundStateTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundStateTest.java @@ -33,6 +33,7 @@ import tech.pegasys.pantheon.ethereum.core.Util; import java.math.BigInteger; import java.util.List; +import java.util.Optional; import com.google.common.collect.Lists; import org.junit.Before; @@ -80,7 +81,7 @@ public class RoundStateTest { final RoundState roundState = new RoundState(roundIdentifier, 1, messageValidator); final Proposal proposal = - validatorMessageFactories.get(0).createProposal(roundIdentifier, block); + validatorMessageFactories.get(0).createProposal(roundIdentifier, block, Optional.empty()); assertThat(roundState.setProposedBlock(proposal)).isFalse(); assertThat(roundState.isPrepared()).isFalse(); @@ -94,8 +95,7 @@ public class RoundStateTest { final RoundState roundState = new RoundState(roundIdentifier, 1, messageValidator); final Proposal proposal = - validatorMessageFactories.get(0).createProposal(roundIdentifier, block); - + validatorMessageFactories.get(0).createProposal(roundIdentifier, block, Optional.empty()); assertThat(roundState.setProposedBlock(proposal)).isTrue(); assertThat(roundState.isPrepared()).isTrue(); assertThat(roundState.isCommitted()).isFalse(); @@ -117,7 +117,7 @@ public class RoundStateTest { final RoundState roundState = new RoundState(roundIdentifier, 1, messageValidator); final Proposal proposal = - validatorMessageFactories.get(0).createProposal(roundIdentifier, block); + validatorMessageFactories.get(0).createProposal(roundIdentifier, block, Optional.empty()); assertThat(roundState.setProposedBlock(proposal)).isTrue(); assertThat(roundState.isPrepared()).isTrue(); @@ -161,7 +161,7 @@ public class RoundStateTest { assertThat(roundState.constructPreparedRoundArtifacts()).isEmpty(); final Proposal proposal = - validatorMessageFactories.get(0).createProposal(roundIdentifier, block); + validatorMessageFactories.get(0).createProposal(roundIdentifier, block, Optional.empty()); assertThat(roundState.setProposedBlock(proposal)).isTrue(); assertThat(roundState.isPrepared()).isTrue(); assertThat(roundState.isCommitted()).isFalse(); @@ -189,7 +189,7 @@ public class RoundStateTest { verify(messageValidator, never()).validatePrepare(any()); final Proposal proposal = - validatorMessageFactories.get(0).createProposal(roundIdentifier, block); + validatorMessageFactories.get(0).createProposal(roundIdentifier, block, Optional.empty()); assertThat(roundState.setProposedBlock(proposal)).isTrue(); assertThat(roundState.isPrepared()).isFalse(); @@ -208,7 +208,7 @@ public class RoundStateTest { validatorMessageFactories.get(2).createPrepare(roundIdentifier, block.getHash()); final Proposal proposal = - validatorMessageFactories.get(0).createProposal(roundIdentifier, block); + validatorMessageFactories.get(0).createProposal(roundIdentifier, block, Optional.empty()); when(messageValidator.validateProposal(any())).thenReturn(true); when(messageValidator.validatePrepare(firstPrepare)).thenReturn(false); @@ -248,7 +248,7 @@ public class RoundStateTest { Signature.create(BigInteger.TEN, BigInteger.TEN, (byte) 1)); final Proposal proposal = - validatorMessageFactories.get(0).createProposal(roundIdentifier, block); + validatorMessageFactories.get(0).createProposal(roundIdentifier, block, Optional.empty()); roundState.setProposedBlock(proposal); roundState.addCommitMessage(firstCommit); diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/FutureRoundProposalMessageValidatorTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/FutureRoundProposalMessageValidatorTest.java new file mode 100644 index 0000000000..8dc3846b49 --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/FutureRoundProposalMessageValidatorTest.java @@ -0,0 +1,89 @@ +/* + * 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.validation; + +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; +import tech.pegasys.pantheon.consensus.ibft.TestHelpers; +import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; +import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; +import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; +import tech.pegasys.pantheon.ethereum.core.Block; +import tech.pegasys.pantheon.ethereum.core.BlockHeader; + +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class FutureRoundProposalMessageValidatorTest { + + private final KeyPair proposerKey = KeyPair.generate(); + private final MessageFactory messageFactoy = new MessageFactory(proposerKey); + private final ConsensusRoundIdentifier roundIdentifier = new ConsensusRoundIdentifier(1, 1); + private final Block proposedBlock = TestHelpers.createProposalBlock(emptyList(), roundIdentifier); + + private FutureRoundProposalMessageValidator validator; + + private final MessageValidatorFactory messageValidatorFactory = + mock(MessageValidatorFactory.class); + private final MessageValidator messageValidator = mock(MessageValidator.class); + + @Before + public void setup() { + + when(messageValidatorFactory.createMessageValidator(any(), any())).thenReturn(messageValidator); + when(messageValidator.validateProposal(any())).thenReturn(true); + + final BlockHeader parentHeader = mock(BlockHeader.class); + + validator = + new FutureRoundProposalMessageValidator( + messageValidatorFactory, roundIdentifier.getSequenceNumber(), parentHeader); + } + + @Test + public void validProposalMatchingCurrentChainHeightPassesValidation() { + final Proposal proposal = + messageFactoy.createProposal(roundIdentifier, proposedBlock, Optional.empty()); + + assertThat(validator.validateProposalMessage(proposal)).isTrue(); + } + + @Test + public void proposalTargettingDifferentChainHeightFailsValidation() { + final ConsensusRoundIdentifier futureChainIdentifier = + TestHelpers.createFrom(roundIdentifier, 1, 0); + final Proposal proposal = + messageFactoy.createProposal(futureChainIdentifier, proposedBlock, Optional.empty()); + + assertThat(validator.validateProposalMessage(proposal)).isFalse(); + } + + @Test + public void proposalWhichFailsMessageValidationFailsFutureRoundValidation() { + final Proposal proposal = + messageFactoy.createProposal(roundIdentifier, proposedBlock, Optional.empty()); + when(messageValidator.validateProposal(any())).thenReturn(false); + + assertThat(validator.validateProposalMessage(proposal)).isFalse(); + } +} 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 9f34f43c5d..30fe9367a4 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 @@ -12,9 +12,11 @@ */ package tech.pegasys.pantheon.consensus.ibft.validation; +import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -26,6 +28,7 @@ import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Commit; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Prepare; import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; +import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.crypto.SECP256K1; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.BlockValidator; @@ -52,7 +55,7 @@ public class MessageValidatorTest { private KeyPair keyPair = KeyPair.generate(); private MessageFactory messageFactory = new MessageFactory(keyPair); - private ConsensusRoundIdentifier roundIdentifier = new ConsensusRoundIdentifier(1, 1); + private ConsensusRoundIdentifier roundIdentifier = new ConsensusRoundIdentifier(1, 0); private SignedDataValidator signedDataValidator = mock(SignedDataValidator.class); private ProposalBlockConsistencyValidator proposalBlockConsistencyValidator = @@ -60,6 +63,8 @@ public class MessageValidatorTest { @Mock private BlockValidator blockValidator; private ProtocolContext protocolContext; + private final RoundChangeCertificateValidator roundChangeCertificateValidator = + mock(RoundChangeCertificateValidator.class); private MessageValidator messageValidator; @@ -88,17 +93,26 @@ public class MessageValidatorTest { when(blockValidator.validateAndProcessBlock(any(), any(), any(), any())) .thenReturn(Optional.of(new BlockProcessingOutputs(null, null))); + when(roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate( + any(), any())) + .thenReturn(true); + when(roundChangeCertificateValidator.validateRoundChangeMessagesAndEnsureTargetRoundMatchesRoot( + any(), any())) + .thenReturn(true); + messageValidator = new MessageValidator( signedDataValidator, proposalBlockConsistencyValidator, blockValidator, - protocolContext); + protocolContext, + roundChangeCertificateValidator); } @Test public void messageValidatorDefersToUnderlyingSignedDataValidator() { - final Proposal proposal = messageFactory.createProposal(roundIdentifier, block); + final Proposal proposal = + messageFactory.createProposal(roundIdentifier, block, Optional.empty()); final Prepare prepare = messageFactory.createPrepare(roundIdentifier, block.getHash()); @@ -118,7 +132,8 @@ public class MessageValidatorTest { @Test public void ifProposalConsistencyChecksFailProposalIsIllegal() { - final Proposal proposal = messageFactory.createProposal(roundIdentifier, block); + final Proposal proposal = + messageFactory.createProposal(roundIdentifier, block, Optional.empty()); when(proposalBlockConsistencyValidator.validateProposalMatchesBlock(any(), any())) .thenReturn(false); @@ -132,8 +147,62 @@ public class MessageValidatorTest { when(blockValidator.validateAndProcessBlock(any(), any(), any(), any())) .thenReturn(Optional.empty()); - final Proposal proposalMsg = messageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + messageFactory.createProposal(roundIdentifier, block, Optional.empty()); assertThat(messageValidator.validateProposal(proposalMsg)).isFalse(); } + + @Test + public void proposalFailsValidationIfRoundChangeCertificateDoeNotMatchBlock() { + final ConsensusRoundIdentifier nonZeroRound = new ConsensusRoundIdentifier(1, 1); + when(roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate( + any(), any())) + .thenReturn(false); + + final Proposal proposal = + messageFactory.createProposal( + nonZeroRound, block, Optional.of(new RoundChangeCertificate(emptyList()))); + + assertThat(messageValidator.validateProposal(proposal)).isFalse(); + } + + @Test + public void proposalFailsValidationIfRoundChangeIsNotSelfConsistent() { + final ConsensusRoundIdentifier nonZeroRound = new ConsensusRoundIdentifier(1, 1); + when(roundChangeCertificateValidator.validateRoundChangeMessagesAndEnsureTargetRoundMatchesRoot( + any(), any())) + .thenReturn(false); + + final Proposal proposal = + messageFactory.createProposal( + nonZeroRound, block, Optional.of(new RoundChangeCertificate(emptyList()))); + + assertThat(messageValidator.validateProposal(proposal)).isFalse(); + } + + @Test + public void proposalForRoundZeroFailsIfItContainsARoundChangeCertificate() { + final Proposal proposal = + messageFactory.createProposal( + roundIdentifier, block, Optional.of(new RoundChangeCertificate(emptyList()))); + + assertThat(messageValidator.validateProposal(proposal)).isFalse(); + verify(roundChangeCertificateValidator, never()) + .validateRoundChangeMessagesAndEnsureTargetRoundMatchesRoot(any(), any()); + verify(roundChangeCertificateValidator, never()) + .validateProposalMessageMatchesLatestPrepareCertificate(any(), any()); + } + + @Test + public void proposalForRoundsGreaterThanZeroFailIfNoRoundChangeCertificateAvailable() { + final ConsensusRoundIdentifier nonZeroRound = new ConsensusRoundIdentifier(1, 1); + final Proposal proposal = messageFactory.createProposal(nonZeroRound, block, Optional.empty()); + + assertThat(messageValidator.validateProposal(proposal)).isFalse(); + verify(roundChangeCertificateValidator, never()) + .validateRoundChangeMessagesAndEnsureTargetRoundMatchesRoot(any(), any()); + verify(roundChangeCertificateValidator, never()) + .validateProposalMessageMatchesLatestPrepareCertificate(any(), any()); + } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundMessageValidatorTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundMessageValidatorTest.java deleted file mode 100644 index 2ae60932ef..0000000000 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundMessageValidatorTest.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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.validation; - -import static java.util.Collections.emptyList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -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.TestHelpers; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; -import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; -import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; -import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; -import tech.pegasys.pantheon.ethereum.BlockValidator; -import tech.pegasys.pantheon.ethereum.BlockValidator.BlockProcessingOutputs; -import tech.pegasys.pantheon.ethereum.ProtocolContext; -import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain; -import tech.pegasys.pantheon.ethereum.core.Address; -import tech.pegasys.pantheon.ethereum.core.Block; -import tech.pegasys.pantheon.ethereum.core.Util; -import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; - -import java.util.List; -import java.util.Optional; - -import org.assertj.core.util.Lists; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class NewRoundMessageValidatorTest { - - private final NewRoundPayloadValidator payloadValidator = mock(NewRoundPayloadValidator.class); - - private final KeyPair proposerKey = KeyPair.generate(); - private final KeyPair validatorKey = KeyPair.generate(); - private final MessageFactory proposerMessageFactory = new MessageFactory(proposerKey); - private final MessageFactory validatorMessageFactory = new MessageFactory(validatorKey); - private final Address proposerAddress = Util.publicKeyToAddress(proposerKey.getPublicKey()); - private final Address validatorAddress = Util.publicKeyToAddress(validatorKey.getPublicKey()); - private final List
validators = Lists.newArrayList(proposerAddress, validatorAddress); - - private final ConsensusRoundIdentifier roundIdentifier = new ConsensusRoundIdentifier(1, 1); - private final Block proposedBlock = TestHelpers.createProposalBlock(validators, roundIdentifier); - - private ProposalBlockConsistencyValidator proposalBlockConsistencyValidator = - mock(ProposalBlockConsistencyValidator.class); - private final RoundChangeCertificateValidator roundChangeCertificateValidator = - mock(RoundChangeCertificateValidator.class); - - @Mock private BlockValidator blockValidator; - private ProtocolContext protocolContext; - - private NewRoundMessageValidator validator; - - @Before - public void setup() { - - when(blockValidator.validateAndProcessBlock(any(), any(), any(), any())) - .thenReturn(Optional.of(new BlockProcessingOutputs(null, null))); - - when(roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate( - any(), any())) - .thenReturn(true); - - protocolContext = - new ProtocolContext<>( - mock(MutableBlockchain.class), mock(WorldStateArchive.class), mock(IbftContext.class)); - - validator = - new NewRoundMessageValidator( - payloadValidator, - proposalBlockConsistencyValidator, - blockValidator, - protocolContext, - roundChangeCertificateValidator); - - when(proposalBlockConsistencyValidator.validateProposalMatchesBlock(any(), any())) - .thenReturn(true); - - when(payloadValidator.validateNewRoundMessage(any())).thenReturn(true); - } - - @Test - public void underlyingPayloadValidatorIsInvokedWithCorrectParameters() { - final Proposal proposal = proposerMessageFactory.createProposal(roundIdentifier, proposedBlock); - final NewRound message = - proposerMessageFactory.createNewRound( - roundIdentifier, - new RoundChangeCertificate(emptyList()), - proposal.getSignedPayload(), - proposal.getBlock()); - - assertThat(validator.validateNewRoundMessage(message)).isTrue(); - verify(payloadValidator, times(1)).validateNewRoundMessage(message.getSignedPayload()); - } - - @Test - public void failedBlockValidationFailsMessageValidation() { - final Proposal proposal = proposerMessageFactory.createProposal(roundIdentifier, proposedBlock); - final NewRound message = - proposerMessageFactory.createNewRound( - roundIdentifier, - new RoundChangeCertificate(emptyList()), - proposal.getSignedPayload(), - proposal.getBlock()); - - when(blockValidator.validateAndProcessBlock(any(), any(), any(), any())) - .thenReturn(Optional.empty()); - assertThat(validator.validateNewRoundMessage(message)).isFalse(); - } - - @Test - public void ifProposalConsistencyChecksFailsProposalIsIllegal() { - final Proposal proposal = proposerMessageFactory.createProposal(roundIdentifier, proposedBlock); - final NewRound message = - proposerMessageFactory.createNewRound( - roundIdentifier, - new RoundChangeCertificate(emptyList()), - proposal.getSignedPayload(), - proposal.getBlock()); - - when(proposalBlockConsistencyValidator.validateProposalMatchesBlock(any(), any())) - .thenReturn(false); - when(payloadValidator.validateNewRoundMessage(any())).thenReturn(true); - - assertThat(validator.validateNewRoundMessage(message)).isFalse(); - verify(proposalBlockConsistencyValidator, times(1)) - .validateProposalMatchesBlock(proposal.getSignedPayload(), proposal.getBlock()); - } - - @Test - public void validationFailsIfUnderlyingSignedDataValidatorFails() { - when(payloadValidator.validateNewRoundMessage(any())).thenReturn(false); - final Proposal proposal = proposerMessageFactory.createProposal(roundIdentifier, proposedBlock); - final NewRound message = - proposerMessageFactory.createNewRound( - roundIdentifier, - new RoundChangeCertificate(emptyList()), - proposal.getSignedPayload(), - proposal.getBlock()); - - assertThat(validator.validateNewRoundMessage(message)).isFalse(); - } - - @Test - public void roundChangeCertificateDoesntContainSuppliedBlockFails() { - when(roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate( - any(), any())) - .thenReturn(false); - - final Proposal proposal = proposerMessageFactory.createProposal(roundIdentifier, proposedBlock); - final NewRound message = - proposerMessageFactory.createNewRound( - roundIdentifier, - new RoundChangeCertificate(emptyList()), - proposal.getSignedPayload(), - proposal.getBlock()); - - assertThat(validator.validateNewRoundMessage(message)).isFalse(); - } -} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundSignedDataValidatorTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundSignedDataValidatorTest.java deleted file mode 100644 index fe8839c54e..0000000000 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/NewRoundSignedDataValidatorTest.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2018 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.validation; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; -import tech.pegasys.pantheon.consensus.ibft.TestHelpers; -import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.NewRound; -import tech.pegasys.pantheon.consensus.ibft.messagewrappers.Proposal; -import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; -import tech.pegasys.pantheon.consensus.ibft.payload.NewRoundPayload; -import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; -import tech.pegasys.pantheon.consensus.ibft.validation.RoundChangePayloadValidator.MessageValidatorForHeightFactory; -import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; -import tech.pegasys.pantheon.ethereum.core.Address; -import tech.pegasys.pantheon.ethereum.core.Block; -import tech.pegasys.pantheon.ethereum.core.Util; - -import java.util.List; -import java.util.Optional; - -import com.google.common.collect.Lists; -import org.junit.Before; -import org.junit.Test; - -public class NewRoundSignedDataValidatorTest { - - private final KeyPair proposerKey = KeyPair.generate(); - private final KeyPair validatorKey = KeyPair.generate(); - private final KeyPair otherValidatorKey = KeyPair.generate(); - private final MessageFactory proposerMessageFactory = new MessageFactory(proposerKey); - private final MessageFactory validatorMessageFactory = new MessageFactory(validatorKey); - private final Address proposerAddress = Util.publicKeyToAddress(proposerKey.getPublicKey()); - private final List
validators = Lists.newArrayList(); - private final long chainHeight = 2; - private final ConsensusRoundIdentifier roundIdentifier = - new ConsensusRoundIdentifier(chainHeight, 4); - private NewRoundPayloadValidator validator; - - private final ProposerSelector proposerSelector = mock(ProposerSelector.class); - private final MessageValidatorForHeightFactory validatorFactory = - mock(MessageValidatorForHeightFactory.class); - private final SignedDataValidator signedDataValidator = mock(SignedDataValidator.class); - private final RoundChangeCertificateValidator roundChangeCertificateValidator = - mock(RoundChangeCertificateValidator.class); - - private Block proposedBlock; - private NewRound validMsg; - private NewRoundPayload validPayload; - private NewRoundPayload.Builder msgBuilder; - - @Before - public void setup() { - validators.add(Util.publicKeyToAddress(proposerKey.getPublicKey())); - validators.add(Util.publicKeyToAddress(validatorKey.getPublicKey())); - validators.add(Util.publicKeyToAddress(otherValidatorKey.getPublicKey())); - - proposedBlock = TestHelpers.createProposalBlock(validators, roundIdentifier); - validMsg = createValidNewRoundMessageSignedBy(proposerKey); - validPayload = validMsg.getSignedPayload().getPayload(); - msgBuilder = NewRoundPayload.Builder.fromExisting(validMsg); - - when(proposerSelector.selectProposerForRound(any())).thenReturn(proposerAddress); - - when(validatorFactory.createAt(any())).thenReturn(signedDataValidator); - when(signedDataValidator.validateProposal(any())).thenReturn(true); - when(signedDataValidator.validatePrepare(any())).thenReturn(true); - - when(roundChangeCertificateValidator.validateRoundChangeMessagesAndEnsureTargetRoundMatchesRoot( - any(), any())) - .thenReturn(true); - - validator = - new NewRoundPayloadValidator( - proposerSelector, validatorFactory, chainHeight, roundChangeCertificateValidator); - } - - /* NOTE: All test herein assume that the Proposer is the expected transmitter of the NewRound - * message. - */ - - private NewRound createValidNewRoundMessageSignedBy(final KeyPair signingKey) { - final MessageFactory messageCreator = new MessageFactory(signingKey); - - final RoundChangeCertificate.Builder builder = new RoundChangeCertificate.Builder(); - builder.appendRoundChangeMessage( - proposerMessageFactory.createRoundChange(roundIdentifier, Optional.empty())); - - return messageCreator.createNewRound( - roundIdentifier, - builder.buildCertificate(), - messageCreator.createProposal(roundIdentifier, proposedBlock).getSignedPayload(), - proposedBlock); - } - - private NewRound signPayload( - final NewRoundPayload payload, final KeyPair signingKey, final Block block) { - - final MessageFactory messageCreator = new MessageFactory(signingKey); - - return messageCreator.createNewRound( - payload.getRoundIdentifier(), - payload.getRoundChangeCertificate(), - payload.getProposalPayload(), - block); - } - - @Test - public void basicNewRoundMessageIsValid() { - assertThat(validator.validateNewRoundMessage(validMsg.getSignedPayload())).isTrue(); - } - - @Test - public void newRoundFromNonProposerFails() { - final NewRound msg = signPayload(validPayload, validatorKey, proposedBlock); - - assertThat(validator.validateNewRoundMessage(msg.getSignedPayload())).isFalse(); - } - - @Test - public void newRoundTargetingRoundZeroFails() { - msgBuilder.setRoundChangeIdentifier( - new ConsensusRoundIdentifier(roundIdentifier.getSequenceNumber(), 0)); - - final NewRound inValidMsg = signPayload(msgBuilder.build(), proposerKey, proposedBlock); - - assertThat(validator.validateNewRoundMessage(inValidMsg.getSignedPayload())).isFalse(); - } - - @Test - public void newRoundTargetingDifferentSequenceNumberFails() { - final ConsensusRoundIdentifier futureRound = TestHelpers.createFrom(roundIdentifier, 1, 0); - msgBuilder.setRoundChangeIdentifier(futureRound); - - final NewRound inValidMsg = signPayload(msgBuilder.build(), proposerKey, proposedBlock); - - assertThat(validator.validateNewRoundMessage(inValidMsg.getSignedPayload())).isFalse(); - } - - @Test - public void roundChangeCertificateFailsValidation() { - when(roundChangeCertificateValidator.validateRoundChangeMessagesAndEnsureTargetRoundMatchesRoot( - any(), any())) - .thenReturn(false); - assertThat(validator.validateNewRoundMessage(validMsg.getSignedPayload())).isFalse(); - } - - @Test - public void embeddedProposalFailsValidation() { - when(signedDataValidator.validateProposal(any())).thenReturn(false, true); - - final Proposal proposal = proposerMessageFactory.createProposal(roundIdentifier, proposedBlock); - - final NewRound msg = - proposerMessageFactory.createNewRound( - roundIdentifier, - new RoundChangeCertificate( - Lists.newArrayList( - proposerMessageFactory - .createRoundChange(roundIdentifier, Optional.empty()) - .getSignedPayload(), - validatorMessageFactory - .createRoundChange(roundIdentifier, Optional.empty()) - .getSignedPayload())), - proposal.getSignedPayload(), - proposedBlock); - - assertThat(validator.validateNewRoundMessage(msg.getSignedPayload())).isFalse(); - } -} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/ProposalBlockConsistencyValidatorTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/ProposalBlockConsistencyValidatorTest.java index 3ba329fca8..7918a80a18 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/ProposalBlockConsistencyValidatorTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/ProposalBlockConsistencyValidatorTest.java @@ -24,6 +24,7 @@ import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.core.Block; import java.util.Collections; +import java.util.Optional; import org.junit.Before; import org.junit.Test; @@ -51,7 +52,8 @@ public class ProposalBlockConsistencyValidatorTest { @Test public void blockDigestMisMatchWithMessageRoundFails() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); final Block misMatchedBlock = IbftBlockInterface.replaceRoundInBlock( @@ -68,7 +70,8 @@ public class ProposalBlockConsistencyValidatorTest { @Test public void blockDigestMatchesButRoundDiffersFails() { final ConsensusRoundIdentifier futureRound = TestHelpers.createFrom(roundIdentifier, 0, +1); - final Proposal proposalMsg = proposerMessageFactory.createProposal(futureRound, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(futureRound, block, Optional.empty()); assertThat( consistencyChecker.validateProposalMatchesBlock(proposalMsg.getSignedPayload(), block)) @@ -78,7 +81,8 @@ public class ProposalBlockConsistencyValidatorTest { @Test public void blockWithMismatchedNumberFails() { final ConsensusRoundIdentifier futureHeight = TestHelpers.createFrom(roundIdentifier, +1, 0); - final Proposal proposalMsg = proposerMessageFactory.createProposal(futureHeight, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(futureHeight, block, Optional.empty()); assertThat( consistencyChecker.validateProposalMatchesBlock(proposalMsg.getSignedPayload(), block)) diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeCertificateValidatorTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeCertificateValidatorTest.java index a503de8c01..910f91c508 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeCertificateValidatorTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeCertificateValidatorTest.java @@ -69,7 +69,7 @@ public class RoundChangeCertificateValidatorTest { } @Test - public void newRoundWithEmptyRoundChangeCertificateFails() { + public void proposalWithEmptyRoundChangeCertificateFails() { final RoundChangeCertificate cert = new RoundChangeCertificate(Collections.emptyList()); assertThat( @@ -104,7 +104,8 @@ public class RoundChangeCertificateValidatorTest { roundIdentifier, Optional.of( new PreparedRoundArtifacts( - proposerMessageFactory.createProposal(prevRound, proposedBlock), + proposerMessageFactory.createProposal( + prevRound, proposedBlock, Optional.empty()), Lists.newArrayList( validatorMessageFactory.createPrepare( prevRound, proposedBlock.getHash())))))); @@ -129,7 +130,8 @@ public class RoundChangeCertificateValidatorTest { final PreparedRoundArtifacts mismatchedRoundArtefacts = new PreparedRoundArtifacts( - proposerMessageFactory.createProposal(preparedRound, prevProposedBlock), + proposerMessageFactory.createProposal( + preparedRound, prevProposedBlock, Optional.empty()), singletonList( validatorMessageFactory.createPrepare(preparedRound, prevProposedBlock.getHash()))); @@ -152,7 +154,7 @@ public class RoundChangeCertificateValidatorTest { TestHelpers.createFrom(roundIdentifier, 0, -1); final Block latterBlock = TestHelpers.createProposalBlock(validators, latterPrepareRound); final Proposal latterProposal = - proposerMessageFactory.createProposal(latterPrepareRound, latterBlock); + proposerMessageFactory.createProposal(latterPrepareRound, latterBlock, Optional.empty()); final Optional latterTerminatedRoundArtefacts = Optional.of( new PreparedRoundArtifacts( @@ -169,7 +171,7 @@ public class RoundChangeCertificateValidatorTest { final Block earlierBlock = TestHelpers.createProposalBlock(validators.subList(0, 1), earlierPreparedRound); final Proposal earlierProposal = - proposerMessageFactory.createProposal(earlierPreparedRound, earlierBlock); + proposerMessageFactory.createProposal(earlierPreparedRound, earlierBlock, Optional.empty()); final Optional earlierTerminatedRoundArtefacts = Optional.of( new PreparedRoundArtifacts( diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeSignedDataValidatorTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeSignedDataValidatorTest.java index 4109e19957..6c7327e906 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeSignedDataValidatorTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/RoundChangeSignedDataValidatorTest.java @@ -97,7 +97,8 @@ public class RoundChangeSignedDataValidatorTest { public void roundChangeContainingInvalidProposalFails() { final PreparedRoundArtifacts preparedRoundArtifacts = new PreparedRoundArtifacts( - proposerMessageFactory.createProposal(currentRound, block), Collections.emptyList()); + proposerMessageFactory.createProposal(currentRound, block, Optional.empty()), + Collections.emptyList()); final PreparedCertificate prepareCertificate = preparedRoundArtifacts.getPreparedCertificate(); @@ -118,7 +119,8 @@ public class RoundChangeSignedDataValidatorTest { public void roundChangeContainingValidProposalButNoPrepareMessagesFails() { final PreparedRoundArtifacts preparedRoundArtifacts = new PreparedRoundArtifacts( - proposerMessageFactory.createProposal(currentRound, block), Collections.emptyList()); + proposerMessageFactory.createProposal(currentRound, block, Optional.empty()), + Collections.emptyList()); final RoundChange msg = proposerMessageFactory.createRoundChange(targetRound, Optional.of(preparedRoundArtifacts)); @@ -132,7 +134,7 @@ public class RoundChangeSignedDataValidatorTest { final Prepare prepareMsg = validatorMessageFactory.createPrepare(currentRound, block.getHash()); final PreparedRoundArtifacts preparedRoundArtifacts = new PreparedRoundArtifacts( - proposerMessageFactory.createProposal(currentRound, block), + proposerMessageFactory.createProposal(currentRound, block, Optional.empty()), Lists.newArrayList(prepareMsg)); when(basicValidator.validateProposal(any())).thenReturn(true); @@ -168,7 +170,7 @@ public class RoundChangeSignedDataValidatorTest { final Prepare prepareMsg = validatorMessageFactory.createPrepare(futureRound, block.getHash()); final PreparedRoundArtifacts preparedRoundArtifacts = new PreparedRoundArtifacts( - proposerMessageFactory.createProposal(futureRound, block), + proposerMessageFactory.createProposal(futureRound, block, Optional.empty()), Lists.newArrayList(prepareMsg)); final RoundChange msg = @@ -185,7 +187,7 @@ public class RoundChangeSignedDataValidatorTest { final Prepare prepareMsg = validatorMessageFactory.createPrepare(currentRound, block.getHash()); final PreparedRoundArtifacts preparedRoundArtifacts = new PreparedRoundArtifacts( - proposerMessageFactory.createProposal(currentRound, block), + proposerMessageFactory.createProposal(currentRound, block, Optional.empty()), Lists.newArrayList(prepareMsg)); final PreparedCertificate prepareCertificate = preparedRoundArtifacts.getPreparedCertificate(); diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/SignedDataValidatorTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/SignedDataValidatorTest.java index ae1038425e..bbd2e8d0a7 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/SignedDataValidatorTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/validation/SignedDataValidatorTest.java @@ -31,6 +31,7 @@ import tech.pegasys.pantheon.ethereum.core.Hash; import tech.pegasys.pantheon.ethereum.core.Util; import java.util.List; +import java.util.Optional; import com.google.common.collect.Lists; import org.junit.Before; @@ -86,14 +87,16 @@ public class SignedDataValidatorTest { @Test public void receivingProposalMessageFromNonProposerFails() { final Block block = TestHelpers.createProposalBlock(emptyList(), roundIdentifier); - final Proposal proposalMsg = validatorMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + validatorMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); assertThat(validator.validateProposal(proposalMsg.getSignedPayload())).isFalse(); } @Test public void receivingPrepareFromProposerFails() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); final Prepare prepareMsg = proposerMessageFactory.createPrepare(roundIdentifier, block.getHash()); @@ -104,7 +107,8 @@ public class SignedDataValidatorTest { @Test public void receivingPrepareFromNonValidatorFails() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); final Prepare prepareMsg = nonValidatorMessageFactory.createPrepare(roundIdentifier, block.getHash()); @@ -115,7 +119,8 @@ public class SignedDataValidatorTest { @Test public void receivingMessagesWithDifferentRoundIdFromProposalFails() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); final ConsensusRoundIdentifier invalidRoundIdentifier = new ConsensusRoundIdentifier( @@ -133,7 +138,8 @@ public class SignedDataValidatorTest { @Test public void receivingPrepareNonProposerValidatorWithCorrectRoundIsSuccessful() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); final Prepare prepareMsg = validatorMessageFactory.createPrepare(roundIdentifier, block.getHash()); @@ -143,7 +149,8 @@ public class SignedDataValidatorTest { @Test public void receivingACommitMessageWithAnInvalidCommitSealFails() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); final Commit commitMsg = proposerMessageFactory.createCommit( @@ -155,7 +162,8 @@ public class SignedDataValidatorTest { @Test public void commitMessageContainingValidSealFromValidatorIsSuccessful() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); final Commit proposerCommitMsg = proposerMessageFactory.createCommit( @@ -172,32 +180,35 @@ public class SignedDataValidatorTest { @Test public void subsequentProposalHasDifferentSenderFails() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); assertThat(validator.validateProposal(proposalMsg.getSignedPayload())).isTrue(); final Proposal secondProposalMsg = - validatorMessageFactory.createProposal(roundIdentifier, block); + validatorMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); assertThat(validator.validateProposal(secondProposalMsg.getSignedPayload())).isFalse(); } @Test public void subsequentProposalHasDifferentContentFails() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); assertThat(validator.validateProposal(proposalMsg.getSignedPayload())).isTrue(); final ConsensusRoundIdentifier newRoundIdentifier = new ConsensusRoundIdentifier(3, 0); final Proposal secondProposalMsg = - proposerMessageFactory.createProposal(newRoundIdentifier, block); + proposerMessageFactory.createProposal(newRoundIdentifier, block, Optional.empty()); assertThat(validator.validateProposal(secondProposalMsg.getSignedPayload())).isFalse(); } @Test public void subsequentProposalHasIdenticalSenderAndContentIsSuccessful() { - final Proposal proposalMsg = proposerMessageFactory.createProposal(roundIdentifier, block); + final Proposal proposalMsg = + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); assertThat(validator.validateProposal(proposalMsg.getSignedPayload())).isTrue(); final Proposal secondProposalMsg = - proposerMessageFactory.createProposal(roundIdentifier, block); + proposerMessageFactory.createProposal(roundIdentifier, block, Optional.empty()); assertThat(validator.validateProposal(secondProposalMsg.getSignedPayload())).isTrue(); } }